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