diff options
author | Hannes Reinecke <hare@suse.de> | 2008-07-17 19:53:21 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-07-26 15:14:52 -0400 |
commit | 057ea7c9683c3d684128cced796f03c179ecf1c2 (patch) | |
tree | ff9494b4da41258c06ef44ab5f7a6ed04daf3d98 /drivers/scsi/device_handler | |
parent | ca9f0089867c9e476cf2e6d4615d2aae887171b2 (diff) |
[SCSI] scsi_dh: add generic SPC-3 alua handler
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi/device_handler')
-rw-r--r-- | drivers/scsi/device_handler/Kconfig | 8 | ||||
-rw-r--r-- | drivers/scsi/device_handler/Makefile | 1 | ||||
-rw-r--r-- | drivers/scsi/device_handler/scsi_dh_alua.c | 802 |
3 files changed, 811 insertions, 0 deletions
diff --git a/drivers/scsi/device_handler/Kconfig b/drivers/scsi/device_handler/Kconfig index 2adc0f666b68..67070257919f 100644 --- a/drivers/scsi/device_handler/Kconfig +++ b/drivers/scsi/device_handler/Kconfig | |||
@@ -30,3 +30,11 @@ config SCSI_DH_EMC | |||
30 | depends on SCSI_DH | 30 | depends on SCSI_DH |
31 | help | 31 | help |
32 | If you have a EMC CLARiiON select y. Otherwise, say N. | 32 | If you have a EMC CLARiiON select y. Otherwise, say N. |
33 | |||
34 | config SCSI_DH_ALUA | ||
35 | tristate "SPC-3 ALUA Device Handler (EXPERIMENTAL)" | ||
36 | depends on SCSI_DH && EXPERIMENTAL | ||
37 | help | ||
38 | SCSI Device handler for generic SPC-3 Asymmetric Logical Unit | ||
39 | Access (ALUA). | ||
40 | |||
diff --git a/drivers/scsi/device_handler/Makefile b/drivers/scsi/device_handler/Makefile index 35272e93b1c8..e1d2ea083e15 100644 --- a/drivers/scsi/device_handler/Makefile +++ b/drivers/scsi/device_handler/Makefile | |||
@@ -5,3 +5,4 @@ obj-$(CONFIG_SCSI_DH) += scsi_dh.o | |||
5 | obj-$(CONFIG_SCSI_DH_RDAC) += scsi_dh_rdac.o | 5 | obj-$(CONFIG_SCSI_DH_RDAC) += scsi_dh_rdac.o |
6 | obj-$(CONFIG_SCSI_DH_HP_SW) += scsi_dh_hp_sw.o | 6 | obj-$(CONFIG_SCSI_DH_HP_SW) += scsi_dh_hp_sw.o |
7 | obj-$(CONFIG_SCSI_DH_EMC) += scsi_dh_emc.o | 7 | obj-$(CONFIG_SCSI_DH_EMC) += scsi_dh_emc.o |
8 | obj-$(CONFIG_SCSI_DH_ALUA) += scsi_dh_alua.o | ||
diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c new file mode 100644 index 000000000000..6e464488bcab --- /dev/null +++ b/drivers/scsi/device_handler/scsi_dh_alua.c | |||
@@ -0,0 +1,802 @@ | |||
1 | /* | ||
2 | * Generic SCSI-3 ALUA SCSI Device Handler | ||
3 | * | ||
4 | * Copyright (C) 2007, 2008 Hannes Reinecke, SUSE Linux Products GmbH. | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
20 | * | ||
21 | */ | ||
22 | #include <scsi/scsi.h> | ||
23 | #include <scsi/scsi_eh.h> | ||
24 | #include <scsi/scsi_dh.h> | ||
25 | |||
26 | #define ALUA_DH_NAME "alua" | ||
27 | #define ALUA_DH_VER "1.2" | ||
28 | |||
29 | #define TPGS_STATE_OPTIMIZED 0x0 | ||
30 | #define TPGS_STATE_NONOPTIMIZED 0x1 | ||
31 | #define TPGS_STATE_STANDBY 0x2 | ||
32 | #define TPGS_STATE_UNAVAILABLE 0x3 | ||
33 | #define TPGS_STATE_OFFLINE 0xe | ||
34 | #define TPGS_STATE_TRANSITIONING 0xf | ||
35 | |||
36 | #define TPGS_SUPPORT_NONE 0x00 | ||
37 | #define TPGS_SUPPORT_OPTIMIZED 0x01 | ||
38 | #define TPGS_SUPPORT_NONOPTIMIZED 0x02 | ||
39 | #define TPGS_SUPPORT_STANDBY 0x04 | ||
40 | #define TPGS_SUPPORT_UNAVAILABLE 0x08 | ||
41 | #define TPGS_SUPPORT_OFFLINE 0x40 | ||
42 | #define TPGS_SUPPORT_TRANSITION 0x80 | ||
43 | |||
44 | #define TPGS_MODE_UNINITIALIZED -1 | ||
45 | #define TPGS_MODE_NONE 0x0 | ||
46 | #define TPGS_MODE_IMPLICIT 0x1 | ||
47 | #define TPGS_MODE_EXPLICIT 0x2 | ||
48 | |||
49 | #define ALUA_INQUIRY_SIZE 36 | ||
50 | #define ALUA_FAILOVER_TIMEOUT (60 * HZ) | ||
51 | #define ALUA_FAILOVER_RETRIES 5 | ||
52 | |||
53 | struct alua_dh_data { | ||
54 | int group_id; | ||
55 | int rel_port; | ||
56 | int tpgs; | ||
57 | int state; | ||
58 | unsigned char inq[ALUA_INQUIRY_SIZE]; | ||
59 | unsigned char *buff; | ||
60 | int bufflen; | ||
61 | unsigned char sense[SCSI_SENSE_BUFFERSIZE]; | ||
62 | int senselen; | ||
63 | }; | ||
64 | |||
65 | #define ALUA_POLICY_SWITCH_CURRENT 0 | ||
66 | #define ALUA_POLICY_SWITCH_ALL 1 | ||
67 | |||
68 | static inline struct alua_dh_data *get_alua_data(struct scsi_device *sdev) | ||
69 | { | ||
70 | struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data; | ||
71 | BUG_ON(scsi_dh_data == NULL); | ||
72 | return ((struct alua_dh_data *) scsi_dh_data->buf); | ||
73 | } | ||
74 | |||
75 | static int realloc_buffer(struct alua_dh_data *h, unsigned len) | ||
76 | { | ||
77 | if (h->buff && h->buff != h->inq) | ||
78 | kfree(h->buff); | ||
79 | |||
80 | h->buff = kmalloc(len, GFP_NOIO); | ||
81 | if (!h->buff) { | ||
82 | h->buff = h->inq; | ||
83 | h->bufflen = ALUA_INQUIRY_SIZE; | ||
84 | return 1; | ||
85 | } | ||
86 | h->bufflen = len; | ||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | static struct request *get_alua_req(struct scsi_device *sdev, | ||
91 | void *buffer, unsigned buflen, int rw) | ||
92 | { | ||
93 | struct request *rq; | ||
94 | struct request_queue *q = sdev->request_queue; | ||
95 | |||
96 | rq = blk_get_request(q, rw, GFP_NOIO); | ||
97 | |||
98 | if (!rq) { | ||
99 | sdev_printk(KERN_INFO, sdev, | ||
100 | "%s: blk_get_request failed\n", __FUNCTION__); | ||
101 | return NULL; | ||
102 | } | ||
103 | |||
104 | if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_NOIO)) { | ||
105 | blk_put_request(rq); | ||
106 | sdev_printk(KERN_INFO, sdev, | ||
107 | "%s: blk_rq_map_kern failed\n", __FUNCTION__); | ||
108 | return NULL; | ||
109 | } | ||
110 | |||
111 | rq->cmd_type = REQ_TYPE_BLOCK_PC; | ||
112 | rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE; | ||
113 | rq->retries = ALUA_FAILOVER_RETRIES; | ||
114 | rq->timeout = ALUA_FAILOVER_TIMEOUT; | ||
115 | |||
116 | return rq; | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * submit_std_inquiry - Issue a standard INQUIRY command | ||
121 | * @sdev: sdev the command should be send to | ||
122 | */ | ||
123 | static int submit_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h) | ||
124 | { | ||
125 | struct request *rq; | ||
126 | int err = SCSI_DH_RES_TEMP_UNAVAIL; | ||
127 | |||
128 | rq = get_alua_req(sdev, h->inq, ALUA_INQUIRY_SIZE, READ); | ||
129 | if (!rq) | ||
130 | goto done; | ||
131 | |||
132 | /* Prepare the command. */ | ||
133 | rq->cmd[0] = INQUIRY; | ||
134 | rq->cmd[1] = 0; | ||
135 | rq->cmd[2] = 0; | ||
136 | rq->cmd[4] = ALUA_INQUIRY_SIZE; | ||
137 | rq->cmd_len = COMMAND_SIZE(INQUIRY); | ||
138 | |||
139 | rq->sense = h->sense; | ||
140 | memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); | ||
141 | rq->sense_len = h->senselen = 0; | ||
142 | |||
143 | err = blk_execute_rq(rq->q, NULL, rq, 1); | ||
144 | if (err == -EIO) { | ||
145 | sdev_printk(KERN_INFO, sdev, | ||
146 | "%s: std inquiry failed with %x\n", | ||
147 | ALUA_DH_NAME, rq->errors); | ||
148 | h->senselen = rq->sense_len; | ||
149 | err = SCSI_DH_IO; | ||
150 | } | ||
151 | blk_put_request(rq); | ||
152 | done: | ||
153 | return err; | ||
154 | } | ||
155 | |||
156 | /* | ||
157 | * submit_vpd_inquiry - Issue an INQUIRY VPD page 0x83 command | ||
158 | * @sdev: sdev the command should be sent to | ||
159 | */ | ||
160 | static int submit_vpd_inquiry(struct scsi_device *sdev, struct alua_dh_data *h) | ||
161 | { | ||
162 | struct request *rq; | ||
163 | int err = SCSI_DH_RES_TEMP_UNAVAIL; | ||
164 | |||
165 | rq = get_alua_req(sdev, h->buff, h->bufflen, READ); | ||
166 | if (!rq) | ||
167 | goto done; | ||
168 | |||
169 | /* Prepare the command. */ | ||
170 | rq->cmd[0] = INQUIRY; | ||
171 | rq->cmd[1] = 1; | ||
172 | rq->cmd[2] = 0x83; | ||
173 | rq->cmd[4] = h->bufflen; | ||
174 | rq->cmd_len = COMMAND_SIZE(INQUIRY); | ||
175 | |||
176 | rq->sense = h->sense; | ||
177 | memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); | ||
178 | rq->sense_len = h->senselen = 0; | ||
179 | |||
180 | err = blk_execute_rq(rq->q, NULL, rq, 1); | ||
181 | if (err == -EIO) { | ||
182 | sdev_printk(KERN_INFO, sdev, | ||
183 | "%s: evpd inquiry failed with %x\n", | ||
184 | ALUA_DH_NAME, rq->errors); | ||
185 | h->senselen = rq->sense_len; | ||
186 | err = SCSI_DH_IO; | ||
187 | } | ||
188 | blk_put_request(rq); | ||
189 | done: | ||
190 | return err; | ||
191 | } | ||
192 | |||
193 | /* | ||
194 | * submit_rtpg - Issue a REPORT TARGET GROUP STATES command | ||
195 | * @sdev: sdev the command should be sent to | ||
196 | */ | ||
197 | static unsigned submit_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) | ||
198 | { | ||
199 | struct request *rq; | ||
200 | int err = SCSI_DH_RES_TEMP_UNAVAIL; | ||
201 | |||
202 | rq = get_alua_req(sdev, h->buff, h->bufflen, READ); | ||
203 | if (!rq) | ||
204 | goto done; | ||
205 | |||
206 | /* Prepare the command. */ | ||
207 | rq->cmd[0] = MAINTENANCE_IN; | ||
208 | rq->cmd[1] = MI_REPORT_TARGET_PGS; | ||
209 | rq->cmd[6] = (h->bufflen >> 24) & 0xff; | ||
210 | rq->cmd[7] = (h->bufflen >> 16) & 0xff; | ||
211 | rq->cmd[8] = (h->bufflen >> 8) & 0xff; | ||
212 | rq->cmd[9] = h->bufflen & 0xff; | ||
213 | rq->cmd_len = COMMAND_SIZE(MAINTENANCE_IN); | ||
214 | |||
215 | rq->sense = h->sense; | ||
216 | memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); | ||
217 | rq->sense_len = h->senselen = 0; | ||
218 | |||
219 | err = blk_execute_rq(rq->q, NULL, rq, 1); | ||
220 | if (err == -EIO) { | ||
221 | sdev_printk(KERN_INFO, sdev, | ||
222 | "%s: rtpg failed with %x\n", | ||
223 | ALUA_DH_NAME, rq->errors); | ||
224 | h->senselen = rq->sense_len; | ||
225 | err = SCSI_DH_IO; | ||
226 | } | ||
227 | blk_put_request(rq); | ||
228 | done: | ||
229 | return err; | ||
230 | } | ||
231 | |||
232 | /* | ||
233 | * submit_stpg - Issue a SET TARGET GROUP STATES command | ||
234 | * @sdev: sdev the command should be sent to | ||
235 | * | ||
236 | * Currently we're only setting the current target port group state | ||
237 | * to 'active/optimized' and let the array firmware figure out | ||
238 | * the states of the remaining groups. | ||
239 | */ | ||
240 | static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h) | ||
241 | { | ||
242 | struct request *rq; | ||
243 | int err = SCSI_DH_RES_TEMP_UNAVAIL; | ||
244 | int stpg_len = 8; | ||
245 | |||
246 | /* Prepare the data buffer */ | ||
247 | memset(h->buff, 0, stpg_len); | ||
248 | h->buff[4] = TPGS_STATE_OPTIMIZED & 0x0f; | ||
249 | h->buff[6] = (h->group_id >> 8) & 0x0f; | ||
250 | h->buff[7] = h->group_id & 0x0f; | ||
251 | |||
252 | rq = get_alua_req(sdev, h->buff, stpg_len, WRITE); | ||
253 | if (!rq) | ||
254 | goto done; | ||
255 | |||
256 | /* Prepare the command. */ | ||
257 | rq->cmd[0] = MAINTENANCE_OUT; | ||
258 | rq->cmd[1] = MO_SET_TARGET_PGS; | ||
259 | rq->cmd[6] = (stpg_len >> 24) & 0xff; | ||
260 | rq->cmd[7] = (stpg_len >> 16) & 0xff; | ||
261 | rq->cmd[8] = (stpg_len >> 8) & 0xff; | ||
262 | rq->cmd[9] = stpg_len & 0xff; | ||
263 | rq->cmd_len = COMMAND_SIZE(MAINTENANCE_OUT); | ||
264 | |||
265 | rq->sense = h->sense; | ||
266 | memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); | ||
267 | rq->sense_len = h->senselen = 0; | ||
268 | |||
269 | err = blk_execute_rq(rq->q, NULL, rq, 1); | ||
270 | if (err == -EIO) { | ||
271 | sdev_printk(KERN_INFO, sdev, | ||
272 | "%s: stpg failed with %x\n", | ||
273 | ALUA_DH_NAME, rq->errors); | ||
274 | h->senselen = rq->sense_len; | ||
275 | err = SCSI_DH_IO; | ||
276 | } | ||
277 | blk_put_request(rq); | ||
278 | done: | ||
279 | return err; | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | * alua_std_inquiry - Evaluate standard INQUIRY command | ||
284 | * @sdev: device to be checked | ||
285 | * | ||
286 | * Just extract the TPGS setting to find out if ALUA | ||
287 | * is supported. | ||
288 | */ | ||
289 | static int alua_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h) | ||
290 | { | ||
291 | int err; | ||
292 | |||
293 | err = submit_std_inquiry(sdev, h); | ||
294 | |||
295 | if (err != SCSI_DH_OK) | ||
296 | return err; | ||
297 | |||
298 | /* Check TPGS setting */ | ||
299 | h->tpgs = (h->inq[5] >> 4) & 0x3; | ||
300 | switch (h->tpgs) { | ||
301 | case TPGS_MODE_EXPLICIT|TPGS_MODE_IMPLICIT: | ||
302 | sdev_printk(KERN_INFO, sdev, | ||
303 | "%s: supports implicit and explicit TPGS\n", | ||
304 | ALUA_DH_NAME); | ||
305 | break; | ||
306 | case TPGS_MODE_EXPLICIT: | ||
307 | sdev_printk(KERN_INFO, sdev, "%s: supports explicit TPGS\n", | ||
308 | ALUA_DH_NAME); | ||
309 | break; | ||
310 | case TPGS_MODE_IMPLICIT: | ||
311 | sdev_printk(KERN_INFO, sdev, "%s: supports implicit TPGS\n", | ||
312 | ALUA_DH_NAME); | ||
313 | break; | ||
314 | default: | ||
315 | h->tpgs = TPGS_MODE_NONE; | ||
316 | sdev_printk(KERN_INFO, sdev, "%s: not supported\n", | ||
317 | ALUA_DH_NAME); | ||
318 | err = SCSI_DH_DEV_UNSUPP; | ||
319 | break; | ||
320 | } | ||
321 | |||
322 | return err; | ||
323 | } | ||
324 | |||
325 | /* | ||
326 | * alua_vpd_inquiry - Evaluate INQUIRY vpd page 0x83 | ||
327 | * @sdev: device to be checked | ||
328 | * | ||
329 | * Extract the relative target port and the target port group | ||
330 | * descriptor from the list of identificators. | ||
331 | */ | ||
332 | static int alua_vpd_inquiry(struct scsi_device *sdev, struct alua_dh_data *h) | ||
333 | { | ||
334 | int len; | ||
335 | unsigned err; | ||
336 | unsigned char *d; | ||
337 | |||
338 | retry: | ||
339 | err = submit_vpd_inquiry(sdev, h); | ||
340 | |||
341 | if (err != SCSI_DH_OK) | ||
342 | return err; | ||
343 | |||
344 | /* Check if vpd page exceeds initial buffer */ | ||
345 | len = (h->buff[2] << 8) + h->buff[3] + 4; | ||
346 | if (len > h->bufflen) { | ||
347 | /* Resubmit with the correct length */ | ||
348 | if (realloc_buffer(h, len)) { | ||
349 | sdev_printk(KERN_WARNING, sdev, | ||
350 | "%s: kmalloc buffer failed\n", | ||
351 | ALUA_DH_NAME); | ||
352 | /* Temporary failure, bypass */ | ||
353 | return SCSI_DH_DEV_TEMP_BUSY; | ||
354 | } | ||
355 | goto retry; | ||
356 | } | ||
357 | |||
358 | /* | ||
359 | * Now look for the correct descriptor. | ||
360 | */ | ||
361 | d = h->buff + 4; | ||
362 | while (d < h->buff + len) { | ||
363 | switch (d[1] & 0xf) { | ||
364 | case 0x4: | ||
365 | /* Relative target port */ | ||
366 | h->rel_port = (d[6] << 8) + d[7]; | ||
367 | break; | ||
368 | case 0x5: | ||
369 | /* Target port group */ | ||
370 | h->group_id = (d[6] << 8) + d[7]; | ||
371 | break; | ||
372 | default: | ||
373 | break; | ||
374 | } | ||
375 | d += d[3] + 4; | ||
376 | } | ||
377 | |||
378 | if (h->group_id == -1) { | ||
379 | /* | ||
380 | * Internal error; TPGS supported but required | ||
381 | * VPD identification descriptors not present. | ||
382 | * Disable ALUA support | ||
383 | */ | ||
384 | sdev_printk(KERN_INFO, sdev, | ||
385 | "%s: No target port descriptors found\n", | ||
386 | ALUA_DH_NAME); | ||
387 | h->state = TPGS_STATE_OPTIMIZED; | ||
388 | h->tpgs = TPGS_MODE_NONE; | ||
389 | err = SCSI_DH_DEV_UNSUPP; | ||
390 | } else { | ||
391 | sdev_printk(KERN_INFO, sdev, | ||
392 | "%s: port group %02x rel port %02x\n", | ||
393 | ALUA_DH_NAME, h->group_id, h->rel_port); | ||
394 | } | ||
395 | |||
396 | return err; | ||
397 | } | ||
398 | |||
399 | static char print_alua_state(int state) | ||
400 | { | ||
401 | switch (state) { | ||
402 | case TPGS_STATE_OPTIMIZED: | ||
403 | return 'A'; | ||
404 | case TPGS_STATE_NONOPTIMIZED: | ||
405 | return 'N'; | ||
406 | case TPGS_STATE_STANDBY: | ||
407 | return 'S'; | ||
408 | case TPGS_STATE_UNAVAILABLE: | ||
409 | return 'U'; | ||
410 | case TPGS_STATE_OFFLINE: | ||
411 | return 'O'; | ||
412 | case TPGS_STATE_TRANSITIONING: | ||
413 | return 'T'; | ||
414 | default: | ||
415 | return 'X'; | ||
416 | } | ||
417 | } | ||
418 | |||
419 | static int alua_check_sense(struct scsi_device *sdev, | ||
420 | struct scsi_sense_hdr *sense_hdr) | ||
421 | { | ||
422 | switch (sense_hdr->sense_key) { | ||
423 | case NOT_READY: | ||
424 | if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0a) | ||
425 | /* | ||
426 | * LUN Not Accessible - ALUA state transition | ||
427 | */ | ||
428 | return NEEDS_RETRY; | ||
429 | if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0b) | ||
430 | /* | ||
431 | * LUN Not Accessible -- Target port in standby state | ||
432 | */ | ||
433 | return SUCCESS; | ||
434 | if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0c) | ||
435 | /* | ||
436 | * LUN Not Accessible -- Target port in unavailable state | ||
437 | */ | ||
438 | return SUCCESS; | ||
439 | if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x12) | ||
440 | /* | ||
441 | * LUN Not Ready -- Offline | ||
442 | */ | ||
443 | return SUCCESS; | ||
444 | break; | ||
445 | case UNIT_ATTENTION: | ||
446 | if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00) | ||
447 | /* | ||
448 | * Power On, Reset, or Bus Device Reset, just retry. | ||
449 | */ | ||
450 | return NEEDS_RETRY; | ||
451 | if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x06) { | ||
452 | /* | ||
453 | * ALUA state changed | ||
454 | */ | ||
455 | return NEEDS_RETRY; | ||
456 | } | ||
457 | if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x07) { | ||
458 | /* | ||
459 | * Implicit ALUA state transition failed | ||
460 | */ | ||
461 | return NEEDS_RETRY; | ||
462 | } | ||
463 | break; | ||
464 | } | ||
465 | |||
466 | return SCSI_RETURN_NOT_HANDLED; | ||
467 | } | ||
468 | |||
469 | /* | ||
470 | * alua_stpg - Evaluate SET TARGET GROUP STATES | ||
471 | * @sdev: the device to be evaluated | ||
472 | * @state: the new target group state | ||
473 | * | ||
474 | * Send a SET TARGET GROUP STATES command to the device. | ||
475 | * We only have to test here if we should resubmit the command; | ||
476 | * any other error is assumed as a failure. | ||
477 | */ | ||
478 | static int alua_stpg(struct scsi_device *sdev, int state, | ||
479 | struct alua_dh_data *h) | ||
480 | { | ||
481 | struct scsi_sense_hdr sense_hdr; | ||
482 | unsigned err; | ||
483 | int retry = ALUA_FAILOVER_RETRIES; | ||
484 | |||
485 | retry: | ||
486 | err = submit_stpg(sdev, h); | ||
487 | if (err == SCSI_DH_IO && h->senselen > 0) { | ||
488 | err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, | ||
489 | &sense_hdr); | ||
490 | if (!err) | ||
491 | return SCSI_DH_IO; | ||
492 | err = alua_check_sense(sdev, &sense_hdr); | ||
493 | if (retry > 0 && err == NEEDS_RETRY) { | ||
494 | retry--; | ||
495 | goto retry; | ||
496 | } | ||
497 | sdev_printk(KERN_INFO, sdev, | ||
498 | "%s: stpg sense code: %02x/%02x/%02x\n", | ||
499 | ALUA_DH_NAME, sense_hdr.sense_key, | ||
500 | sense_hdr.asc, sense_hdr.ascq); | ||
501 | err = SCSI_DH_IO; | ||
502 | } | ||
503 | if (err == SCSI_DH_OK) { | ||
504 | h->state = state; | ||
505 | sdev_printk(KERN_INFO, sdev, | ||
506 | "%s: port group %02x switched to state %c\n", | ||
507 | ALUA_DH_NAME, h->group_id, | ||
508 | print_alua_state(h->state) ); | ||
509 | } | ||
510 | return err; | ||
511 | } | ||
512 | |||
513 | /* | ||
514 | * alua_rtpg - Evaluate REPORT TARGET GROUP STATES | ||
515 | * @sdev: the device to be evaluated. | ||
516 | * | ||
517 | * Evaluate the Target Port Group State. | ||
518 | * Returns SCSI_DH_DEV_OFFLINED if the path is | ||
519 | * found to be unuseable. | ||
520 | */ | ||
521 | static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) | ||
522 | { | ||
523 | struct scsi_sense_hdr sense_hdr; | ||
524 | int len, k, off, valid_states = 0; | ||
525 | char *ucp; | ||
526 | unsigned err; | ||
527 | |||
528 | retry: | ||
529 | err = submit_rtpg(sdev, h); | ||
530 | |||
531 | if (err == SCSI_DH_IO && h->senselen > 0) { | ||
532 | err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, | ||
533 | &sense_hdr); | ||
534 | if (!err) | ||
535 | return SCSI_DH_IO; | ||
536 | |||
537 | err = alua_check_sense(sdev, &sense_hdr); | ||
538 | if (err == NEEDS_RETRY) | ||
539 | goto retry; | ||
540 | sdev_printk(KERN_INFO, sdev, | ||
541 | "%s: rtpg sense code %02x/%02x/%02x\n", | ||
542 | ALUA_DH_NAME, sense_hdr.sense_key, | ||
543 | sense_hdr.asc, sense_hdr.ascq); | ||
544 | err = SCSI_DH_IO; | ||
545 | } | ||
546 | if (err != SCSI_DH_OK) | ||
547 | return err; | ||
548 | |||
549 | len = (h->buff[0] << 24) + (h->buff[1] << 16) + | ||
550 | (h->buff[2] << 8) + h->buff[3] + 4; | ||
551 | |||
552 | if (len > h->bufflen) { | ||
553 | /* Resubmit with the correct length */ | ||
554 | if (realloc_buffer(h, len)) { | ||
555 | sdev_printk(KERN_WARNING, sdev, | ||
556 | "%s: kmalloc buffer failed\n",__FUNCTION__); | ||
557 | /* Temporary failure, bypass */ | ||
558 | return SCSI_DH_DEV_TEMP_BUSY; | ||
559 | } | ||
560 | goto retry; | ||
561 | } | ||
562 | |||
563 | for (k = 4, ucp = h->buff + 4; k < len; k += off, ucp += off) { | ||
564 | if (h->group_id == (ucp[2] << 8) + ucp[3]) { | ||
565 | h->state = ucp[0] & 0x0f; | ||
566 | valid_states = ucp[1]; | ||
567 | } | ||
568 | off = 8 + (ucp[7] * 4); | ||
569 | } | ||
570 | |||
571 | sdev_printk(KERN_INFO, sdev, | ||
572 | "%s: port group %02x state %c supports %c%c%c%c%c%c\n", | ||
573 | ALUA_DH_NAME, h->group_id, print_alua_state(h->state), | ||
574 | valid_states&TPGS_SUPPORT_TRANSITION?'T':'t', | ||
575 | valid_states&TPGS_SUPPORT_OFFLINE?'O':'o', | ||
576 | valid_states&TPGS_SUPPORT_UNAVAILABLE?'U':'u', | ||
577 | valid_states&TPGS_SUPPORT_STANDBY?'S':'s', | ||
578 | valid_states&TPGS_SUPPORT_NONOPTIMIZED?'N':'n', | ||
579 | valid_states&TPGS_SUPPORT_OPTIMIZED?'A':'a'); | ||
580 | |||
581 | if (h->tpgs & TPGS_MODE_EXPLICIT) { | ||
582 | switch (h->state) { | ||
583 | case TPGS_STATE_TRANSITIONING: | ||
584 | /* State transition, retry */ | ||
585 | goto retry; | ||
586 | break; | ||
587 | case TPGS_STATE_OFFLINE: | ||
588 | /* Path is offline, fail */ | ||
589 | err = SCSI_DH_DEV_OFFLINED; | ||
590 | break; | ||
591 | default: | ||
592 | break; | ||
593 | } | ||
594 | } else { | ||
595 | /* Only Implicit ALUA support */ | ||
596 | if (h->state == TPGS_STATE_OPTIMIZED || | ||
597 | h->state == TPGS_STATE_NONOPTIMIZED || | ||
598 | h->state == TPGS_STATE_STANDBY) | ||
599 | /* Useable path if active */ | ||
600 | err = SCSI_DH_OK; | ||
601 | else | ||
602 | /* Path unuseable for unavailable/offline */ | ||
603 | err = SCSI_DH_DEV_OFFLINED; | ||
604 | } | ||
605 | return err; | ||
606 | } | ||
607 | |||
608 | /* | ||
609 | * alua_initialize - Initialize ALUA state | ||
610 | * @sdev: the device to be initialized | ||
611 | * | ||
612 | * For the prep_fn to work correctly we have | ||
613 | * to initialize the ALUA state for the device. | ||
614 | */ | ||
615 | static int alua_initialize(struct scsi_device *sdev, struct alua_dh_data *h) | ||
616 | { | ||
617 | int err; | ||
618 | |||
619 | err = alua_std_inquiry(sdev, h); | ||
620 | if (err != SCSI_DH_OK) | ||
621 | goto out; | ||
622 | |||
623 | err = alua_vpd_inquiry(sdev, h); | ||
624 | if (err != SCSI_DH_OK) | ||
625 | goto out; | ||
626 | |||
627 | err = alua_rtpg(sdev, h); | ||
628 | if (err != SCSI_DH_OK) | ||
629 | goto out; | ||
630 | |||
631 | out: | ||
632 | return err; | ||
633 | } | ||
634 | |||
635 | /* | ||
636 | * alua_activate - activate a path | ||
637 | * @sdev: device on the path to be activated | ||
638 | * | ||
639 | * We're currently switching the port group to be activated only and | ||
640 | * let the array figure out the rest. | ||
641 | * There may be other arrays which require us to switch all port groups | ||
642 | * based on a certain policy. But until we actually encounter them it | ||
643 | * should be okay. | ||
644 | */ | ||
645 | static int alua_activate(struct scsi_device *sdev) | ||
646 | { | ||
647 | struct alua_dh_data *h = get_alua_data(sdev); | ||
648 | int err = SCSI_DH_OK; | ||
649 | |||
650 | if (h->group_id != -1) { | ||
651 | err = alua_rtpg(sdev, h); | ||
652 | if (err != SCSI_DH_OK) | ||
653 | goto out; | ||
654 | } | ||
655 | |||
656 | if (h->tpgs == TPGS_MODE_EXPLICIT && h->state != TPGS_STATE_OPTIMIZED) | ||
657 | err = alua_stpg(sdev, TPGS_STATE_OPTIMIZED, h); | ||
658 | |||
659 | out: | ||
660 | return err; | ||
661 | } | ||
662 | |||
663 | /* | ||
664 | * alua_prep_fn - request callback | ||
665 | * | ||
666 | * Fail I/O to all paths not in state | ||
667 | * active/optimized or active/non-optimized. | ||
668 | */ | ||
669 | static int alua_prep_fn(struct scsi_device *sdev, struct request *req) | ||
670 | { | ||
671 | struct alua_dh_data *h = get_alua_data(sdev); | ||
672 | int ret = BLKPREP_OK; | ||
673 | |||
674 | if (h->state != TPGS_STATE_OPTIMIZED && | ||
675 | h->state != TPGS_STATE_NONOPTIMIZED) { | ||
676 | ret = BLKPREP_KILL; | ||
677 | req->cmd_flags |= REQ_QUIET; | ||
678 | } | ||
679 | return ret; | ||
680 | |||
681 | } | ||
682 | |||
683 | const struct scsi_dh_devlist alua_dev_list[] = { | ||
684 | {"HP", "MSA VOLUME" }, | ||
685 | {"HP", "HSV101" }, | ||
686 | {"HP", "HSV111" }, | ||
687 | {"HP", "HSV200" }, | ||
688 | {"HP", "HSV210" }, | ||
689 | {"HP", "HSV300" }, | ||
690 | {"IBM", "2107900" }, | ||
691 | {"IBM", "2145" }, | ||
692 | {"Pillar", "Axiom" }, | ||
693 | {NULL, NULL} | ||
694 | }; | ||
695 | |||
696 | static int alua_bus_attach(struct scsi_device *sdev); | ||
697 | static void alua_bus_detach(struct scsi_device *sdev); | ||
698 | |||
699 | static struct scsi_device_handler alua_dh = { | ||
700 | .name = ALUA_DH_NAME, | ||
701 | .module = THIS_MODULE, | ||
702 | .devlist = alua_dev_list, | ||
703 | .attach = alua_bus_attach, | ||
704 | .detach = alua_bus_detach, | ||
705 | .prep_fn = alua_prep_fn, | ||
706 | .check_sense = alua_check_sense, | ||
707 | .activate = alua_activate, | ||
708 | }; | ||
709 | |||
710 | /* | ||
711 | * alua_bus_attach - Attach device handler | ||
712 | * @sdev: device to be attached to | ||
713 | */ | ||
714 | static int alua_bus_attach(struct scsi_device *sdev) | ||
715 | { | ||
716 | struct scsi_dh_data *scsi_dh_data; | ||
717 | struct alua_dh_data *h; | ||
718 | unsigned long flags; | ||
719 | int err = SCSI_DH_OK; | ||
720 | |||
721 | scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) | ||
722 | + sizeof(*h) , GFP_KERNEL); | ||
723 | if (!scsi_dh_data) { | ||
724 | sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n", | ||
725 | ALUA_DH_NAME); | ||
726 | return -ENOMEM; | ||
727 | } | ||
728 | |||
729 | scsi_dh_data->scsi_dh = &alua_dh; | ||
730 | h = (struct alua_dh_data *) scsi_dh_data->buf; | ||
731 | h->tpgs = TPGS_MODE_UNINITIALIZED; | ||
732 | h->state = TPGS_STATE_OPTIMIZED; | ||
733 | h->group_id = -1; | ||
734 | h->rel_port = -1; | ||
735 | h->buff = h->inq; | ||
736 | h->bufflen = ALUA_INQUIRY_SIZE; | ||
737 | |||
738 | err = alua_initialize(sdev, h); | ||
739 | if (err != SCSI_DH_OK) | ||
740 | goto failed; | ||
741 | |||
742 | if (!try_module_get(THIS_MODULE)) | ||
743 | goto failed; | ||
744 | |||
745 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); | ||
746 | sdev->scsi_dh_data = scsi_dh_data; | ||
747 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); | ||
748 | |||
749 | return 0; | ||
750 | |||
751 | failed: | ||
752 | kfree(scsi_dh_data); | ||
753 | sdev_printk(KERN_ERR, sdev, "%s: not attached\n", ALUA_DH_NAME); | ||
754 | return -EINVAL; | ||
755 | } | ||
756 | |||
757 | /* | ||
758 | * alua_bus_detach - Detach device handler | ||
759 | * @sdev: device to be detached from | ||
760 | */ | ||
761 | static void alua_bus_detach(struct scsi_device *sdev) | ||
762 | { | ||
763 | struct scsi_dh_data *scsi_dh_data; | ||
764 | struct alua_dh_data *h; | ||
765 | unsigned long flags; | ||
766 | |||
767 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); | ||
768 | scsi_dh_data = sdev->scsi_dh_data; | ||
769 | sdev->scsi_dh_data = NULL; | ||
770 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); | ||
771 | |||
772 | h = (struct alua_dh_data *) scsi_dh_data->buf; | ||
773 | if (h->buff && h->inq != h->buff) | ||
774 | kfree(h->buff); | ||
775 | kfree(scsi_dh_data); | ||
776 | module_put(THIS_MODULE); | ||
777 | sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", ALUA_DH_NAME); | ||
778 | } | ||
779 | |||
780 | static int __init alua_init(void) | ||
781 | { | ||
782 | int r; | ||
783 | |||
784 | r = scsi_register_device_handler(&alua_dh); | ||
785 | if (r != 0) | ||
786 | printk(KERN_ERR "%s: Failed to register scsi device handler", | ||
787 | ALUA_DH_NAME); | ||
788 | return r; | ||
789 | } | ||
790 | |||
791 | static void __exit alua_exit(void) | ||
792 | { | ||
793 | scsi_unregister_device_handler(&alua_dh); | ||
794 | } | ||
795 | |||
796 | module_init(alua_init); | ||
797 | module_exit(alua_exit); | ||
798 | |||
799 | MODULE_DESCRIPTION("DM Multipath ALUA support"); | ||
800 | MODULE_AUTHOR("Hannes Reinecke <hare@suse.de>"); | ||
801 | MODULE_LICENSE("GPL"); | ||
802 | MODULE_VERSION(ALUA_DH_VER); | ||