747dfb53d514fe1a74fd43019a4a09d57558aca2
[multipath-tools/.git] / libmultipath / prioritizers / ontap.c
1 /*
2  * Copyright 2005 Network Appliance, Inc., All Rights Reserved
3  * Author:  David Wysochanski available at davidw@netapp.com
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License v2 for more details.
13  */
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <sys/ioctl.h>
19 #include <errno.h>
20 #include <assert.h>
21
22 #include "sg_include.h"
23 #include "debug.h"
24 #include "prio.h"
25 #include "structs.h"
26
27 #define INQUIRY_CMD     0x12
28 #define INQUIRY_CMDLEN  6
29 #define DEFAULT_PRIOVAL 10
30 #define RESULTS_MAX     256
31 #define SG_TIMEOUT      60000
32
33 #define pp_ontap_log(prio, fmt, args...) \
34         condlog(prio, "%s: ontap prio: " fmt, dev, ##args)
35
36 static void dump_cdb(unsigned char *cdb, int size)
37 {
38         int i;
39         char buf[10*5+1];
40         char * p = &buf[0];
41
42         condlog(0, "- SCSI CDB: ");
43         for (i=0; i<size; i++) {
44                 p += snprintf(p, 10*(size-i), "0x%02x ", cdb[i]);
45         }
46         condlog(0, "%s", buf);
47 }
48
49 static void process_sg_error(struct sg_io_hdr *io_hdr)
50 {
51         int i;
52         char buf[128*5+1];
53         char * p = &buf[0];
54
55         condlog(0, "- masked_status=0x%02x, host_status=0x%02x, "
56                 "driver_status=0x%02x", io_hdr->masked_status,
57                 io_hdr->host_status, io_hdr->driver_status);
58         if (io_hdr->sb_len_wr > 0) {
59                 condlog(0, "- SCSI sense data: ");
60                 for (i=0; i<io_hdr->sb_len_wr; i++) {
61                         p += snprintf(p, 128*(io_hdr->sb_len_wr-i), "0x%02x ",
62                                       io_hdr->sbp[i]);
63                 }
64                 condlog(0, "%s", buf);
65         }
66 }
67
68 /*
69  * Returns:
70  * -1: error, errno set
71  *  0: success
72  */
73 static int send_gva(const char *dev, int fd, unsigned char pg,
74                     unsigned char *results, int *results_size,
75                     unsigned int timeout)
76 {
77         unsigned char sb[128];
78         unsigned char cdb[10] = {0xc0, 0, 0x1, 0xa, 0x98, 0xa,
79                                  pg, sizeof(sb), 0, 0};
80         struct sg_io_hdr io_hdr;
81         int ret = -1;
82
83         memset(&io_hdr, 0, sizeof (struct sg_io_hdr));
84         memset(results, 0, *results_size);
85         io_hdr.interface_id = 'S';
86         io_hdr.cmd_len = sizeof (cdb);
87         io_hdr.mx_sb_len = sizeof (sb);
88         io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
89         io_hdr.dxfer_len = *results_size;
90         io_hdr.dxferp = results;
91         io_hdr.cmdp = cdb;
92         io_hdr.sbp = sb;
93         io_hdr.timeout = get_prio_timeout(timeout, SG_TIMEOUT);
94         io_hdr.pack_id = 0;
95         if (ioctl(fd, SG_IO, &io_hdr) < 0) {
96                 pp_ontap_log(0, "SG_IO ioctl failed, errno=%d", errno);
97                 dump_cdb(cdb, sizeof(cdb));
98                 goto out;
99         }
100         if (io_hdr.info & SG_INFO_OK_MASK) {
101                 pp_ontap_log(0, "SCSI error");
102                 dump_cdb(cdb, sizeof(cdb));
103                 process_sg_error(&io_hdr);
104                 goto out;
105         }
106
107         if (results[4] != 0x0a || results[5] != 0x98 ||
108             results[6] != 0x0a ||results[7] != 0x01) {
109                 dump_cdb(cdb, sizeof(cdb));
110                 pp_ontap_log(0, "GVA return wrong format ");
111                 pp_ontap_log(0, "results[4-7] = 0x%02x 0x%02x 0x%02x 0x%02x",
112                         results[4], results[5], results[6], results[7]);
113                 goto out;
114         }
115         ret = 0;
116  out:
117         return(ret);
118 }
119
120 /*
121  * Retuns:
122  * -1: Unable to obtain proxy info
123  *  0: Device _not_ proxy path
124  *  1: Device _is_ proxy path
125  */
126 static int get_proxy(const char *dev, int fd, unsigned int timeout)
127 {
128         unsigned char results[256];
129         unsigned char sb[128];
130         unsigned char cdb[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xc1, 0,
131                                                    sizeof(sb), 0};
132         struct sg_io_hdr io_hdr;
133         int ret = -1;
134
135         memset(&results, 0, sizeof (results));
136         memset(&io_hdr, 0, sizeof (struct sg_io_hdr));
137         io_hdr.interface_id = 'S';
138         io_hdr.cmd_len = sizeof (cdb);
139         io_hdr.mx_sb_len = sizeof (sb);
140         io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
141         io_hdr.dxfer_len = sizeof (results);
142         io_hdr.dxferp = results;
143         io_hdr.cmdp = cdb;
144         io_hdr.sbp = sb;
145         io_hdr.timeout = get_prio_timeout(timeout, SG_TIMEOUT);
146         io_hdr.pack_id = 0;
147         if (ioctl(fd, SG_IO, &io_hdr) < 0) {
148                 pp_ontap_log(0, "ioctl sending inquiry command failed, "
149                         "errno=%d", errno);
150                 dump_cdb(cdb, sizeof(cdb));
151                 goto out;
152         }
153         if (io_hdr.info & SG_INFO_OK_MASK) {
154                 pp_ontap_log(0, "SCSI error");
155                 dump_cdb(cdb, sizeof(cdb));
156                 process_sg_error(&io_hdr);
157                 goto out;
158         }
159
160         if (results[1] != 0xc1 || results[8] != 0x0a ||
161             results[9] != 0x98 || results[10] != 0x0a ||
162             results[11] != 0x0 || results[12] != 0xc1 ||
163             results[13] != 0x0) {
164                 pp_ontap_log(0,"proxy info page in unknown format - ");
165                 pp_ontap_log(0,"results[8-13]=0x%02x 0x%02x 0x%02x 0x%02x "
166                         "0x%02x 0x%02x",
167                         results[8], results[9], results[10],
168                         results[11], results[12], results[13]);
169                 dump_cdb(cdb, sizeof(cdb));
170                 goto out;
171         }
172         ret = (results[19] & 0x02) >> 1;
173
174  out:
175         return(ret);
176 }
177
178 /*
179  * Returns priority of device based on device info.
180  *
181  * 4: FCP non-proxy, FCP proxy unknown, or unable to determine protocol
182  * 3: iSCSI HBA
183  * 2: iSCSI software
184  * 1: FCP proxy
185  */
186 static int ontap_prio(const char *dev, int fd, unsigned int timeout)
187 {
188         unsigned char results[RESULTS_MAX];
189         int results_size=RESULTS_MAX;
190         int rc;
191         int is_proxy;
192         int is_iscsi_software;
193         int is_iscsi_hardware;
194         int tot_len;
195
196         is_iscsi_software = is_iscsi_hardware = is_proxy = 0;
197
198         memset(&results, 0, sizeof (results));
199         rc = send_gva(dev, fd, 0x41, results, &results_size, timeout);
200         if (rc >= 0) {
201                 tot_len = results[0] << 24 | results[1] << 16 |
202                           results[2] << 8 | results[3];
203                 if (tot_len <= 8) {
204                         goto try_fcp_proxy;
205                 }
206                 if (results[8] != 0x41) {
207                         pp_ontap_log(0, "GVA page 0x41 error - "
208                                 "results[8] = 0x%x", results[8]);
209                         goto try_fcp_proxy;
210                 }
211                 if ((strncmp((char *)&results[12], "ism_sw", 6) == 0) ||
212                     (strncmp((char *)&results[12], "iswt", 4) == 0)) {
213                         is_iscsi_software = 1;
214                         goto prio_select;
215                 }
216                 else if (strncmp((char *)&results[12], "ism_sn", 6) == 0) {
217                         is_iscsi_hardware = 1;
218                         goto prio_select;
219                 }
220         } else {
221                 return 0;
222         }
223
224  try_fcp_proxy:
225         rc = get_proxy(dev, fd, timeout);
226         if (rc >= 0) {
227                 is_proxy = rc;
228         }
229
230  prio_select:
231         if (is_iscsi_hardware) {
232                 return 3;
233         } else if (is_iscsi_software) {
234                 return 2;
235         } else {
236                 if (is_proxy) {
237                         return 1;
238                 } else {
239                         /* Either non-proxy, or couldn't get proxy info */
240                         return 4;
241                 }
242         }
243 }
244
245 int getprio (struct path * pp, char * args, unsigned int timeout)
246 {
247         return ontap_prio(pp->dev, pp->fd, timeout);
248 }