multipath-tools: fix the bug while processing vpd 0x83 designation descriptors
[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 <sys/ioctl.h>
19 #include <inttypes.h>
20
21 #define __user
22 #include <scsi/sg.h>
23
24 #include "alua_rtpg.h"
25
26 #define SENSE_BUFF_LEN  32
27 #define DEF_TIMEOUT     60000
28
29 /*
30  * Macro used to print debug messaged.
31  */
32 #if DEBUG > 0
33 #define PRINT_DEBUG(f, a...) \
34                 fprintf(stderr, "DEBUG: " f, ##a)
35 #else
36 #define PRINT_DEBUG(f, a...)
37 #endif
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, void *resp, int resplen)
114 {
115         struct inquiry_command  cmd;
116         struct sg_io_hdr        hdr;
117         unsigned char           sense[SENSE_BUFF_LEN];
118
119         memset(&cmd, 0, sizeof(cmd));
120         cmd.op = OPERATION_CODE_INQUIRY;
121         if (evpd) {
122                 inquiry_command_set_evpd(&cmd);
123                 cmd.page = codepage;
124         }
125         set_uint16(cmd.length, resplen);
126         PRINT_HEX((unsigned char *) &cmd, sizeof(cmd));
127
128         memset(&hdr, 0, sizeof(hdr));
129         hdr.interface_id        = 'S';
130         hdr.cmdp                = (unsigned char *) &cmd;
131         hdr.cmd_len             = sizeof(cmd);
132         hdr.dxfer_direction     = SG_DXFER_FROM_DEV;
133         hdr.dxferp              = resp;
134         hdr.dxfer_len           = resplen;
135         hdr.sbp                 = sense;
136         hdr.mx_sb_len           = sizeof(sense);
137         hdr.timeout             = DEF_TIMEOUT;
138
139         if (ioctl(fd, SG_IO, &hdr) < 0) {
140                 PRINT_DEBUG("do_inquiry: IOCTL failed!\n");
141                 return -RTPG_INQUIRY_FAILED;
142         }
143
144         if (scsi_error(&hdr)) {
145                 PRINT_DEBUG("do_inquiry: SCSI error!\n");
146                 return -RTPG_INQUIRY_FAILED;
147         }
148         PRINT_HEX((unsigned char *) resp, resplen);
149
150         return 0;
151 }
152
153 /*
154  * This function returns the support for target port groups by evaluating the
155  * data returned by the standard inquiry command.
156  */
157 int
158 get_target_port_group_support(int fd)
159 {
160         struct inquiry_data     inq;
161         int                     rc;
162
163         memset((unsigned char *)&inq, 0, sizeof(inq));
164         rc = do_inquiry(fd, 0, 0x00, &inq, sizeof(inq));
165         if (!rc) {
166                 rc = inquiry_data_get_tpgs(&inq);
167         }
168
169         return rc;
170 }
171
172 int
173 get_target_port_group(int fd)
174 {
175         unsigned char           buf[128];
176         struct vpd83_data *     vpd83;
177         struct vpd83_dscr *     dscr;
178         int                     rc;
179
180         memset(buf, 0, sizeof(buf));
181         rc = do_inquiry(fd, 1, 0x83, buf, sizeof(buf));
182         if (!rc) {
183                 vpd83 = (struct vpd83_data *) buf;
184
185                 rc = -RTPG_NO_TPG_IDENTIFIER;
186                 FOR_EACH_VPD83_DSCR(vpd83, dscr) {
187                         if (vpd83_dscr_istype(dscr, IDTYPE_TARGET_PORT_GROUP)) {
188                                 struct vpd83_tpg_dscr * p;
189
190                                 if (rc != -RTPG_NO_TPG_IDENTIFIER) {
191                                         PRINT_DEBUG("get_target_port_group: "
192                                                 "more than one TPG identifier "
193                                                 "found!\n");
194                                         continue;
195                                 }
196
197                                 p  = (struct vpd83_tpg_dscr *) dscr->data;
198                                 rc = get_uint16(p->tpg);
199                         }
200                 }
201                 if (rc == -RTPG_NO_TPG_IDENTIFIER) {
202                         PRINT_DEBUG("get_target_port_group: "
203                                 "no TPG identifier found!\n");
204                 }
205         }
206
207         return rc;
208 }
209
210 int
211 do_rtpg(int fd, void* resp, long resplen)
212 {
213         struct rtpg_command     cmd;
214         struct sg_io_hdr        hdr;
215         unsigned char           sense[SENSE_BUFF_LEN];
216
217         memset(&cmd, 0, sizeof(cmd));
218         cmd.op                  = OPERATION_CODE_RTPG;
219         rtpg_command_set_service_action(&cmd);
220         set_uint32(cmd.length, resplen);
221         PRINT_HEX((unsigned char *) &cmd, sizeof(cmd));
222
223         memset(&hdr, 0, sizeof(hdr));
224         hdr.interface_id        = 'S';
225         hdr.cmdp                = (unsigned char *) &cmd;
226         hdr.cmd_len             = sizeof(cmd);
227         hdr.dxfer_direction     = SG_DXFER_FROM_DEV;
228         hdr.dxferp              = resp;
229         hdr.dxfer_len           = resplen;
230         hdr.mx_sb_len           = sizeof(sense);
231         hdr.sbp                 = sense;
232         hdr.timeout             = DEF_TIMEOUT;
233
234         if (ioctl(fd, SG_IO, &hdr) < 0)
235                 return -RTPG_RTPG_FAILED;
236
237         if (scsi_error(&hdr)) {
238                 PRINT_DEBUG("do_rtpg: SCSI error!\n");
239                 return -RTPG_RTPG_FAILED;
240         }
241         PRINT_HEX(resp, resplen);
242
243         return 0;
244 }
245
246 int
247 get_asymmetric_access_state(int fd, unsigned int tpg)
248 {
249         unsigned char           *buf;
250         struct rtpg_data *      tpgd;
251         struct rtpg_tpg_dscr *  dscr;
252         int                     rc;
253         int                     buflen;
254         uint32_t                scsi_buflen;
255
256         buflen = 128; /* Initial value from old code */
257         buf = (unsigned char *)malloc(buflen);
258         if (!buf) {
259                 PRINT_DEBUG ("malloc failed: could not allocate"
260                         "%u bytes\n", buflen);
261                 return -RTPG_RTPG_FAILED;
262         }
263         memset(buf, 0, buflen);
264         rc = do_rtpg(fd, buf, buflen);
265         if (rc < 0)
266                 goto out;
267         scsi_buflen = (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]) + 4;
268         if (buflen < scsi_buflen) {
269                 free(buf);
270                 buf = (unsigned char *)malloc(scsi_buflen);
271                 if (!buf) {
272                         PRINT_DEBUG ("malloc failed: could not allocate"
273                                 "%u bytes\n", scsi_buflen);
274                         return -RTPG_RTPG_FAILED;
275                 }
276                 buflen = scsi_buflen;
277                 memset(buf, 0, buflen);
278                 rc = do_rtpg(fd, buf, buflen);
279                 if (rc < 0)
280                         goto out;
281         }
282
283         tpgd = (struct rtpg_data *) buf;
284         rc   = -RTPG_TPG_NOT_FOUND;
285         RTPG_FOR_EACH_PORT_GROUP(tpgd, dscr) {
286                 if (get_uint16(dscr->tpg) == tpg) {
287                         if (rc != -RTPG_TPG_NOT_FOUND) {
288                                 PRINT_DEBUG("get_asymmetric_access_state: "
289                                         "more than one entry with same port "
290                                         "group.\n");
291                         } else {
292                                 PRINT_DEBUG("pref=%i\n", dscr->b0);
293                                 rc = rtpg_tpg_dscr_get_aas(dscr);
294                         }
295                 }
296         }
297 out:
298         free(buf);
299         return rc;
300 }
301