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 UNIT_ATTENTION: 50 ret = SCSI_DH_IMM_RETRY; 51 break; 52 case NOT_READY: 53 if (sshdr->asc == 0x04 && sshdr->ascq == 2) { 54 /* 55 * LUN not ready - Initialization command required 56 * 57 * This is the passive path 58 */ 59 h->path_state = HP_SW_PATH_PASSIVE; 60 ret = SCSI_DH_OK; 61 break; 62 } 63 fallthrough; 64 default: 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, 68 sshdr->ascq); 69 break; 70 } 71 return ret; 72 } 73 74 /* 75 * hp_sw_tur - Send TEST UNIT READY 76 * @sdev: sdev command should be sent to 77 * 78 * Use the TEST UNIT READY command to determine 79 * the path state. 80 */ 81 static int hp_sw_tur(struct scsi_device *sdev, struct hp_sw_dh_data *h) 82 { 83 unsigned char cmd[6] = { TEST_UNIT_READY }; 84 struct scsi_sense_hdr sshdr; 85 int ret = SCSI_DH_OK, res; 86 blk_opf_t opf = REQ_OP_DRV_IN | REQ_FAILFAST_DEV | 87 REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER; 88 const struct scsi_exec_args exec_args = { 89 .sshdr = &sshdr, 90 }; 91 92 retry: 93 res = scsi_execute_cmd(sdev, cmd, opf, NULL, 0, HP_SW_TIMEOUT, 94 HP_SW_RETRIES, &exec_args); 95 if (res) { 96 if (scsi_sense_valid(&sshdr)) 97 ret = tur_done(sdev, h, &sshdr); 98 else { 99 sdev_printk(KERN_WARNING, sdev, 100 "%s: sending tur failed with %x\n", 101 HP_SW_NAME, res); 102 ret = SCSI_DH_IO; 103 } 104 } else { 105 h->path_state = HP_SW_PATH_ACTIVE; 106 ret = SCSI_DH_OK; 107 } 108 if (ret == SCSI_DH_IMM_RETRY) 109 goto retry; 110 111 return ret; 112 } 113 114 /* 115 * hp_sw_start_stop - Send START STOP UNIT command 116 * @sdev: sdev command should be sent to 117 * 118 * Sending START STOP UNIT activates the SP. 119 */ 120 static int hp_sw_start_stop(struct hp_sw_dh_data *h) 121 { 122 unsigned char cmd[6] = { START_STOP, 0, 0, 0, 1, 0 }; 123 struct scsi_sense_hdr sshdr; 124 struct scsi_device *sdev = h->sdev; 125 int res, rc = SCSI_DH_OK; 126 int retry_cnt = HP_SW_RETRIES; 127 blk_opf_t opf = REQ_OP_DRV_IN | REQ_FAILFAST_DEV | 128 REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER; 129 const struct scsi_exec_args exec_args = { 130 .sshdr = &sshdr, 131 }; 132 133 retry: 134 res = scsi_execute_cmd(sdev, cmd, opf, NULL, 0, HP_SW_TIMEOUT, 135 HP_SW_RETRIES, &exec_args); 136 if (res) { 137 if (!scsi_sense_valid(&sshdr)) { 138 sdev_printk(KERN_WARNING, sdev, 139 "%s: sending start_stop_unit failed, " 140 "no sense available\n", HP_SW_NAME); 141 return SCSI_DH_IO; 142 } 143 switch (sshdr.sense_key) { 144 case NOT_READY: 145 if (sshdr.asc == 0x04 && sshdr.ascq == 3) { 146 /* 147 * LUN not ready - manual intervention required 148 * 149 * Switch-over in progress, retry. 150 */ 151 if (--retry_cnt) 152 goto retry; 153 rc = SCSI_DH_RETRY; 154 break; 155 } 156 fallthrough; 157 default: 158 sdev_printk(KERN_WARNING, sdev, 159 "%s: sending start_stop_unit failed, " 160 "sense %x/%x/%x\n", HP_SW_NAME, 161 sshdr.sense_key, sshdr.asc, sshdr.ascq); 162 rc = SCSI_DH_IO; 163 } 164 } 165 return rc; 166 } 167 168 static blk_status_t hp_sw_prep_fn(struct scsi_device *sdev, struct request *req) 169 { 170 struct hp_sw_dh_data *h = sdev->handler_data; 171 172 if (h->path_state != HP_SW_PATH_ACTIVE) { 173 req->rq_flags |= RQF_QUIET; 174 return BLK_STS_IOERR; 175 } 176 177 return BLK_STS_OK; 178 } 179 180 /* 181 * hp_sw_activate - Activate a path 182 * @sdev: sdev on the path to be activated 183 * 184 * The HP Active/Passive firmware is pretty simple; 185 * the passive path reports NOT READY with sense codes 186 * 0x04/0x02; a START STOP UNIT command will then 187 * activate the passive path (and deactivate the 188 * previously active one). 189 */ 190 static int hp_sw_activate(struct scsi_device *sdev, 191 activate_complete fn, void *data) 192 { 193 int ret = SCSI_DH_OK; 194 struct hp_sw_dh_data *h = sdev->handler_data; 195 196 ret = hp_sw_tur(sdev, h); 197 198 if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE) 199 ret = hp_sw_start_stop(h); 200 201 if (fn) 202 fn(data, ret); 203 return 0; 204 } 205 206 static int hp_sw_bus_attach(struct scsi_device *sdev) 207 { 208 struct hp_sw_dh_data *h; 209 int ret; 210 211 h = kzalloc(sizeof(*h), GFP_KERNEL); 212 if (!h) 213 return SCSI_DH_NOMEM; 214 h->path_state = HP_SW_PATH_UNINITIALIZED; 215 h->retries = HP_SW_RETRIES; 216 h->sdev = sdev; 217 218 ret = hp_sw_tur(sdev, h); 219 if (ret != SCSI_DH_OK) 220 goto failed; 221 if (h->path_state == HP_SW_PATH_UNINITIALIZED) { 222 ret = SCSI_DH_NOSYS; 223 goto failed; 224 } 225 226 sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n", 227 HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE? 228 "active":"passive"); 229 230 sdev->handler_data = h; 231 return SCSI_DH_OK; 232 failed: 233 kfree(h); 234 return ret; 235 } 236 237 static void hp_sw_bus_detach( struct scsi_device *sdev ) 238 { 239 kfree(sdev->handler_data); 240 sdev->handler_data = NULL; 241 } 242 243 static struct scsi_device_handler hp_sw_dh = { 244 .name = HP_SW_NAME, 245 .module = THIS_MODULE, 246 .attach = hp_sw_bus_attach, 247 .detach = hp_sw_bus_detach, 248 .activate = hp_sw_activate, 249 .prep_fn = hp_sw_prep_fn, 250 }; 251 252 static int __init hp_sw_init(void) 253 { 254 return scsi_register_device_handler(&hp_sw_dh); 255 } 256 257 static void __exit hp_sw_exit(void) 258 { 259 scsi_unregister_device_handler(&hp_sw_dh); 260 } 261 262 module_init(hp_sw_init); 263 module_exit(hp_sw_exit); 264 265 MODULE_DESCRIPTION("HP Active/Passive driver"); 266 MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu"); 267 MODULE_LICENSE("GPL"); 268