2 * Target driver for EMC CLARiiON AX/CX-series hardware.
3 * Based on code from Lars Marowsky-Bree <lmb@suse.de>
4 * and Ed Goggin <egoggin@emc.com>.
6 * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
7 * Copyright (C) 2006 Mike Christie
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; see the file COPYING. If not, write to
21 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include <scsi/scsi.h>
24 #include <scsi/scsi_eh.h>
25 #include <scsi/scsi_dh.h>
26 #include <scsi/scsi_device.h>
28 #define CLARIION_NAME "emc_clariion"
30 #define CLARIION_TRESPASS_PAGE 0x22
31 #define CLARIION_BUFFER_SIZE 0x80
32 #define CLARIION_TIMEOUT (60 * HZ)
33 #define CLARIION_RETRIES 3
34 #define CLARIION_UNBOUND_LU -1
36 static unsigned char long_trespass
[] = {
38 CLARIION_TRESPASS_PAGE
, /* Page code */
39 0x09, /* Page length - 2 */
40 0x81, /* Trespass code + Honor reservation bit */
41 0xff, 0xff, /* Trespass target */
42 0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */
45 static unsigned char long_trespass_hr
[] = {
47 CLARIION_TRESPASS_PAGE
, /* Page code */
48 0x09, /* Page length - 2 */
49 0x01, /* Trespass code + Honor reservation bit */
50 0xff, 0xff, /* Trespass target */
51 0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */
54 static unsigned char short_trespass
[] = {
56 CLARIION_TRESPASS_PAGE
, /* Page code */
57 0x02, /* Page length - 2 */
58 0x81, /* Trespass code + Honor reservation bit */
59 0xff, /* Trespass target */
62 static unsigned char short_trespass_hr
[] = {
64 CLARIION_TRESPASS_PAGE
, /* Page code */
65 0x02, /* Page length - 2 */
66 0x01, /* Trespass code + Honor reservation bit */
67 0xff, /* Trespass target */
70 struct clariion_dh_data
{
72 * Use short trespass command (FC-series) or the long version
73 * (default for AX/CX CLARiiON arrays).
75 unsigned short_trespass
;
77 * Whether or not (default) to honor SCSI reservations when
78 * initiating a switch-over.
81 /* I/O buffer for both MODE_SELECT and INQUIRY commands. */
82 char buffer
[CLARIION_BUFFER_SIZE
];
84 * SCSI sense buffer for commands -- assumes serial issuance
85 * and completion sequence of all commands for same multipath.
87 unsigned char sense
[SCSI_SENSE_BUFFERSIZE
];
88 /* which SP (A=0,B=1,UNBOUND=-1) is dflt SP for path's mapped dev */
90 /* which SP (A=0,B=1,UNBOUND=-1) is active for path's mapped dev */
94 static inline struct clariion_dh_data
95 *get_clariion_data(struct scsi_device
*sdev
)
97 struct scsi_dh_data
*scsi_dh_data
= sdev
->scsi_dh_data
;
98 BUG_ON(scsi_dh_data
== NULL
);
99 return ((struct clariion_dh_data
*) scsi_dh_data
->buf
);
103 * Parse MODE_SELECT cmd reply.
105 static int trespass_endio(struct scsi_device
*sdev
, int result
)
107 int err
= SCSI_DH_OK
;
108 struct scsi_sense_hdr sshdr
;
109 struct clariion_dh_data
*csdev
= get_clariion_data(sdev
);
110 char *sense
= csdev
->sense
;
112 if (status_byte(result
) == CHECK_CONDITION
&&
113 scsi_normalize_sense(sense
, SCSI_SENSE_BUFFERSIZE
, &sshdr
)) {
114 sdev_printk(KERN_ERR
, sdev
, "Found valid sense data 0x%2x, "
115 "0x%2x, 0x%2x while sending CLARiiON trespass "
116 "command.\n", sshdr
.sense_key
, sshdr
.asc
,
119 if ((sshdr
.sense_key
== 0x05) && (sshdr
.asc
== 0x04) &&
120 (sshdr
.ascq
== 0x00)) {
122 * Array based copy in progress -- do not send
123 * mode_select or copy will be aborted mid-stream.
125 sdev_printk(KERN_INFO
, sdev
, "Array Based Copy in "
126 "progress while sending CLARiiON trespass "
128 err
= SCSI_DH_DEV_TEMP_BUSY
;
129 } else if ((sshdr
.sense_key
== 0x02) && (sshdr
.asc
== 0x04) &&
130 (sshdr
.ascq
== 0x03)) {
132 * LUN Not Ready - Manual Intervention Required
133 * indicates in-progress ucode upgrade (NDU).
135 sdev_printk(KERN_INFO
, sdev
, "Detected in-progress "
136 "ucode upgrade NDU operation while sending "
137 "CLARiiON trespass command.\n");
138 err
= SCSI_DH_DEV_TEMP_BUSY
;
140 err
= SCSI_DH_DEV_FAILED
;
142 sdev_printk(KERN_ERR
, sdev
, "Error 0x%x while sending "
143 "CLARiiON trespass command.\n", result
);
150 static int parse_sp_info_reply(struct scsi_device
*sdev
, int result
,
151 int *default_sp
, int *current_sp
, int *new_current_sp
)
153 int err
= SCSI_DH_OK
;
154 struct clariion_dh_data
*csdev
= get_clariion_data(sdev
);
157 /* check for in-progress ucode upgrade (NDU) */
158 if (csdev
->buffer
[48] != 0) {
159 sdev_printk(KERN_NOTICE
, sdev
, "Detected in-progress "
160 "ucode upgrade NDU operation while finding "
161 "current active SP.");
162 err
= SCSI_DH_DEV_TEMP_BUSY
;
164 *default_sp
= csdev
->buffer
[5];
166 if (csdev
->buffer
[4] == 2)
167 /* SP for path is current */
168 *current_sp
= csdev
->buffer
[8];
170 if (csdev
->buffer
[4] == 1)
171 /* SP for this path is NOT current */
172 if (csdev
->buffer
[8] == 0)
177 /* unbound LU or LUNZ */
178 *current_sp
= CLARIION_UNBOUND_LU
;
180 *new_current_sp
= csdev
->buffer
[8];
183 struct scsi_sense_hdr sshdr
;
187 if (scsi_normalize_sense(csdev
->sense
, SCSI_SENSE_BUFFERSIZE
,
189 sdev_printk(KERN_ERR
, sdev
, "Found valid sense data "
190 "0x%2x, 0x%2x, 0x%2x while finding current "
191 "active SP.", sshdr
.sense_key
, sshdr
.asc
,
194 sdev_printk(KERN_ERR
, sdev
, "Error 0x%x finding "
195 "current active SP.", result
);
201 static int sp_info_endio(struct scsi_device
*sdev
, int result
,
202 int mode_select_sent
, int *done
)
204 struct clariion_dh_data
*csdev
= get_clariion_data(sdev
);
205 int err_flags
, default_sp
, current_sp
, new_current_sp
;
207 err_flags
= parse_sp_info_reply(sdev
, result
, &default_sp
,
208 ¤t_sp
, &new_current_sp
);
210 if (err_flags
!= SCSI_DH_OK
)
213 if (mode_select_sent
) {
214 csdev
->default_sp
= default_sp
;
215 csdev
->current_sp
= current_sp
;
218 * Issue the actual module_selec request IFF either
219 * (1) we do not know the identity of the current SP OR
220 * (2) what we think we know is actually correct.
222 if ((current_sp
!= CLARIION_UNBOUND_LU
) &&
223 (new_current_sp
!= current_sp
)) {
225 csdev
->default_sp
= default_sp
;
226 csdev
->current_sp
= current_sp
;
228 sdev_printk(KERN_INFO
, sdev
, "Ignoring path group "
229 "switch-over command for CLARiiON SP%s since "
230 " mapped device is already initialized.",
231 current_sp
? "B" : "A");
233 *done
= 1; /* as good as doing it */
241 * Get block request for REQ_BLOCK_PC command issued to path. Currently
242 * limited to MODE_SELECT (trespass) and INQUIRY (VPD page 0xC0) commands.
244 * Uses data and sense buffers in hardware handler context structure and
245 * assumes serial servicing of commands, both issuance and completion.
247 static struct request
*get_req(struct scsi_device
*sdev
, int cmd
)
249 struct clariion_dh_data
*csdev
= get_clariion_data(sdev
);
251 unsigned char *page22
;
254 rq
= blk_get_request(sdev
->request_queue
,
255 (cmd
== MODE_SELECT
) ? WRITE
: READ
, GFP_ATOMIC
);
257 sdev_printk(KERN_INFO
, sdev
, "get_req: blk_get_request failed");
261 memset(&rq
->cmd
, 0, BLK_MAX_CDB
);
263 rq
->cmd_len
= COMMAND_SIZE(rq
->cmd
[0]);
267 if (csdev
->short_trespass
) {
268 page22
= csdev
->hr
? short_trespass_hr
: short_trespass
;
269 len
= sizeof(short_trespass
);
271 page22
= csdev
->hr
? long_trespass_hr
: long_trespass
;
272 len
= sizeof(long_trespass
);
275 * Can't DMA from kernel BSS -- must copy selected trespass
276 * command mode page contents to context buffer which is
277 * allocated by kmalloc.
279 BUG_ON((len
> CLARIION_BUFFER_SIZE
));
280 memcpy(csdev
->buffer
, page22
, len
);
281 rq
->cmd_flags
|= REQ_RW
;
287 len
= CLARIION_BUFFER_SIZE
;
288 memset(csdev
->buffer
, 0, CLARIION_BUFFER_SIZE
);
296 rq
->cmd_type
= REQ_TYPE_BLOCK_PC
;
297 rq
->cmd_flags
|= REQ_FAILFAST
;
298 rq
->timeout
= CLARIION_TIMEOUT
;
299 rq
->retries
= CLARIION_RETRIES
;
301 rq
->sense
= csdev
->sense
;
302 memset(rq
->sense
, 0, SCSI_SENSE_BUFFERSIZE
);
305 if (blk_rq_map_kern(sdev
->request_queue
, rq
, csdev
->buffer
,
307 __blk_put_request(rq
->q
, rq
);
314 static int send_cmd(struct scsi_device
*sdev
, int cmd
)
316 struct request
*rq
= get_req(sdev
, cmd
);
319 return SCSI_DH_RES_TEMP_UNAVAIL
;
321 return blk_execute_rq(sdev
->request_queue
, NULL
, rq
, 1);
324 static int clariion_activate(struct scsi_device
*sdev
)
326 int result
, done
= 0;
328 result
= send_cmd(sdev
, INQUIRY
);
329 result
= sp_info_endio(sdev
, result
, 0, &done
);
333 result
= send_cmd(sdev
, MODE_SELECT
);
334 result
= trespass_endio(sdev
, result
);
338 result
= send_cmd(sdev
, INQUIRY
);
339 result
= sp_info_endio(sdev
, result
, 1, NULL
);
344 static int clariion_check_sense(struct scsi_device
*sdev
,
345 struct scsi_sense_hdr
*sense_hdr
)
347 switch (sense_hdr
->sense_key
) {
349 if (sense_hdr
->asc
== 0x04 && sense_hdr
->ascq
== 0x03)
351 * LUN Not Ready - Manual Intervention Required
352 * indicates this is a passive path.
354 * FIXME: However, if this is seen and EVPD C0
355 * indicates that this is due to a NDU in
356 * progress, we should set FAIL_PATH too.
357 * This indicates we might have to do a SCSI
358 * inquiry in the end_io path. Ugh.
360 * Can return FAILED only when we want the error
361 * recovery process to kick in.
365 case ILLEGAL_REQUEST
:
366 if (sense_hdr
->asc
== 0x25 && sense_hdr
->ascq
== 0x01)
368 * An array based copy is in progress. Do not
369 * fail the path, do not bypass to another PG,
370 * do not retry. Fail the IO immediately.
371 * (Actually this is the same conclusion as in
372 * the default handler, but lets make sure.)
374 * Can return FAILED only when we want the error
375 * recovery process to kick in.
380 if (sense_hdr
->asc
== 0x29 && sense_hdr
->ascq
== 0x00)
382 * Unit Attention Code. This is the first IO
383 * to the new path, so just retry.
389 /* success just means we do not care what scsi-ml does */
393 const struct scsi_dh_devlist clariion_dev_list
[] = {
399 static int clariion_bus_attach(struct scsi_device
*sdev
);
400 static void clariion_bus_detach(struct scsi_device
*sdev
);
402 static struct scsi_device_handler clariion_dh
= {
403 .name
= CLARIION_NAME
,
404 .module
= THIS_MODULE
,
405 .devlist
= clariion_dev_list
,
406 .attach
= clariion_bus_attach
,
407 .detach
= clariion_bus_detach
,
408 .check_sense
= clariion_check_sense
,
409 .activate
= clariion_activate
,
413 * TODO: need some interface so we can set trespass values
415 static int clariion_bus_attach(struct scsi_device
*sdev
)
417 struct scsi_dh_data
*scsi_dh_data
;
418 struct clariion_dh_data
*h
;
421 scsi_dh_data
= kzalloc(sizeof(struct scsi_device_handler
*)
422 + sizeof(*h
) , GFP_KERNEL
);
424 sdev_printk(KERN_ERR
, sdev
, "Attach failed %s.\n",
429 scsi_dh_data
->scsi_dh
= &clariion_dh
;
430 h
= (struct clariion_dh_data
*) scsi_dh_data
->buf
;
431 h
->default_sp
= CLARIION_UNBOUND_LU
;
432 h
->current_sp
= CLARIION_UNBOUND_LU
;
434 spin_lock_irqsave(sdev
->request_queue
->queue_lock
, flags
);
435 sdev
->scsi_dh_data
= scsi_dh_data
;
436 spin_unlock_irqrestore(sdev
->request_queue
->queue_lock
, flags
);
438 sdev_printk(KERN_NOTICE
, sdev
, "Attached %s.\n", CLARIION_NAME
);
439 try_module_get(THIS_MODULE
);
444 static void clariion_bus_detach(struct scsi_device
*sdev
)
446 struct scsi_dh_data
*scsi_dh_data
;
449 spin_lock_irqsave(sdev
->request_queue
->queue_lock
, flags
);
450 scsi_dh_data
= sdev
->scsi_dh_data
;
451 sdev
->scsi_dh_data
= NULL
;
452 spin_unlock_irqrestore(sdev
->request_queue
->queue_lock
, flags
);
454 sdev_printk(KERN_NOTICE
, sdev
, "Detached %s.\n",
458 module_put(THIS_MODULE
);
461 static int __init
clariion_init(void)
465 r
= scsi_register_device_handler(&clariion_dh
);
467 printk(KERN_ERR
"Failed to register scsi device handler.");
471 static void __exit
clariion_exit(void)
473 scsi_unregister_device_handler(&clariion_dh
);
476 module_init(clariion_init
);
477 module_exit(clariion_exit
);
479 MODULE_DESCRIPTION("EMC CX/AX/FC-family driver");
480 MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, Chandra Seetharaman <sekharan@us.ibm.com>");
481 MODULE_LICENSE("GPL");