libmultipath: alua_rtpg: use condlog for error messages
[multipath-tools/.git] / libmultipath / prioritizers / alua_rtpg.c
1 /*
2  * (C) Copyright IBM Corp. 2004, 2005   All Rights Reserved.
3  *
4  * rtpg.c
5  *
6  * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access.
7  * It determines the ALUA state of a device and prints a priority value to
8  * stdout.
9  *
10  * Author(s): Jan Kunigk
11  *            S. Bader <shbader@de.ibm.com>
12  *
13  * This file is released under the GPL.
14  */
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <limits.h>
19 #include <sys/ioctl.h>
20 #include <inttypes.h>
21 #include <libudev.h>
22
23 #define __user
24 #include <scsi/sg.h>
25
26 #include "../structs.h"
27 #include "../prio.h"
28 #include "../discovery.h"
29 #include "../unaligned.h"
30 #include "../debug.h"
31 #include "alua_rtpg.h"
32
33 #define SENSE_BUFF_LEN  32
34 #define SGIO_TIMEOUT     60000
35
36 #define PRINT_DEBUG(f, a...) \
37         condlog(4, "alua: " f, ##a)
38
39 /*
40  * Optionally print the commands sent and the data received a hex dump.
41  */
42 #if DEBUG > 0
43 #if DEBUG_DUMPHEX > 0
44 #define PRINT_HEX(p, l) print_hex(p, l)
45 void
46 print_hex(unsigned char *p, unsigned long len)
47 {
48         int     i;
49
50         for(i = 0; i < len; i++) {
51                 if (i % 16 == 0)
52                         printf("%04x: ", i);
53                 printf("%02x%s", p[i], (((i + 1) % 16) == 0) ? "\n" : " ");
54         }
55         printf("\n");
56 }
57 #else
58 #define PRINT_HEX(p, l)
59 #endif
60 #else
61 #define PRINT_HEX(p, l)
62 #endif
63
64 /*
65  * Returns 0 if the SCSI command either was successful or if the an error was
66  * recovered, otherwise 1. (definitions taken from sg_err.h)
67  */
68 #define SCSI_CHECK_CONDITION    0x2
69 #define SCSI_COMMAND_TERMINATED 0x22
70 #define SG_ERR_DRIVER_SENSE     0x08
71 #define RECOVERED_ERROR 0x01
72
73 static int
74 scsi_error(struct sg_io_hdr *hdr)
75 {
76         /* Treat SG_ERR here to get rid of sg_err.[ch] */
77         hdr->status &= 0x7e;
78
79         if (
80                 (hdr->status == 0)        &&
81                 (hdr->host_status == 0)   &&
82                 (hdr->driver_status == 0)
83         ) {
84                 return 0;
85         }
86
87         if (
88                 (hdr->status == SCSI_CHECK_CONDITION)    ||
89                 (hdr->status == SCSI_COMMAND_TERMINATED) ||
90                 ((hdr->driver_status & 0xf) == SG_ERR_DRIVER_SENSE)
91         ) {
92                 if (hdr->sbp && (hdr->sb_len_wr > 2)) {
93                         int             sense_key;
94                         unsigned char * sense_buffer = hdr->sbp;
95
96                         if (sense_buffer[0] & 0x2)
97                                 sense_key = sense_buffer[1] & 0xf;
98                         else
99                                 sense_key = sense_buffer[2] & 0xf;
100
101                         if (sense_key == RECOVERED_ERROR)
102                                 return 0;
103                 }
104         }
105
106         return 1;
107 }
108
109 /*
110  * Helper function to setup and run a SCSI inquiry command.
111  */
112 int
113 do_inquiry(int fd, int evpd, unsigned int codepage,
114            void *resp, int resplen, unsigned int timeout)
115 {
116         struct inquiry_command  cmd;
117         struct sg_io_hdr        hdr;
118         unsigned char           sense[SENSE_BUFF_LEN];
119
120         memset(&cmd, 0, sizeof(cmd));
121         cmd.op = OPERATION_CODE_INQUIRY;
122         if (evpd) {
123                 inquiry_command_set_evpd(&cmd);
124                 cmd.page = codepage;
125         }
126         put_unaligned_be16(resplen, cmd.length);
127         PRINT_HEX((unsigned char *) &cmd, sizeof(cmd));
128
129         memset(&hdr, 0, sizeof(hdr));
130         hdr.interface_id        = 'S';
131         hdr.cmdp                = (unsigned char *) &cmd;
132         hdr.cmd_len             = sizeof(cmd);
133         hdr.dxfer_direction     = SG_DXFER_FROM_DEV;
134         hdr.dxferp              = resp;
135         hdr.dxfer_len           = resplen;
136         hdr.sbp                 = sense;
137         hdr.mx_sb_len           = sizeof(sense);
138         hdr.timeout             = get_prio_timeout(timeout, SGIO_TIMEOUT);
139
140         if (ioctl(fd, SG_IO, &hdr) < 0) {
141                 PRINT_DEBUG("do_inquiry: IOCTL failed!");
142                 return -RTPG_INQUIRY_FAILED;
143         }
144
145         if (scsi_error(&hdr)) {
146                 PRINT_DEBUG("do_inquiry: SCSI error!");
147                 return -RTPG_INQUIRY_FAILED;
148         }
149         PRINT_HEX((unsigned char *) resp, resplen);
150
151         return 0;
152 }
153
154 /*
155  * This function returns the support for target port groups by evaluating the
156  * data returned by the standard inquiry command.
157  */
158 int
159 get_target_port_group_support(int fd, unsigned int timeout)
160 {
161         struct inquiry_data     inq;
162         int                     rc;
163
164         memset((unsigned char *)&inq, 0, sizeof(inq));
165         rc = do_inquiry(fd, 0, 0x00, &inq, sizeof(inq), timeout);
166         if (!rc) {
167                 rc = inquiry_data_get_tpgs(&inq);
168         }
169
170         return rc;
171 }
172
173 static int
174 get_sysfs_pg83(struct path *pp, unsigned char *buff, int buflen)
175 {
176         struct udev_device *parent = pp->udev;
177
178         while (parent) {
179                 const char *subsys = udev_device_get_subsystem(parent);
180                 if (subsys && !strncmp(subsys, "scsi", 4))
181                         break;
182                 parent = udev_device_get_parent(parent);
183         }
184
185         if (!parent || sysfs_get_vpd(parent, 0x83, buff, buflen) <= 0) {
186                 PRINT_DEBUG("failed to read sysfs vpd pg83");
187                 return -1;
188         }
189         return 0;
190 }
191
192 int
193 get_target_port_group(struct path * pp, unsigned int timeout)
194 {
195         unsigned char           *buf;
196         struct vpd83_data *     vpd83;
197         struct vpd83_dscr *     dscr;
198         int                     rc;
199         int                     buflen, scsi_buflen;
200
201         buflen = 4096;
202         buf = (unsigned char *)malloc(buflen);
203         if (!buf) {
204                 PRINT_DEBUG("malloc failed: could not allocate"
205                              "%u bytes", buflen);
206                 return -RTPG_RTPG_FAILED;
207         }
208
209         memset(buf, 0, buflen);
210
211         rc = get_sysfs_pg83(pp, buf, buflen);
212
213         if (rc < 0) {
214                 rc = do_inquiry(pp->fd, 1, 0x83, buf, buflen, timeout);
215                 if (rc < 0)
216                         goto out;
217
218                 scsi_buflen = get_unaligned_be16(&buf[2]) + 4;
219                 /* Paranoia */
220                 if (scsi_buflen >= USHRT_MAX)
221                         scsi_buflen = USHRT_MAX;
222                 if (buflen < scsi_buflen) {
223                         free(buf);
224                         buf = (unsigned char *)malloc(scsi_buflen);
225                         if (!buf) {
226                                 PRINT_DEBUG("malloc failed: could not allocate"
227                                             "%u bytes", scsi_buflen);
228                                 return -RTPG_RTPG_FAILED;
229                         }
230                         buflen = scsi_buflen;
231                         memset(buf, 0, buflen);
232                         rc = do_inquiry(pp->fd, 1, 0x83, buf, buflen, timeout);
233                         if (rc < 0)
234                                 goto out;
235                 }
236         }
237
238         vpd83 = (struct vpd83_data *) buf;
239         rc = -RTPG_NO_TPG_IDENTIFIER;
240         FOR_EACH_VPD83_DSCR(vpd83, dscr) {
241                 if (vpd83_dscr_istype(dscr, IDTYPE_TARGET_PORT_GROUP)) {
242                         struct vpd83_tpg_dscr *p;
243                         if (rc != -RTPG_NO_TPG_IDENTIFIER) {
244                                 PRINT_DEBUG("get_target_port_group: more "
245                                             "than one TPG identifier found!");
246                                 continue;
247                         }
248                         p  = (struct vpd83_tpg_dscr *)dscr->data;
249                         rc = get_unaligned_be16(p->tpg);
250                 }
251         }
252
253         if (rc == -RTPG_NO_TPG_IDENTIFIER) {
254                 PRINT_DEBUG("get_target_port_group: "
255                             "no TPG identifier found!");
256         }
257 out:
258         free(buf);
259         return rc;
260 }
261
262 int
263 do_rtpg(int fd, void* resp, long resplen, unsigned int timeout)
264 {
265         struct rtpg_command     cmd;
266         struct sg_io_hdr        hdr;
267         unsigned char           sense[SENSE_BUFF_LEN];
268
269         memset(&cmd, 0, sizeof(cmd));
270         cmd.op                  = OPERATION_CODE_RTPG;
271         rtpg_command_set_service_action(&cmd);
272         put_unaligned_be32(resplen, cmd.length);
273         PRINT_HEX((unsigned char *) &cmd, sizeof(cmd));
274
275         memset(&hdr, 0, sizeof(hdr));
276         hdr.interface_id        = 'S';
277         hdr.cmdp                = (unsigned char *) &cmd;
278         hdr.cmd_len             = sizeof(cmd);
279         hdr.dxfer_direction     = SG_DXFER_FROM_DEV;
280         hdr.dxferp              = resp;
281         hdr.dxfer_len           = resplen;
282         hdr.mx_sb_len           = sizeof(sense);
283         hdr.sbp                 = sense;
284         hdr.timeout             = get_prio_timeout(timeout, SGIO_TIMEOUT);
285
286         if (ioctl(fd, SG_IO, &hdr) < 0)
287                 return -RTPG_RTPG_FAILED;
288
289         if (scsi_error(&hdr)) {
290                 PRINT_DEBUG("do_rtpg: SCSI error!");
291                 return -RTPG_RTPG_FAILED;
292         }
293         PRINT_HEX(resp, resplen);
294
295         return 0;
296 }
297
298 int
299 get_asymmetric_access_state(int fd, unsigned int tpg, unsigned int timeout)
300 {
301         unsigned char           *buf;
302         struct rtpg_data *      tpgd;
303         struct rtpg_tpg_dscr *  dscr;
304         int                     rc;
305         int                     buflen;
306         uint64_t                scsi_buflen;
307
308         buflen = 4096;
309         buf = (unsigned char *)malloc(buflen);
310         if (!buf) {
311                 PRINT_DEBUG ("malloc failed: could not allocate"
312                         "%u bytes", buflen);
313                 return -RTPG_RTPG_FAILED;
314         }
315         memset(buf, 0, buflen);
316         rc = do_rtpg(fd, buf, buflen, timeout);
317         if (rc < 0)
318                 goto out;
319         scsi_buflen = get_unaligned_be32(&buf[0]) + 4;
320         if (scsi_buflen > UINT_MAX)
321                 scsi_buflen = UINT_MAX;
322         if (buflen < scsi_buflen) {
323                 free(buf);
324                 buf = (unsigned char *)malloc(scsi_buflen);
325                 if (!buf) {
326                         PRINT_DEBUG ("malloc failed: could not allocate"
327                                 "%u bytes", scsi_buflen);
328                         return -RTPG_RTPG_FAILED;
329                 }
330                 buflen = scsi_buflen;
331                 memset(buf, 0, buflen);
332                 rc = do_rtpg(fd, buf, buflen, timeout);
333                 if (rc < 0)
334                         goto out;
335         }
336
337         tpgd = (struct rtpg_data *) buf;
338         rc   = -RTPG_TPG_NOT_FOUND;
339         RTPG_FOR_EACH_PORT_GROUP(tpgd, dscr) {
340                 if (get_unaligned_be16(dscr->tpg) == tpg) {
341                         if (rc != -RTPG_TPG_NOT_FOUND) {
342                                 PRINT_DEBUG("get_asymmetric_access_state: "
343                                         "more than one entry with same port "
344                                         "group.");
345                         } else {
346                                 condlog(5, "pref=%i", dscr->b0);
347                                 rc = rtpg_tpg_dscr_get_aas(dscr);
348                         }
349                 }
350         }
351 out:
352         free(buf);
353         return rc;
354 }