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