1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Basic HP/COMPAQ MSA 1000 support. This is only needed if your HW cannot be
6 * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
7 * Copyright (C) 2006 Mike Christie
8 * Copyright (C) 2008 Hannes Reinecke <hare@suse.de>
11 #include <linux/slab.h>
12 #include <linux/module.h>
13 #include <scsi/scsi.h>
14 #include <scsi/scsi_dbg.h>
15 #include <scsi/scsi_eh.h>
16 #include <scsi/scsi_dh.h>
18 #define HP_SW_NAME "hp_sw"
20 #define HP_SW_TIMEOUT (60 * HZ)
21 #define HP_SW_RETRIES 3
23 #define HP_SW_PATH_UNINITIALIZED -1
24 #define HP_SW_PATH_ACTIVE 0
25 #define HP_SW_PATH_PASSIVE 1
27 struct hp_sw_dh_data
{
31 struct scsi_device
*sdev
;
34 static int hp_sw_start_stop(struct hp_sw_dh_data
*);
37 * tur_done - Handle TEST UNIT READY return status
38 * @sdev: sdev the command has been sent to
39 * @errors: blk error code
41 * Returns SCSI_DH_DEV_OFFLINED if the sdev is on the passive path
43 static int tur_done(struct scsi_device
*sdev
, struct hp_sw_dh_data
*h
,
44 struct scsi_sense_hdr
*sshdr
)
48 switch (sshdr
->sense_key
) {
50 if (sshdr
->asc
== 0x04 && sshdr
->ascq
== 2) {
52 * LUN not ready - Initialization command required
54 * This is the passive path
56 h
->path_state
= HP_SW_PATH_PASSIVE
;
62 sdev_printk(KERN_WARNING
, sdev
,
63 "%s: sending tur failed, sense %x/%x/%x\n",
64 HP_SW_NAME
, sshdr
->sense_key
, sshdr
->asc
,
72 * hp_sw_tur - Send TEST UNIT READY
73 * @sdev: sdev command should be sent to
75 * Use the TEST UNIT READY command to determine
78 static int hp_sw_tur(struct scsi_device
*sdev
, struct hp_sw_dh_data
*h
)
80 unsigned char cmd
[6] = { TEST_UNIT_READY
};
81 struct scsi_sense_hdr sshdr
;
83 blk_opf_t opf
= REQ_OP_DRV_IN
| REQ_FAILFAST_DEV
|
84 REQ_FAILFAST_TRANSPORT
| REQ_FAILFAST_DRIVER
;
85 struct scsi_failure failure_defs
[] = {
87 .sense
= UNIT_ATTENTION
,
88 .asc
= SCMD_FAILURE_ASC_ANY
,
89 .ascq
= SCMD_FAILURE_ASCQ_ANY
,
90 .allowed
= SCMD_FAILURE_NO_LIMIT
,
91 .result
= SAM_STAT_CHECK_CONDITION
,
95 struct scsi_failures failures
= {
96 .failure_definitions
= failure_defs
,
98 const struct scsi_exec_args exec_args
= {
100 .failures
= &failures
,
103 res
= scsi_execute_cmd(sdev
, cmd
, opf
, NULL
, 0, HP_SW_TIMEOUT
,
104 HP_SW_RETRIES
, &exec_args
);
105 if (res
> 0 && scsi_sense_valid(&sshdr
)) {
106 ret
= tur_done(sdev
, h
, &sshdr
);
107 } else if (res
== 0) {
108 h
->path_state
= HP_SW_PATH_ACTIVE
;
111 sdev_printk(KERN_WARNING
, sdev
,
112 "%s: sending tur failed with %x\n",
121 * hp_sw_start_stop - Send START STOP UNIT command
122 * @sdev: sdev command should be sent to
124 * Sending START STOP UNIT activates the SP.
126 static int hp_sw_start_stop(struct hp_sw_dh_data
*h
)
128 unsigned char cmd
[6] = { START_STOP
, 0, 0, 0, 1, 0 };
129 struct scsi_sense_hdr sshdr
;
130 struct scsi_device
*sdev
= h
->sdev
;
132 blk_opf_t opf
= REQ_OP_DRV_IN
| REQ_FAILFAST_DEV
|
133 REQ_FAILFAST_TRANSPORT
| REQ_FAILFAST_DRIVER
;
134 struct scsi_failure failure_defs
[] = {
137 * LUN not ready - manual intervention required
139 * Switch-over in progress, retry.
144 .allowed
= HP_SW_RETRIES
,
145 .result
= SAM_STAT_CHECK_CONDITION
,
149 struct scsi_failures failures
= {
150 .failure_definitions
= failure_defs
,
152 const struct scsi_exec_args exec_args
= {
154 .failures
= &failures
,
157 res
= scsi_execute_cmd(sdev
, cmd
, opf
, NULL
, 0, HP_SW_TIMEOUT
,
158 HP_SW_RETRIES
, &exec_args
);
161 } else if (res
< 0 || !scsi_sense_valid(&sshdr
)) {
162 sdev_printk(KERN_WARNING
, sdev
,
163 "%s: sending start_stop_unit failed, "
164 "no sense available\n", HP_SW_NAME
);
168 switch (sshdr
.sense_key
) {
170 if (sshdr
.asc
== 0x04 && sshdr
.ascq
== 3) {
176 sdev_printk(KERN_WARNING
, sdev
,
177 "%s: sending start_stop_unit failed, "
178 "sense %x/%x/%x\n", HP_SW_NAME
,
179 sshdr
.sense_key
, sshdr
.asc
, sshdr
.ascq
);
186 static blk_status_t
hp_sw_prep_fn(struct scsi_device
*sdev
, struct request
*req
)
188 struct hp_sw_dh_data
*h
= sdev
->handler_data
;
190 if (h
->path_state
!= HP_SW_PATH_ACTIVE
) {
191 req
->rq_flags
|= RQF_QUIET
;
192 return BLK_STS_IOERR
;
199 * hp_sw_activate - Activate a path
200 * @sdev: sdev on the path to be activated
202 * The HP Active/Passive firmware is pretty simple;
203 * the passive path reports NOT READY with sense codes
204 * 0x04/0x02; a START STOP UNIT command will then
205 * activate the passive path (and deactivate the
206 * previously active one).
208 static int hp_sw_activate(struct scsi_device
*sdev
,
209 activate_complete fn
, void *data
)
211 int ret
= SCSI_DH_OK
;
212 struct hp_sw_dh_data
*h
= sdev
->handler_data
;
214 ret
= hp_sw_tur(sdev
, h
);
216 if (ret
== SCSI_DH_OK
&& h
->path_state
== HP_SW_PATH_PASSIVE
)
217 ret
= hp_sw_start_stop(h
);
224 static int hp_sw_bus_attach(struct scsi_device
*sdev
)
226 struct hp_sw_dh_data
*h
;
229 h
= kzalloc(sizeof(*h
), GFP_KERNEL
);
231 return SCSI_DH_NOMEM
;
232 h
->path_state
= HP_SW_PATH_UNINITIALIZED
;
233 h
->retries
= HP_SW_RETRIES
;
236 ret
= hp_sw_tur(sdev
, h
);
237 if (ret
!= SCSI_DH_OK
)
239 if (h
->path_state
== HP_SW_PATH_UNINITIALIZED
) {
244 sdev_printk(KERN_INFO
, sdev
, "%s: attached to %s path\n",
245 HP_SW_NAME
, h
->path_state
== HP_SW_PATH_ACTIVE
?
248 sdev
->handler_data
= h
;
255 static void hp_sw_bus_detach( struct scsi_device
*sdev
)
257 kfree(sdev
->handler_data
);
258 sdev
->handler_data
= NULL
;
261 static struct scsi_device_handler hp_sw_dh
= {
263 .module
= THIS_MODULE
,
264 .attach
= hp_sw_bus_attach
,
265 .detach
= hp_sw_bus_detach
,
266 .activate
= hp_sw_activate
,
267 .prep_fn
= hp_sw_prep_fn
,
270 static int __init
hp_sw_init(void)
272 return scsi_register_device_handler(&hp_sw_dh
);
275 static void __exit
hp_sw_exit(void)
277 scsi_unregister_device_handler(&hp_sw_dh
);
280 module_init(hp_sw_init
);
281 module_exit(hp_sw_exit
);
283 MODULE_DESCRIPTION("HP Active/Passive driver");
284 MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu");
285 MODULE_LICENSE("GPL");