1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Basic HP/COMPAQ MSA 1000 support. This is only needed if your HW cannot be 4 * upgraded. 5 * 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> 9 */ 10 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> 17 18 #define HP_SW_NAME "hp_sw" 19 20 #define HP_SW_TIMEOUT (60 * HZ) 21 #define HP_SW_RETRIES 3 22 23 #define HP_SW_PATH_UNINITIALIZED -1 24 #define HP_SW_PATH_ACTIVE 0 25 #define HP_SW_PATH_PASSIVE 1 26 27 struct hp_sw_dh_data { 28 int path_state; 29 int retries; 30 int retry_cnt; 31 struct scsi_device *sdev; 32 }; 33 34 static int hp_sw_start_stop(struct hp_sw_dh_data *); 35 36 /* 37 * tur_done - Handle TEST UNIT READY return status 38 * @sdev: sdev the command has been sent to 39 * @errors: blk error code 40 * 41 * Returns SCSI_DH_DEV_OFFLINED if the sdev is on the passive path 42 */ 43 static int tur_done(struct scsi_device *sdev, struct hp_sw_dh_data *h, 44 struct scsi_sense_hdr *sshdr) 45 { 46 int ret = SCSI_DH_IO; 47 48 switch (sshdr->sense_key) { 49 case NOT_READY: 50 if (sshdr->asc == 0x04 && sshdr->ascq == 2) { 51 /* 52 * LUN not ready - Initialization command required 53 * 54 * This is the passive path 55 */ 56 h->path_state = HP_SW_PATH_PASSIVE; 57 ret = SCSI_DH_OK; 58 break; 59 } 60 fallthrough; 61 default: 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, 65 sshdr->ascq); 66 break; 67 } 68 return ret; 69 } 70 71 /* 72 * hp_sw_tur - Send TEST UNIT READY 73 * @sdev: sdev command should be sent to 74 * 75 * Use the TEST UNIT READY command to determine 76 * the path state. 77 */ 78 static int hp_sw_tur(struct scsi_device *sdev, struct hp_sw_dh_data *h) 79 { 80 unsigned char cmd[6] = { TEST_UNIT_READY }; 81 struct scsi_sense_hdr sshdr; 82 int ret, res; 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[] = { 86 { 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, 92 }, 93 {} 94 }; 95 struct scsi_failures failures = { 96 .failure_definitions = failure_defs, 97 }; 98 const struct scsi_exec_args exec_args = { 99 .sshdr = &sshdr, 100 .failures = &failures, 101 }; 102 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; 109 ret = SCSI_DH_OK; 110 } else { 111 sdev_printk(KERN_WARNING, sdev, 112 "%s: sending tur failed with %x\n", 113 HP_SW_NAME, res); 114 ret = SCSI_DH_IO; 115 } 116 117 return ret; 118 } 119 120 /* 121 * hp_sw_start_stop - Send START STOP UNIT command 122 * @sdev: sdev command should be sent to 123 * 124 * Sending START STOP UNIT activates the SP. 125 */ 126 static int hp_sw_start_stop(struct hp_sw_dh_data *h) 127 { 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; 131 int res, rc; 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[] = { 135 { 136 /* 137 * LUN not ready - manual intervention required 138 * 139 * Switch-over in progress, retry. 140 */ 141 .sense = NOT_READY, 142 .asc = 0x04, 143 .ascq = 0x03, 144 .allowed = HP_SW_RETRIES, 145 .result = SAM_STAT_CHECK_CONDITION, 146 }, 147 {} 148 }; 149 struct scsi_failures failures = { 150 .failure_definitions = failure_defs, 151 }; 152 const struct scsi_exec_args exec_args = { 153 .sshdr = &sshdr, 154 .failures = &failures, 155 }; 156 157 res = scsi_execute_cmd(sdev, cmd, opf, NULL, 0, HP_SW_TIMEOUT, 158 HP_SW_RETRIES, &exec_args); 159 if (!res) { 160 return SCSI_DH_OK; 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); 165 return SCSI_DH_IO; 166 } 167 168 switch (sshdr.sense_key) { 169 case NOT_READY: 170 if (sshdr.asc == 0x04 && sshdr.ascq == 3) { 171 rc = SCSI_DH_RETRY; 172 break; 173 } 174 fallthrough; 175 default: 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); 180 rc = SCSI_DH_IO; 181 } 182 183 return rc; 184 } 185 186 static blk_status_t hp_sw_prep_fn(struct scsi_device *sdev, struct request *req) 187 { 188 struct hp_sw_dh_data *h = sdev->handler_data; 189 190 if (h->path_state != HP_SW_PATH_ACTIVE) { 191 req->rq_flags |= RQF_QUIET; 192 return BLK_STS_IOERR; 193 } 194 195 return BLK_STS_OK; 196 } 197 198 /* 199 * hp_sw_activate - Activate a path 200 * @sdev: sdev on the path to be activated 201 * 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). 207 */ 208 static int hp_sw_activate(struct scsi_device *sdev, 209 activate_complete fn, void *data) 210 { 211 int ret = SCSI_DH_OK; 212 struct hp_sw_dh_data *h = sdev->handler_data; 213 214 ret = hp_sw_tur(sdev, h); 215 216 if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE) 217 ret = hp_sw_start_stop(h); 218 219 if (fn) 220 fn(data, ret); 221 return 0; 222 } 223 224 static int hp_sw_bus_attach(struct scsi_device *sdev) 225 { 226 struct hp_sw_dh_data *h; 227 int ret; 228 229 h = kzalloc(sizeof(*h), GFP_KERNEL); 230 if (!h) 231 return SCSI_DH_NOMEM; 232 h->path_state = HP_SW_PATH_UNINITIALIZED; 233 h->retries = HP_SW_RETRIES; 234 h->sdev = sdev; 235 236 ret = hp_sw_tur(sdev, h); 237 if (ret != SCSI_DH_OK) 238 goto failed; 239 if (h->path_state == HP_SW_PATH_UNINITIALIZED) { 240 ret = SCSI_DH_NOSYS; 241 goto failed; 242 } 243 244 sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n", 245 HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE? 246 "active":"passive"); 247 248 sdev->handler_data = h; 249 return SCSI_DH_OK; 250 failed: 251 kfree(h); 252 return ret; 253 } 254 255 static void hp_sw_bus_detach( struct scsi_device *sdev ) 256 { 257 kfree(sdev->handler_data); 258 sdev->handler_data = NULL; 259 } 260 261 static struct scsi_device_handler hp_sw_dh = { 262 .name = HP_SW_NAME, 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, 268 }; 269 270 static int __init hp_sw_init(void) 271 { 272 return scsi_register_device_handler(&hp_sw_dh); 273 } 274 275 static void __exit hp_sw_exit(void) 276 { 277 scsi_unregister_device_handler(&hp_sw_dh); 278 } 279 280 module_init(hp_sw_init); 281 module_exit(hp_sw_exit); 282 283 MODULE_DESCRIPTION("HP Active/Passive driver"); 284 MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu"); 285 MODULE_LICENSE("GPL"); 286