7151cdd8d97134685a2019c1bfb670c654cf3841
[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     300000
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         rc = do_inquiry(fd, 0, 0x00, &inq, sizeof(inq));
164         if (!rc) {
165                 rc = inquiry_data_get_tpgs(&inq);
166         }
167
168         return rc;
169 }
170
171 int
172 get_target_port_group(int fd)
173 {
174         unsigned char           buf[128];
175         struct vpd83_data *     vpd83;
176         struct vpd83_dscr *     dscr;
177         int                     rc;
178
179         rc = do_inquiry(fd, 1, 0x83, buf, sizeof(buf));
180         if (!rc) {
181                 vpd83 = (struct vpd83_data *) buf;
182
183                 rc = -RTPG_NO_TPG_IDENTIFIER;
184                 FOR_EACH_VPD83_DSCR(vpd83, dscr) {
185                         if ((((char *) dscr) - ((char *) vpd83)) > sizeof(buf))
186                                 break;
187
188                         if (vpd83_dscr_istype(dscr, IDTYPE_TARGET_PORT_GROUP)) {
189                                 struct vpd83_tpg_dscr * p;
190
191                                 if (rc != -RTPG_NO_TPG_IDENTIFIER) {
192                                         PRINT_DEBUG("get_target_port_group: "
193                                                 "more than one TPG identifier "
194                                                 "found!\n");
195                                         continue;
196                                 }
197
198                                 p  = (struct vpd83_tpg_dscr *) dscr->data;
199                                 rc = get_uint16(p->tpg);
200                         }
201                 }
202                 if (rc == -RTPG_NO_TPG_IDENTIFIER) {
203                         PRINT_DEBUG("get_target_port_group: "
204                                 "no TPG identifier found!\n");
205                 }
206         }
207
208         return rc;
209 }
210
211 int
212 do_rtpg(int fd, void* resp, long resplen)
213 {
214         struct rtpg_command     cmd;
215         struct sg_io_hdr        hdr;
216         unsigned char           sense[SENSE_BUFF_LEN];
217
218         memset(&cmd, 0, sizeof(cmd));
219         cmd.op                  = OPERATION_CODE_RTPG;
220         rtpg_command_set_service_action(&cmd);
221         set_uint32(cmd.length, resplen);
222         PRINT_HEX((unsigned char *) &cmd, sizeof(cmd));
223
224         memset(&hdr, 0, sizeof(hdr));
225         hdr.interface_id        = 'S';
226         hdr.cmdp                = (unsigned char *) &cmd;
227         hdr.cmd_len             = sizeof(cmd);
228         hdr.dxfer_direction     = SG_DXFER_FROM_DEV;
229         hdr.dxferp              = resp;
230         hdr.dxfer_len           = resplen;
231         hdr.mx_sb_len           = sizeof(sense);
232         hdr.sbp                 = sense;
233         hdr.timeout             = DEF_TIMEOUT;
234  
235         if (ioctl(fd, SG_IO, &hdr) < 0)
236                 return -RTPG_RTPG_FAILED;
237
238         if (scsi_error(&hdr)) {
239                 PRINT_DEBUG("do_rtpg: SCSI error!\n");
240                 return -RTPG_RTPG_FAILED;
241         }
242         PRINT_HEX(resp, resplen);
243
244         return 0;
245
246
247 int
248 get_asymmetric_access_state(int fd, unsigned int tpg)
249 {
250         unsigned char           *buf;
251         struct rtpg_data *      tpgd;
252         struct rtpg_tpg_dscr *  dscr;
253         int                     rc;
254         int                     buflen;
255         uint32_t                scsi_buflen;
256
257         buflen = 128; /* Initial value from old code */
258         buf = (unsigned char *)malloc(buflen);
259         if (!buf) {
260                 PRINT_DEBUG ("malloc failed: could not allocate"
261                         "%u bytes\n", buflen);
262                 return -RTPG_RTPG_FAILED;
263         }
264         rc = do_rtpg(fd, buf, buflen);
265         if (rc < 0)
266                 return rc;
267         scsi_buflen = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
268         if (buflen < (scsi_buflen + 4)) {
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                 rc = do_rtpg(fd, buf, buflen);
278                 if (rc < 0)
279                         goto out;
280         }
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