5f24f55d8739c8b188cf81bbe0abccd7b95ed896
[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
30
31 #define CURRENT_PAGE_CODE_VALUES        0
32 #define CHANGEABLE_PAGE_CODE_VALUES     1
33
34 #define MSG_RDAC_UP    "rdac checker reports path is up"
35 #define MSG_RDAC_DOWN  "rdac checker reports path is down"
36 #define MSG_RDAC_GHOST "rdac checker reports path is ghost"
37
38 struct control_mode_page {
39         unsigned char header[8];
40         unsigned char page_code;
41         unsigned char page_len;
42         unsigned char dontcare0[3];
43         unsigned char tas_bit;
44         unsigned char dontcare1[6];
45 };
46
47 struct rdac_checker_context {
48         void * dummy;
49 };
50
51 int libcheck_init (struct checker * c)
52 {
53         unsigned char cmd[MODE_SEN_SEL_CMDLEN];
54         unsigned char sense_b[SENSE_BUFF_LEN];
55         struct sg_io_hdr io_hdr;
56         struct control_mode_page current, changeable;
57         int set = 0;
58
59         memset(cmd, 0, MODE_SEN_SEL_CMDLEN);
60         cmd[0] = MODE_SENSE_CMD;
61         cmd[1] = 0x08; /* DBD bit on */
62         cmd[2] = 0xA + (CURRENT_PAGE_CODE_VALUES << 6);
63         cmd[8] = (sizeof(struct control_mode_page) &  0xff);
64
65         memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
66         memset(sense_b, 0, SENSE_BUFF_LEN);
67         memset(&current, 0, sizeof(struct control_mode_page));
68
69         io_hdr.interface_id = 'S';
70         io_hdr.cmd_len = MODE_SEN_SEL_CMDLEN;
71         io_hdr.mx_sb_len = sizeof(sense_b);
72         io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
73         io_hdr.dxfer_len = (sizeof(struct control_mode_page) &  0xff);
74         io_hdr.dxferp = &current;
75         io_hdr.cmdp = cmd;
76         io_hdr.sbp = sense_b;
77         io_hdr.timeout = c->timeout;
78
79         if (ioctl(c->fd, SG_IO, &io_hdr) < 0)
80                 goto out;
81
82         /* check the TAS bit to see if it is already set */
83         if ((current.tas_bit >> 6) & 0x1) {
84                 set = 1;
85                 goto out;
86         }
87
88         /* get the changeble values */
89         cmd[2] = 0xA + (CHANGEABLE_PAGE_CODE_VALUES << 6);
90         io_hdr.dxferp = &changeable;
91         memset(&changeable, 0, sizeof(struct control_mode_page));
92
93         if (ioctl(c->fd, SG_IO, &io_hdr) < 0)
94                 goto out;
95
96         /* if TAS bit is not settable exit */
97         if (((changeable.tas_bit >> 6) & 0x1) == 0)
98                 goto out;
99
100         /* Now go ahead and set it */
101         memset(cmd, 0, MODE_SEN_SEL_CMDLEN);
102         cmd[0] = MODE_SELECT_CMD;
103         cmd[1] = 0x1; /* set SP bit on */
104         cmd[8] = (sizeof(struct control_mode_page) &  0xff);
105
106         /* use the same buffer as current, only set the tas bit */
107         current.page_code = 0xA;
108         current.page_len = 0xA;
109         current.tas_bit |= (1 << 6);
110
111         io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
112         io_hdr.dxferp = &current;
113
114         if (ioctl(c->fd, SG_IO, &io_hdr) < 0)
115                 goto out;
116
117         /* Success */
118         set = 1;
119 out:
120         if (set == 0)
121                 condlog(3, "rdac checker failed to set TAS bit");
122         return 0;
123 }
124
125 void libcheck_free (struct checker * c)
126 {
127         return;
128 }
129
130 static int
131 do_inq(int sg_fd, unsigned int pg_op, void *resp, int mx_resp_len,
132        unsigned int timeout)
133 {
134         unsigned char inqCmdBlk[INQUIRY_CMDLEN] = { INQUIRY_CMD, 1, 0, 0, 0, 0 };
135         unsigned char sense_b[SENSE_BUFF_LEN];
136         struct sg_io_hdr io_hdr;
137         int retry_rdac = 5;
138
139 retry:
140         inqCmdBlk[2] = (unsigned char) pg_op;
141         inqCmdBlk[4] = (unsigned char) (mx_resp_len & 0xff);
142         memset(&io_hdr, 0, sizeof (struct sg_io_hdr));
143         memset(sense_b, 0, SENSE_BUFF_LEN);
144
145         io_hdr.interface_id = 'S';
146         io_hdr.cmd_len = sizeof (inqCmdBlk);
147         io_hdr.mx_sb_len = sizeof (sense_b);
148         io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
149         io_hdr.dxfer_len = mx_resp_len;
150         io_hdr.dxferp = resp;
151         io_hdr.cmdp = inqCmdBlk;
152         io_hdr.sbp = sense_b;
153         io_hdr.timeout = timeout;
154
155         if (ioctl(sg_fd, SG_IO, &io_hdr) < 0)
156                 return 1;
157
158         /* treat SG_ERR here to get rid of sg_err.[ch] */
159         io_hdr.status &= 0x7e;
160         if ((0 == io_hdr.status) && (0 == io_hdr.host_status) &&
161             (0 == io_hdr.driver_status))
162                 return 0;
163
164         /* check if we need to retry this error */
165         if (io_hdr.info & SG_INFO_OK_MASK) {
166                 switch (io_hdr.host_status) {
167                 case DID_BUS_BUSY:
168                 case DID_ERROR:
169                 case DID_SOFT_ERROR:
170                 case DID_TRANSPORT_DISRUPTED:
171                         /* Transport error, retry */
172                         if (--retry_rdac)
173                                 goto retry;
174                         break;
175                 default:
176                         break;
177                 }
178         }
179
180         if ((SCSI_CHECK_CONDITION == io_hdr.status) ||
181             (SCSI_COMMAND_TERMINATED == io_hdr.status) ||
182             (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) {
183                 if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) {
184                         int sense_key;
185                         unsigned char * sense_buffer = io_hdr.sbp;
186                         if (sense_buffer[0] & 0x2)
187                                 sense_key = sense_buffer[1] & 0xf;
188                         else
189                                 sense_key = sense_buffer[2] & 0xf;
190                         if (RECOVERED_ERROR == sense_key)
191                                 return 0;
192                 }
193         }
194         return 1;
195 }
196
197 struct volume_access_inq
198 {
199         char PQ_PDT;
200         char dontcare0[7];
201         char avtcvp;
202         char dontcare1;
203         char asym_access_state_cur;
204         char vendor_specific_cur;
205         char dontcare2[36];
206 };
207
208 extern int
209 libcheck_check (struct checker * c)
210 {
211         struct volume_access_inq inq;
212         int ret;
213
214         memset(&inq, 0, sizeof(struct volume_access_inq));
215         if (0 != do_inq(c->fd, 0xC9, &inq, sizeof(struct volume_access_inq),
216                         c->timeout)) {
217                 ret = PATH_DOWN;
218                 goto done;
219         } else if (((inq.PQ_PDT & 0xE0) == 0x20) || (inq.PQ_PDT & 0x7f)) {
220                 /* LUN not connected*/
221                 ret = PATH_DOWN;
222                 goto done;
223         }
224
225         /* check if controller is in service mode */
226         if ((inq.avtcvp & 0x10) &&
227             ((inq.asym_access_state_cur & 0x0F) == 0x3) &&
228             (inq.vendor_specific_cur == 0x7)) {
229                 ret = PATH_DOWN;
230                 goto done;
231         }
232
233         /* If owner set or ioship mode is enabled return PATH_UP always */
234         if ((inq.avtcvp & 0x1) || ((inq.avtcvp >> 5) & 0x1))
235                 ret = PATH_UP;
236         else
237                 ret = PATH_GHOST;
238
239 done:
240         switch (ret) {
241         case PATH_DOWN:
242                 MSG(c, MSG_RDAC_DOWN);
243                 break;
244         case PATH_UP:
245                 MSG(c, MSG_RDAC_UP);
246                 break;
247         case PATH_GHOST:
248                 MSG(c, MSG_RDAC_GHOST);
249                 break;
250         }
251
252         return ret;
253 }