6fc89113beb600bf9879ecfe2045e7675a6c1e36
[multipath-tools/.git] / libmultipath / checkers / emc_clariion.c
1 /*
2  * Copyright (c) 2004, 2005 Lars Marowsky-Bree
3  */
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10 #include <fcntl.h>
11 #include <sys/ioctl.h>
12 #include <errno.h>
13
14 #include "../libmultipath/sg_include.h"
15 #include "libsg.h"
16 #include "checkers.h"
17 #include "debug.h"
18 #include "memory.h"
19
20 #define INQUIRY_CMD     0x12
21 #define INQUIRY_CMDLEN  6
22 #define HEAVY_CHECK_COUNT       10
23 #define SCSI_COMMAND_TERMINATED 0x22
24 #define SCSI_CHECK_CONDITION    0x2
25 #define RECOVERED_ERROR         0x01
26 #define ILLEGAL_REQUEST         0x05
27 #define SG_ERR_DRIVER_SENSE     0x08
28
29 /*
30  * Mechanism to track CLARiiON inactive snapshot LUs.
31  * This is done so that we can fail passive paths
32  * to an inactive snapshot LU even though since a
33  * simple read test would return 02/04/03 instead
34  * of 05/25/01 sensekey/ASC/ASCQ data.
35  */
36 #define IS_INACTIVE_SNAP(c)   (c->mpcontext ?                              \
37                                ((struct emc_clariion_checker_LU_context *) \
38                                         (*c->mpcontext))->inactive_snap    \
39                                             : 0)
40
41 #define SET_INACTIVE_SNAP(c)  if (c->mpcontext)                            \
42                                 ((struct emc_clariion_checker_LU_context *)\
43                                         (*c->mpcontext))->inactive_snap = 1
44
45 #define CLR_INACTIVE_SNAP(c)  if (c->mpcontext)                            \
46                                 ((struct emc_clariion_checker_LU_context *)\
47                                         (*c->mpcontext))->inactive_snap = 0
48
49 enum {
50         MSG_CLARIION_QUERY_FAILED = CHECKER_FIRST_MSGID,
51         MSG_CLARIION_QUERY_ERROR,
52         MSG_CLARIION_PATH_CONFIG,
53         MSG_CLARIION_UNIT_REPORT,
54         MSG_CLARIION_PATH_NOT_AVAIL,
55         MSG_CLARIION_LUN_UNBOUND,
56         MSG_CLARIION_WWN_CHANGED,
57         MSG_CLARIION_READ_ERROR,
58         MSG_CLARIION_PASSIVE_GOOD,
59 };
60
61 #define _IDX(x) (MSG_CLARIION_ ## x - CHECKER_FIRST_MSGID)
62 const char *libcheck_msgtable[] = {
63         [_IDX(QUERY_FAILED)] = ": sending query command failed",
64         [_IDX(QUERY_ERROR)] = ": query command indicates error",
65         [_IDX(PATH_CONFIG)] =
66         ": Path not correctly configured for failover",
67         [_IDX(UNIT_REPORT)] =
68         ": Path unit report page in unknown format",
69         [_IDX(PATH_NOT_AVAIL)] =
70         ": Path not available for normal operations",
71         [_IDX(LUN_UNBOUND)] = ": Logical Unit is unbound or LUNZ",
72         [_IDX(WWN_CHANGED)] = ": Logical Unit WWN has changed",
73         [_IDX(READ_ERROR)] = ": Read error",
74         [_IDX(PASSIVE_GOOD)] = ": Active path is healthy",
75         NULL,
76 };
77
78 struct emc_clariion_checker_path_context {
79         char wwn[16];
80         unsigned wwn_set;
81 };
82
83 struct emc_clariion_checker_LU_context {
84         int inactive_snap;
85 };
86
87 void hexadecimal_to_ascii(char * wwn, char *wwnstr)
88 {
89         int i,j, nbl;
90
91         for (i=0,j=0;i<16;i++) {
92                 wwnstr[j++] = ((nbl = ((wwn[i]&0xf0) >> 4)) <= 9) ?
93                                         '0' + nbl : 'a' + (nbl - 10);
94                 wwnstr[j++] = ((nbl = (wwn[i]&0x0f)) <= 9) ?
95                                         '0' + nbl : 'a' + (nbl - 10);
96         }
97         wwnstr[32]=0;
98 }
99
100 int libcheck_init (struct checker * c)
101 {
102         /*
103          * Allocate and initialize the path specific context.
104          */
105         c->context = MALLOC(sizeof(struct emc_clariion_checker_path_context));
106         if (!c->context)
107                 return 1;
108         ((struct emc_clariion_checker_path_context *)c->context)->wwn_set = 0;
109
110         /*
111          * Allocate and initialize the multi-path global context.
112          */
113         if (c->mpcontext && *c->mpcontext == NULL) {
114                 void * mpctxt = malloc(sizeof(int));
115                 *c->mpcontext = mpctxt;
116                 CLR_INACTIVE_SNAP(c);
117         }
118
119         return 0;
120 }
121
122 void libcheck_free (struct checker * c)
123 {
124         free(c->context);
125 }
126
127 int libcheck_check (struct checker * c)
128 {
129         unsigned char sense_buffer[128] = { 0, };
130         unsigned char sb[SENSE_BUFF_LEN] = { 0, }, *sbb;
131         unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC0, 0,
132                                                 sizeof(sense_buffer), 0};
133         struct sg_io_hdr io_hdr;
134         struct emc_clariion_checker_path_context * ct =
135                 (struct emc_clariion_checker_path_context *)c->context;
136         char wwnstr[33];
137         int ret;
138         int retry_emc = 5;
139
140 retry:
141         memset(&io_hdr, 0, sizeof (struct sg_io_hdr));
142         memset(sense_buffer, 0, 128);
143         memset(sb, 0, SENSE_BUFF_LEN);
144         io_hdr.interface_id = 'S';
145         io_hdr.cmd_len = sizeof (inqCmdBlk);
146         io_hdr.mx_sb_len = sizeof (sb);
147         io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
148         io_hdr.dxfer_len = sizeof (sense_buffer);
149         io_hdr.dxferp = sense_buffer;
150         io_hdr.cmdp = inqCmdBlk;
151         io_hdr.sbp = sb;
152         io_hdr.timeout = c->timeout * 1000;
153         io_hdr.pack_id = 0;
154         if (ioctl(c->fd, SG_IO, &io_hdr) < 0) {
155                 if (errno == ENOTTY) {
156                         c->msgid = CHECKER_MSGID_UNSUPPORTED;
157                         return PATH_WILD;
158                 }
159                 c->msgid = MSG_CLARIION_QUERY_FAILED;
160                 return PATH_DOWN;
161         }
162
163         if (io_hdr.info & SG_INFO_OK_MASK) {
164                 switch (io_hdr.host_status) {
165                 case DID_BUS_BUSY:
166                 case DID_ERROR:
167                 case DID_SOFT_ERROR:
168                 case DID_TRANSPORT_DISRUPTED:
169                         /* Transport error, retry */
170                         if (--retry_emc)
171                                 goto retry;
172                         break;
173                 default:
174                         break;
175                 }
176         }
177
178         if (SCSI_CHECK_CONDITION == io_hdr.status ||
179             SCSI_COMMAND_TERMINATED == io_hdr.status ||
180             SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status)) {
181                 if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) {
182                         unsigned char *sbp = io_hdr.sbp;
183                         int sense_key;
184
185                         if (sbp[0] & 0x2)
186                                 sense_key = sbp[1] & 0xf;
187                         else
188                                 sense_key = sbp[2] & 0xf;
189
190                         if (sense_key == ILLEGAL_REQUEST) {
191                                 c->msgid = CHECKER_MSGID_UNSUPPORTED;
192                                 return PATH_WILD;
193                         } else if (sense_key != RECOVERED_ERROR) {
194                                 condlog(1, "emc_clariion_checker: INQUIRY failed with sense key %02x",
195                                         sense_key);
196                                 c->msgid = MSG_CLARIION_QUERY_ERROR;
197                                 return PATH_DOWN;
198                         }
199                 }
200         }
201
202         if (io_hdr.info & SG_INFO_OK_MASK) {
203                 condlog(1, "emc_clariion_checker: INQUIRY failed without sense, status %02x",
204                         io_hdr.status);
205                 c->msgid = MSG_CLARIION_QUERY_ERROR;
206                 return PATH_DOWN;
207         }
208
209         if (/* Verify the code page - right page & revision */
210             sense_buffer[1] != 0xc0 || sense_buffer[9] != 0x00) {
211                 c->msgid = MSG_CLARIION_UNIT_REPORT;
212                 return PATH_DOWN;
213         }
214
215         if ( /* Effective initiator type */
216                 sense_buffer[27] != 0x03
217                 /*
218                  * Failover mode should be set to 1 (PNR failover mode)
219                  * or 4 (ALUA failover mode).
220                  */
221                 || (((sense_buffer[28] & 0x07) != 0x04) &&
222                     ((sense_buffer[28] & 0x07) != 0x06))
223                 /* Arraycommpath should be set to 1 */
224                 || (sense_buffer[30] & 0x04) != 0x04) {
225                 c->msgid = MSG_CLARIION_PATH_CONFIG;
226                 return PATH_DOWN;
227         }
228
229         if ( /* LUN operations should indicate normal operations */
230                 sense_buffer[48] != 0x00) {
231                 c->msgid = MSG_CLARIION_PATH_NOT_AVAIL;
232                 return PATH_SHAKY;
233         }
234
235         if ( /* LUN should at least be bound somewhere and not be LUNZ */
236                 sense_buffer[4] == 0x00) {
237                 c->msgid = MSG_CLARIION_LUN_UNBOUND;
238                 return PATH_DOWN;
239         }
240
241         /*
242          * store the LUN WWN there and compare that it indeed did not
243          * change in between, to protect against the path suddenly
244          * pointing somewhere else.
245          */
246         if (ct->wwn_set) {
247                 if (memcmp(ct->wwn, &sense_buffer[10], 16) != 0) {
248                         c->msgid = MSG_CLARIION_WWN_CHANGED;
249                         return PATH_DOWN;
250                 }
251         } else {
252                 memcpy(ct->wwn, &sense_buffer[10], 16);
253                 ct->wwn_set = 1;
254         }
255
256         /*
257          * Issue read on active path to determine if inactive snapshot.
258          */
259         if (sense_buffer[4] == 2) {/* if active path */
260                 unsigned char buf[4096];
261
262                 memset(buf, 0, 4096);
263                 ret = sg_read(c->fd, &buf[0], 4096,
264                               sbb = &sb[0], SENSE_BUFF_LEN, c->timeout);
265                 if (ret == PATH_DOWN) {
266                         hexadecimal_to_ascii(ct->wwn, wwnstr);
267
268                         /*
269                          * Check for inactive snapshot LU this way.  Must
270                          * fail these.
271                          */
272                         if (((sbb[2]&0xf) == 5) && (sbb[12] == 0x25) &&
273                             (sbb[13]==1)) {
274                                 /*
275                                  * Do this so that we can fail even the
276                                  * passive paths which will return
277                                  * 02/04/03 not 05/25/01 on read.
278                                  */
279                                 SET_INACTIVE_SNAP(c);
280                                 condlog(3, "emc_clariion_checker: Active "
281                                         "path to inactive snapshot WWN %s.",
282                                         wwnstr);
283                         } else {
284                                 condlog(3, "emc_clariion_checker: Read "
285                                         "error for WWN %s.  Sense data are "
286                                         "0x%x/0x%x/0x%x.", wwnstr,
287                                         sbb[2]&0xf, sbb[12], sbb[13]);
288                                 c->msgid = MSG_CLARIION_READ_ERROR;
289                         }
290                 } else {
291                         c->msgid = MSG_CLARIION_PASSIVE_GOOD;
292                         /*
293                          * Remove the path from the set of paths to inactive
294                          * snapshot LUs if it was in this list since the
295                          * snapshot is no longer inactive.
296                          */
297                         CLR_INACTIVE_SNAP(c);
298                 }
299         } else {
300                 if (IS_INACTIVE_SNAP(c)) {
301                         hexadecimal_to_ascii(ct->wwn, wwnstr);
302                         condlog(3, "emc_clariion_checker: Passive "
303                                 "path to inactive snapshot WWN %s.",
304                                 wwnstr);
305                         ret = PATH_DOWN;
306                 } else {
307                         c->msgid = MSG_CLARIION_PASSIVE_GOOD;
308                         ret = PATH_UP;  /* not ghost */
309                 }
310         }
311
312         return ret;
313 }