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