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 ret
= SCSI_DH_IMM_RETRY
;
53 if (sshdr
->asc
== 0x04 && sshdr
->ascq
== 2) {
55 * LUN not ready - Initialization command required
57 * This is the passive path
59 h
->path_state
= HP_SW_PATH_PASSIVE
;
65 sdev_printk(KERN_WARNING
, sdev
,
66 "%s: sending tur failed, sense %x/%x/%x\n",
67 HP_SW_NAME
, sshdr
->sense_key
, sshdr
->asc
,
75 * hp_sw_tur - Send TEST UNIT READY
76 * @sdev: sdev command should be sent to
78 * Use the TEST UNIT READY command to determine
81 static int hp_sw_tur(struct scsi_device
*sdev
, struct hp_sw_dh_data
*h
)
83 unsigned char cmd
[6] = { TEST_UNIT_READY
};
84 struct scsi_sense_hdr sshdr
;
85 int ret
= SCSI_DH_OK
, res
;
86 u64 req_flags
= REQ_FAILFAST_DEV
| REQ_FAILFAST_TRANSPORT
|
90 res
= scsi_execute(sdev
, cmd
, DMA_NONE
, NULL
, 0, NULL
, &sshdr
,
91 HP_SW_TIMEOUT
, HP_SW_RETRIES
, req_flags
, 0, NULL
);
93 if (scsi_sense_valid(&sshdr
))
94 ret
= tur_done(sdev
, h
, &sshdr
);
96 sdev_printk(KERN_WARNING
, sdev
,
97 "%s: sending tur failed with %x\n",
102 h
->path_state
= HP_SW_PATH_ACTIVE
;
105 if (ret
== SCSI_DH_IMM_RETRY
)
112 * hp_sw_start_stop - Send START STOP UNIT command
113 * @sdev: sdev command should be sent to
115 * Sending START STOP UNIT activates the SP.
117 static int hp_sw_start_stop(struct hp_sw_dh_data
*h
)
119 unsigned char cmd
[6] = { START_STOP
, 0, 0, 0, 1, 0 };
120 struct scsi_sense_hdr sshdr
;
121 struct scsi_device
*sdev
= h
->sdev
;
122 int res
, rc
= SCSI_DH_OK
;
123 int retry_cnt
= HP_SW_RETRIES
;
124 u64 req_flags
= REQ_FAILFAST_DEV
| REQ_FAILFAST_TRANSPORT
|
128 res
= scsi_execute(sdev
, cmd
, DMA_NONE
, NULL
, 0, NULL
, &sshdr
,
129 HP_SW_TIMEOUT
, HP_SW_RETRIES
, req_flags
, 0, NULL
);
131 if (!scsi_sense_valid(&sshdr
)) {
132 sdev_printk(KERN_WARNING
, sdev
,
133 "%s: sending start_stop_unit failed, "
134 "no sense available\n", HP_SW_NAME
);
137 switch (sshdr
.sense_key
) {
139 if (sshdr
.asc
== 0x04 && sshdr
.ascq
== 3) {
141 * LUN not ready - manual intervention required
143 * Switch-over in progress, retry.
152 sdev_printk(KERN_WARNING
, sdev
,
153 "%s: sending start_stop_unit failed, "
154 "sense %x/%x/%x\n", HP_SW_NAME
,
155 sshdr
.sense_key
, sshdr
.asc
, sshdr
.ascq
);
162 static blk_status_t
hp_sw_prep_fn(struct scsi_device
*sdev
, struct request
*req
)
164 struct hp_sw_dh_data
*h
= sdev
->handler_data
;
166 if (h
->path_state
!= HP_SW_PATH_ACTIVE
) {
167 req
->rq_flags
|= RQF_QUIET
;
168 return BLK_STS_IOERR
;
175 * hp_sw_activate - Activate a path
176 * @sdev: sdev on the path to be activated
178 * The HP Active/Passive firmware is pretty simple;
179 * the passive path reports NOT READY with sense codes
180 * 0x04/0x02; a START STOP UNIT command will then
181 * activate the passive path (and deactivate the
182 * previously active one).
184 static int hp_sw_activate(struct scsi_device
*sdev
,
185 activate_complete fn
, void *data
)
187 int ret
= SCSI_DH_OK
;
188 struct hp_sw_dh_data
*h
= sdev
->handler_data
;
190 ret
= hp_sw_tur(sdev
, h
);
192 if (ret
== SCSI_DH_OK
&& h
->path_state
== HP_SW_PATH_PASSIVE
)
193 ret
= hp_sw_start_stop(h
);
200 static int hp_sw_bus_attach(struct scsi_device
*sdev
)
202 struct hp_sw_dh_data
*h
;
205 h
= kzalloc(sizeof(*h
), GFP_KERNEL
);
207 return SCSI_DH_NOMEM
;
208 h
->path_state
= HP_SW_PATH_UNINITIALIZED
;
209 h
->retries
= HP_SW_RETRIES
;
212 ret
= hp_sw_tur(sdev
, h
);
213 if (ret
!= SCSI_DH_OK
)
215 if (h
->path_state
== HP_SW_PATH_UNINITIALIZED
) {
220 sdev_printk(KERN_INFO
, sdev
, "%s: attached to %s path\n",
221 HP_SW_NAME
, h
->path_state
== HP_SW_PATH_ACTIVE
?
224 sdev
->handler_data
= h
;
231 static void hp_sw_bus_detach( struct scsi_device
*sdev
)
233 kfree(sdev
->handler_data
);
234 sdev
->handler_data
= NULL
;
237 static struct scsi_device_handler hp_sw_dh
= {
239 .module
= THIS_MODULE
,
240 .attach
= hp_sw_bus_attach
,
241 .detach
= hp_sw_bus_detach
,
242 .activate
= hp_sw_activate
,
243 .prep_fn
= hp_sw_prep_fn
,
246 static int __init
hp_sw_init(void)
248 return scsi_register_device_handler(&hp_sw_dh
);
251 static void __exit
hp_sw_exit(void)
253 scsi_unregister_device_handler(&hp_sw_dh
);
256 module_init(hp_sw_init
);
257 module_exit(hp_sw_exit
);
259 MODULE_DESCRIPTION("HP Active/Passive driver");
260 MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu");
261 MODULE_LICENSE("GPL");