Specify checker_timeout in seconds
[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 #include <memory.h>
14
15 #include "../libmultipath/sg_include.h"
16 #include "libsg.h"
17 #include "checkers.h"
18 #include "debug.h"
19
20 #define INQUIRY_CMD     0x12
21 #define INQUIRY_CMDLEN  6
22 #define HEAVY_CHECK_COUNT       10
23
24 /*
25  * Mechanism to track CLARiiON inactive snapshot LUs.
26  * This is done so that we can fail passive paths
27  * to an inactive snapshot LU even though since a
28  * simple read test would return 02/04/03 instead
29  * of 05/25/01 sensekey/ASC/ASCQ data.
30  */
31 #define IS_INACTIVE_SNAP(c)   (c->mpcontext ?                              \
32                                ((struct emc_clariion_checker_LU_context *) \
33                                         (*c->mpcontext))->inactive_snap    \
34                                             : 0)
35
36 #define SET_INACTIVE_SNAP(c)  if (c->mpcontext)                            \
37                                 ((struct emc_clariion_checker_LU_context *)\
38                                         (*c->mpcontext))->inactive_snap = 1
39
40 #define CLR_INACTIVE_SNAP(c)  if (c->mpcontext)                            \
41                                 ((struct emc_clariion_checker_LU_context *)\
42                                         (*c->mpcontext))->inactive_snap = 0
43
44 struct emc_clariion_checker_path_context {
45         char wwn[16];
46         unsigned wwn_set;
47 };
48
49 struct emc_clariion_checker_LU_context {
50         int inactive_snap;
51 };
52
53 extern void
54 hexadecimal_to_ascii(char * wwn, char *wwnstr)
55 {
56         int i,j, nbl;
57
58         for (i=0,j=0;i<16;i++) {
59                 wwnstr[j++] = ((nbl = ((wwn[i]&0xf0) >> 4)) <= 9) ?
60                                         '0' + nbl : 'a' + (nbl - 10);
61                 wwnstr[j++] = ((nbl = (wwn[i]&0x0f)) <= 9) ?
62                                         '0' + nbl : 'a' + (nbl - 10);
63         }
64         wwnstr[32]=0;
65 }
66
67 int libcheck_init (struct checker * c)
68 {
69         /*
70          * Allocate and initialize the path specific context.
71          */
72         c->context = MALLOC(sizeof(struct emc_clariion_checker_path_context));
73         if (!c->context)
74                 return 1;
75         ((struct emc_clariion_checker_path_context *)c->context)->wwn_set = 0;
76
77         /*
78          * Allocate and initialize the multi-path global context.
79          */
80         if (c->mpcontext && *c->mpcontext == NULL) {
81                 void * mpctxt = malloc(sizeof(int));
82                 *c->mpcontext = mpctxt;
83                 CLR_INACTIVE_SNAP(c);
84         }
85
86         return 0;
87 }
88
89 void libcheck_free (struct checker * c)
90 {
91         free(c->context);
92 }
93
94 int libcheck_check (struct checker * c)
95 {
96         unsigned char sense_buffer[128] = { 0, };
97         unsigned char sb[SENSE_BUFF_LEN] = { 0, }, *sbb;
98         unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC0, 0,
99                                                 sizeof(sense_buffer), 0};
100         struct sg_io_hdr io_hdr;
101         struct emc_clariion_checker_path_context * ct =
102                 (struct emc_clariion_checker_path_context *)c->context;
103         char wwnstr[33];
104         int ret;
105
106         memset(&io_hdr, 0, sizeof (struct sg_io_hdr));
107         memset(sense_buffer, 0, 128);
108         memset(sb, 0, SENSE_BUFF_LEN);
109         io_hdr.interface_id = 'S';
110         io_hdr.cmd_len = sizeof (inqCmdBlk);
111         io_hdr.mx_sb_len = sizeof (sb);
112         io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
113         io_hdr.dxfer_len = sizeof (sense_buffer);
114         io_hdr.dxferp = sense_buffer;
115         io_hdr.cmdp = inqCmdBlk;
116         io_hdr.sbp = sb;
117         io_hdr.timeout = c->timeout * 1000;
118         io_hdr.pack_id = 0;
119         if (ioctl(c->fd, SG_IO, &io_hdr) < 0) {
120                 MSG(c, "emc_clariion_checker: sending query command failed");
121                 return PATH_DOWN;
122         }
123         if (io_hdr.info & SG_INFO_OK_MASK) {
124                 MSG(c, "emc_clariion_checker: query command indicates error");
125                 return PATH_DOWN;
126         }
127         if (/* Verify the code page - right page & revision */
128             sense_buffer[1] != 0xc0 || sense_buffer[9] != 0x00) {
129                 MSG(c, "emc_clariion_checker: Path unit report page in "
130                     "unknown format");
131                 return PATH_DOWN;
132         }
133
134         if ( /* Effective initiator type */
135                 sense_buffer[27] != 0x03
136                 /*
137                  * Failover mode should be set to 1 (PNR failover mode)
138                  * or 4 (ALUA failover mode).
139                  */
140                 || (((sense_buffer[28] & 0x07) != 0x04) &&
141                     ((sense_buffer[28] & 0x07) != 0x06))
142                 /* Arraycommpath should be set to 1 */
143                 || (sense_buffer[30] & 0x04) != 0x04) {
144                 MSG(c, "emc_clariion_checker: Path not correctly configured "
145                     "for failover");
146                 return PATH_DOWN;
147         }
148
149         if ( /* LUN operations should indicate normal operations */
150                 sense_buffer[48] != 0x00) {
151                 MSG(c, "emc_clariion_checker: Path not available for normal "
152                     "operations");
153                 return PATH_SHAKY;
154         }
155
156         if ( /* LUN should at least be bound somewhere and not be LUNZ */
157                 sense_buffer[4] == 0x00) {
158                 MSG(c, "emc_clariion_checker: Logical Unit is unbound "
159                     "or LUNZ");
160                 return PATH_DOWN;
161         }
162
163         /*
164          * store the LUN WWN there and compare that it indeed did not
165          * change in between, to protect against the path suddenly
166          * pointing somewhere else.
167          */
168         if (ct->wwn_set) {
169                 if (memcmp(ct->wwn, &sense_buffer[10], 16) != 0) {
170                         MSG(c, "emc_clariion_checker: Logical Unit WWN "
171                             "has changed!");
172                         return PATH_DOWN;
173                 }
174         } else {
175                 memcpy(ct->wwn, &sense_buffer[10], 16);
176                 ct->wwn_set = 1;
177         }
178
179         /*
180          * Issue read on active path to determine if inactive snapshot.
181          */
182         if (sense_buffer[4] == 2) {/* if active path */
183                 unsigned char buf[4096];
184
185                 memset(buf, 0, 4096);
186                 ret = sg_read(c->fd, &buf[0], 4096,
187                               sbb = &sb[0], SENSE_BUFF_LEN, c->timeout);
188                 if (ret == PATH_DOWN) {
189                         hexadecimal_to_ascii(ct->wwn, wwnstr);
190
191                         /*
192                          * Check for inactive snapshot LU this way.  Must
193                          * fail these.
194                          */
195                         if (((sbb[2]&0xf) == 5) && (sbb[12] == 0x25) &&
196                             (sbb[13]==1)) {
197                                 /*
198                                  * Do this so that we can fail even the
199                                  * passive paths which will return
200                                  * 02/04/03 not 05/25/01 on read.
201                                  */
202                                 SET_INACTIVE_SNAP(c);
203                                 condlog(3, "emc_clariion_checker: Active "
204                                         "path to inactive snapshot WWN %s.",
205                                         wwnstr);
206                         } else
207                                 MSG(c, "emc_clariion_checker: Read "
208                                         "error for WWN %s.  Sense data are "
209                                         "0x%x/0x%x/0x%x.", wwnstr,
210                                         sbb[2]&0xf, sbb[12], sbb[13]);
211                 } else {
212                         MSG(c, "emc_clariion_checker: Active path is "
213                             "healthy.");
214                         /*
215                          * Remove the path from the set of paths to inactive
216                          * snapshot LUs if it was in this list since the
217                          * snapshot is no longer inactive.
218                          */
219                         CLR_INACTIVE_SNAP(c);
220                 }
221         } else {
222                 if (IS_INACTIVE_SNAP(c)) {
223                         hexadecimal_to_ascii(ct->wwn, wwnstr);
224                         condlog(3, "emc_clariion_checker: Passive "
225                                 "path to inactive snapshot WWN %s.",
226                                 wwnstr);
227                         ret = PATH_DOWN;
228                 } else {
229                         MSG(c,
230                             "emc_clariion_checker: Passive path is healthy.");
231                         ret = PATH_UP;  /* not ghost */
232                 }
233         }
234
235         return ret;
236 }