libmultipath: rdac checker: leave unsupported paths alone
[multipath-tools/.git] / libmultipath / checkers / rdac.c
1 /*
2  * Copyright (c) 2005 Christophe Varoqui
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 "checkers.h"
15 #include "debug.h"
16
17 #include "../libmultipath/sg_include.h"
18
19 #define INQUIRY_CMDLEN          6
20 #define INQUIRY_CMD             0x12
21 #define MODE_SENSE_CMD          0x5a
22 #define MODE_SELECT_CMD         0x55
23 #define MODE_SEN_SEL_CMDLEN     10
24 #define SENSE_BUFF_LEN          32
25 #define SCSI_CHECK_CONDITION    0x2
26 #define SCSI_COMMAND_TERMINATED 0x22
27 #define SG_ERR_DRIVER_SENSE     0x08
28 #define RECOVERED_ERROR         0x01
29 #define ILLEGAL_REQUEST         0x05
30
31
32 #define CURRENT_PAGE_CODE_VALUES        0
33 #define CHANGEABLE_PAGE_CODE_VALUES     1
34
35 #define MSG_RDAC_DOWN  " reports path is down"
36 #define MSG_RDAC_DOWN_TYPE(STR) MSG_RDAC_DOWN": "STR
37
38 #define RTPG_UNAVAILABLE        0x3
39 #define RTPG_OFFLINE            0xE
40 #define RTPG_TRANSITIONING      0xF
41
42 #define RTPG_UNAVAIL_NON_RESPONSIVE     0x2
43 #define RTPG_UNAVAIL_IN_RESET           0x3
44 #define RTPG_UNAVAIL_CFW_DL1            0x4
45 #define RTPG_UNAVAIL_CFW_DL2            0x5
46 #define RTPG_UNAVAIL_QUIESCED           0x6
47 #define RTPG_UNAVAIL_SERVICE_MODE       0x7
48
49 struct control_mode_page {
50         unsigned char header[8];
51         unsigned char page_code;
52         unsigned char page_len;
53         unsigned char dontcare0[3];
54         unsigned char tas_bit;
55         unsigned char dontcare1[6];
56 };
57
58 struct rdac_checker_context {
59         void * dummy;
60 };
61
62 int libcheck_init (struct checker * c)
63 {
64         unsigned char cmd[MODE_SEN_SEL_CMDLEN];
65         unsigned char sense_b[SENSE_BUFF_LEN];
66         struct sg_io_hdr io_hdr;
67         struct control_mode_page current, changeable;
68         int set = 0;
69
70         memset(cmd, 0, MODE_SEN_SEL_CMDLEN);
71         cmd[0] = MODE_SENSE_CMD;
72         cmd[1] = 0x08; /* DBD bit on */
73         cmd[2] = 0xA + (CURRENT_PAGE_CODE_VALUES << 6);
74         cmd[8] = (sizeof(struct control_mode_page) &  0xff);
75
76         memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
77         memset(sense_b, 0, SENSE_BUFF_LEN);
78         memset(&current, 0, sizeof(struct control_mode_page));
79
80         io_hdr.interface_id = 'S';
81         io_hdr.cmd_len = MODE_SEN_SEL_CMDLEN;
82         io_hdr.mx_sb_len = sizeof(sense_b);
83         io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
84         io_hdr.dxfer_len = (sizeof(struct control_mode_page) &  0xff);
85         io_hdr.dxferp = &current;
86         io_hdr.cmdp = cmd;
87         io_hdr.sbp = sense_b;
88         io_hdr.timeout = c->timeout * 1000;
89
90         if (ioctl(c->fd, SG_IO, &io_hdr) < 0)
91                 goto out;
92
93         /* check the TAS bit to see if it is already set */
94         if ((current.tas_bit >> 6) & 0x1) {
95                 set = 1;
96                 goto out;
97         }
98
99         /* get the changeble values */
100         cmd[2] = 0xA + (CHANGEABLE_PAGE_CODE_VALUES << 6);
101         io_hdr.dxferp = &changeable;
102         memset(&changeable, 0, sizeof(struct control_mode_page));
103
104         if (ioctl(c->fd, SG_IO, &io_hdr) < 0)
105                 goto out;
106
107         /* if TAS bit is not settable exit */
108         if (((changeable.tas_bit >> 6) & 0x1) == 0)
109                 goto out;
110
111         /* Now go ahead and set it */
112         memset(cmd, 0, MODE_SEN_SEL_CMDLEN);
113         cmd[0] = MODE_SELECT_CMD;
114         cmd[1] = 0x1; /* set SP bit on */
115         cmd[8] = (sizeof(struct control_mode_page) &  0xff);
116
117         /* use the same buffer as current, only set the tas bit */
118         current.page_code = 0xA;
119         current.page_len = 0xA;
120         current.tas_bit |= (1 << 6);
121
122         io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
123         io_hdr.dxferp = &current;
124
125         if (ioctl(c->fd, SG_IO, &io_hdr) < 0)
126                 goto out;
127
128         /* Success */
129         set = 1;
130 out:
131         if (set == 0)
132                 condlog(3, "rdac checker failed to set TAS bit");
133         return 0;
134 }
135
136 void libcheck_free (struct checker * c)
137 {
138         return;
139 }
140
141 static int
142 do_inq(int sg_fd, unsigned int pg_op, void *resp, int mx_resp_len,
143        unsigned int timeout)
144 {
145         unsigned char inqCmdBlk[INQUIRY_CMDLEN] = { INQUIRY_CMD, 1, 0, 0, 0, 0 };
146         unsigned char sense_b[SENSE_BUFF_LEN];
147         struct sg_io_hdr io_hdr;
148         int retry_rdac = 5;
149
150 retry:
151         inqCmdBlk[2] = (unsigned char) pg_op;
152         inqCmdBlk[4] = (unsigned char) (mx_resp_len & 0xff);
153         memset(&io_hdr, 0, sizeof (struct sg_io_hdr));
154         memset(sense_b, 0, SENSE_BUFF_LEN);
155
156         io_hdr.interface_id = 'S';
157         io_hdr.cmd_len = sizeof (inqCmdBlk);
158         io_hdr.mx_sb_len = sizeof (sense_b);
159         io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
160         io_hdr.dxfer_len = mx_resp_len;
161         io_hdr.dxferp = resp;
162         io_hdr.cmdp = inqCmdBlk;
163         io_hdr.sbp = sense_b;
164         io_hdr.timeout = timeout * 1000;
165
166         if (ioctl(sg_fd, SG_IO, &io_hdr) < 0 && errno == ENOTTY)
167                 return PATH_WILD;
168
169         /* treat SG_ERR here to get rid of sg_err.[ch] */
170         io_hdr.status &= 0x7e;
171         if ((0 == io_hdr.status) && (0 == io_hdr.host_status) &&
172             (0 == io_hdr.driver_status))
173                 return PATH_UP;
174
175         /* check if we need to retry this error */
176         if (io_hdr.info & SG_INFO_OK_MASK) {
177                 switch (io_hdr.host_status) {
178                 case DID_BUS_BUSY:
179                 case DID_ERROR:
180                 case DID_SOFT_ERROR:
181                 case DID_TRANSPORT_DISRUPTED:
182                         /* Transport error, retry */
183                         if (--retry_rdac)
184                                 goto retry;
185                         break;
186                 default:
187                         break;
188                 }
189         }
190
191         if ((SCSI_CHECK_CONDITION == io_hdr.status) ||
192             (SCSI_COMMAND_TERMINATED == io_hdr.status) ||
193             (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) {
194                 if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) {
195                         int sense_key;
196                         unsigned char * sense_buffer = io_hdr.sbp;
197                         if (sense_buffer[0] & 0x2)
198                                 sense_key = sense_buffer[1] & 0xf;
199                         else
200                                 sense_key = sense_buffer[2] & 0xf;
201                         if (RECOVERED_ERROR == sense_key)
202                                 return PATH_UP;
203                         else if (ILLEGAL_REQUEST == sense_key)
204                                 return PATH_WILD;
205                         condlog(1, "rdac checker: INQUIRY failed with sense key %02x",
206                                 sense_key);
207                 }
208         }
209         return PATH_DOWN;
210 }
211
212 struct volume_access_inq
213 {
214         char PQ_PDT;
215         char dontcare0[7];
216         char avtcvp;
217         char vol_ppp;
218         char aas_cur;
219         char vendor_specific_cur;
220         char aas_alt;
221         char vendor_specific_alt;
222         char dontcare1[34];
223 };
224
225 enum {
226         RDAC_MSGID_NOT_CONN = CHECKER_FIRST_MSGID,
227         RDAC_MSGID_IN_STARTUP,
228         RDAC_MSGID_NON_RESPONSIVE,
229         RDAC_MSGID_IN_RESET,
230         RDAC_MSGID_FW_DOWNLOADING,
231         RDAC_MSGID_QUIESCED,
232         RDAC_MSGID_SERVICE_MODE,
233         RDAC_MSGID_UNAVAILABLE,
234         RDAC_MSGID_INQUIRY_FAILED,
235 };
236
237 #define _IDX(x) (RDAC_MSGID_##x - CHECKER_FIRST_MSGID)
238 const char *libcheck_msgtable[] = {
239         [_IDX(NOT_CONN)] = MSG_RDAC_DOWN_TYPE("lun not connected"),
240         [_IDX(IN_STARTUP)] = MSG_RDAC_DOWN_TYPE("ctlr is in startup sequence"),
241         [_IDX(NON_RESPONSIVE)] =
242         MSG_RDAC_DOWN_TYPE("non-responsive to queries"),
243         [_IDX(IN_RESET)] = MSG_RDAC_DOWN_TYPE("ctlr held in reset"),
244         [_IDX(FW_DOWNLOADING)] =
245         MSG_RDAC_DOWN_TYPE("ctlr firmware downloading"),
246         [_IDX(QUIESCED)] = MSG_RDAC_DOWN_TYPE("ctlr quiesced by admin request"),
247         [_IDX(SERVICE_MODE)] = MSG_RDAC_DOWN_TYPE("ctlr is in service mode"),
248         [_IDX(UNAVAILABLE)] = MSG_RDAC_DOWN_TYPE("ctlr is unavailable"),
249         [_IDX(INQUIRY_FAILED)] = MSG_RDAC_DOWN_TYPE("inquiry failed"),
250         NULL,
251 };
252
253 static int
254 checker_msg_string(const struct volume_access_inq *inq)
255 {
256         /* lun not connected */
257         if (((inq->PQ_PDT & 0xE0) == 0x20) || (inq->PQ_PDT & 0x7f))
258                 return RDAC_MSGID_NOT_CONN;
259
260         /* if no tpg data is available, give the generic path down message */
261         if (!(inq->avtcvp & 0x10))
262                 return CHECKER_MSGID_DOWN;
263
264         /* controller is booting up */
265         if (((inq->aas_cur & 0x0F) == RTPG_TRANSITIONING) &&
266                 (inq->aas_alt & 0x0F) != RTPG_TRANSITIONING)
267                 return RDAC_MSGID_IN_STARTUP;
268
269         /* if not unavailable, give generic message */
270         if ((inq->aas_cur & 0x0F) != RTPG_UNAVAILABLE)
271                 return CHECKER_MSGID_DOWN;
272
273         /* target port group unavailable */
274         switch (inq->vendor_specific_cur) {
275         case RTPG_UNAVAIL_NON_RESPONSIVE:
276                 return RDAC_MSGID_NON_RESPONSIVE;
277         case RTPG_UNAVAIL_IN_RESET:
278                 return RDAC_MSGID_IN_RESET;
279         case RTPG_UNAVAIL_CFW_DL1:
280         case RTPG_UNAVAIL_CFW_DL2:
281                 return RDAC_MSGID_FW_DOWNLOADING;
282         case RTPG_UNAVAIL_QUIESCED:
283                 return RDAC_MSGID_QUIESCED;
284         case RTPG_UNAVAIL_SERVICE_MODE:
285                 return RDAC_MSGID_SERVICE_MODE;
286         default:
287                 return RDAC_MSGID_UNAVAILABLE;
288         }
289 }
290
291 int libcheck_check(struct checker * c)
292 {
293         struct volume_access_inq inq;
294         int ret, inqfail;
295
296         inqfail = 0;
297         memset(&inq, 0, sizeof(struct volume_access_inq));
298         ret = do_inq(c->fd, 0xC9, &inq, sizeof(struct volume_access_inq),
299                      c->timeout);
300         if (ret != PATH_UP) {
301                 inqfail = 1;
302                 goto done;
303         }
304
305         if (((inq.PQ_PDT & 0xE0) == 0x20) || (inq.PQ_PDT & 0x7f)) {
306                 /* LUN not connected*/
307                 ret = PATH_DOWN;
308                 goto done;
309         }
310
311         /* If TPGDE bit set, evaluate TPG information */
312         if ((inq.avtcvp & 0x10)) {
313                 switch (inq.aas_cur & 0x0F) {
314                 /* Never use the path if it reports unavailable */
315                 case RTPG_UNAVAILABLE:
316                         ret = PATH_DOWN;
317                         goto done;
318                 /*
319                  * If both controllers report transitioning, it
320                  * means mode select or STPG is being processed.
321                  *
322                  * If this controller alone is transitioning, it's
323                  * booting and we shouldn't use it yet.
324                  */
325                 case RTPG_TRANSITIONING:
326                         if ((inq.aas_alt & 0xF) != RTPG_TRANSITIONING) {
327                                 ret = PATH_DOWN;
328                                 goto done;
329                         }
330                         break;
331                 }
332         }
333
334         /* If owner set or ioship mode is enabled return PATH_UP always */
335         if ((inq.avtcvp & 0x1) || ((inq.avtcvp >> 5) & 0x1))
336                 ret = PATH_UP;
337         else
338                 ret = PATH_GHOST;
339
340 done:
341         switch (ret) {
342         case PATH_WILD:
343                 c->msgid = CHECKER_MSGID_UNSUPPORTED;
344                 break;
345         case PATH_DOWN:
346                 c->msgid = (inqfail ? RDAC_MSGID_INQUIRY_FAILED :
347                             checker_msg_string(&inq));
348                 break;
349         case PATH_UP:
350                 c->msgid = CHECKER_MSGID_UP;
351                 break;
352         case PATH_GHOST:
353                 c->msgid = CHECKER_MSGID_GHOST;
354                 break;
355         }
356
357         return ret;
358 }