981ba0616d7ba5d8b7ddb3b7200aed57b0202822
[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;
176         struct vpd83_data *     vpd83;
177         struct vpd83_dscr *     dscr;
178         int                     rc;
179         int                     buflen, scsi_buflen;
180
181         buflen = 128; /* Lets start from 128 */
182         buf = (unsigned char *)malloc(buflen);
183         if (!buf) {
184                 PRINT_DEBUG("malloc failed: could not allocate"
185                              "%u bytes\n", buflen);
186                 return -RTPG_RTPG_FAILED;
187         }
188
189         memset(buf, 0, buflen);
190         rc = do_inquiry(fd, 1, 0x83, buf, buflen);
191         if (rc < 0)
192                 goto out;
193
194         scsi_buflen = (buf[2] << 8 | buf[3]) + 4;
195         if (buflen < scsi_buflen) {
196                 free(buf);
197                 buf = (unsigned char *)malloc(scsi_buflen);
198                 if (!buf) {
199                         PRINT_DEBUG("malloc failed: could not allocate"
200                                      "%u bytes\n", scsi_buflen);
201                         return -RTPG_RTPG_FAILED;
202                 }
203                 buflen = scsi_buflen;
204                 memset(buf, 0, buflen);
205                 rc = do_inquiry(fd, 1, 0x83, buf, buflen);
206                 if (rc < 0)
207                         goto out;
208         }
209
210         vpd83 = (struct vpd83_data *) buf;
211         rc = -RTPG_NO_TPG_IDENTIFIER;
212         FOR_EACH_VPD83_DSCR(vpd83, dscr) {
213                 if (vpd83_dscr_istype(dscr, IDTYPE_TARGET_PORT_GROUP)) {
214                         struct vpd83_tpg_dscr *p;
215                         if (rc != -RTPG_NO_TPG_IDENTIFIER) {
216                                 PRINT_DEBUG("get_target_port_group: more "
217                                             "than one TPG identifier found!\n");
218                                 continue;
219                         }
220                         p  = (struct vpd83_tpg_dscr *)dscr->data;
221                         rc = get_uint16(p->tpg);
222                 }
223         }
224
225         if (rc == -RTPG_NO_TPG_IDENTIFIER) {
226                 PRINT_DEBUG("get_target_port_group: "
227                             "no TPG identifier found!\n");
228         }
229 out:
230         free(buf);
231         return rc;
232 }
233
234 int
235 do_rtpg(int fd, void* resp, long resplen)
236 {
237         struct rtpg_command     cmd;
238         struct sg_io_hdr        hdr;
239         unsigned char           sense[SENSE_BUFF_LEN];
240
241         memset(&cmd, 0, sizeof(cmd));
242         cmd.op                  = OPERATION_CODE_RTPG;
243         rtpg_command_set_service_action(&cmd);
244         set_uint32(cmd.length, resplen);
245         PRINT_HEX((unsigned char *) &cmd, sizeof(cmd));
246
247         memset(&hdr, 0, sizeof(hdr));
248         hdr.interface_id        = 'S';
249         hdr.cmdp                = (unsigned char *) &cmd;
250         hdr.cmd_len             = sizeof(cmd);
251         hdr.dxfer_direction     = SG_DXFER_FROM_DEV;
252         hdr.dxferp              = resp;
253         hdr.dxfer_len           = resplen;
254         hdr.mx_sb_len           = sizeof(sense);
255         hdr.sbp                 = sense;
256         hdr.timeout             = DEF_TIMEOUT;
257
258         if (ioctl(fd, SG_IO, &hdr) < 0)
259                 return -RTPG_RTPG_FAILED;
260
261         if (scsi_error(&hdr)) {
262                 PRINT_DEBUG("do_rtpg: SCSI error!\n");
263                 return -RTPG_RTPG_FAILED;
264         }
265         PRINT_HEX(resp, resplen);
266
267         return 0;
268 }
269
270 int
271 get_asymmetric_access_state(int fd, unsigned int tpg)
272 {
273         unsigned char           *buf;
274         struct rtpg_data *      tpgd;
275         struct rtpg_tpg_dscr *  dscr;
276         int                     rc;
277         int                     buflen;
278         uint32_t                scsi_buflen;
279
280         buflen = 128; /* Initial value from old code */
281         buf = (unsigned char *)malloc(buflen);
282         if (!buf) {
283                 PRINT_DEBUG ("malloc failed: could not allocate"
284                         "%u bytes\n", buflen);
285                 return -RTPG_RTPG_FAILED;
286         }
287         memset(buf, 0, buflen);
288         rc = do_rtpg(fd, buf, buflen);
289         if (rc < 0)
290                 goto out;
291         scsi_buflen = (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]) + 4;
292         if (buflen < scsi_buflen) {
293                 free(buf);
294                 buf = (unsigned char *)malloc(scsi_buflen);
295                 if (!buf) {
296                         PRINT_DEBUG ("malloc failed: could not allocate"
297                                 "%u bytes\n", scsi_buflen);
298                         return -RTPG_RTPG_FAILED;
299                 }
300                 buflen = scsi_buflen;
301                 memset(buf, 0, buflen);
302                 rc = do_rtpg(fd, buf, buflen);
303                 if (rc < 0)
304                         goto out;
305         }
306
307         tpgd = (struct rtpg_data *) buf;
308         rc   = -RTPG_TPG_NOT_FOUND;
309         RTPG_FOR_EACH_PORT_GROUP(tpgd, dscr) {
310                 if (get_uint16(dscr->tpg) == tpg) {
311                         if (rc != -RTPG_TPG_NOT_FOUND) {
312                                 PRINT_DEBUG("get_asymmetric_access_state: "
313                                         "more than one entry with same port "
314                                         "group.\n");
315                         } else {
316                                 PRINT_DEBUG("pref=%i\n", dscr->b0);
317                                 rc = rtpg_tpg_dscr_get_aas(dscr);
318                         }
319                 }
320         }
321 out:
322         free(buf);
323         return rc;
324 }
325