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 req_flags = REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | 87 REQ_FAILFAST_DRIVER; 88 89 retry: 90 res = scsi_execute(sdev, cmd, DMA_NONE, NULL, 0, NULL, &sshdr, 91 HP_SW_TIMEOUT, HP_SW_RETRIES, req_flags, 0, NULL); 92 if (res) { 93 if (scsi_sense_valid(&sshdr)) 94 ret = tur_done(sdev, h, &sshdr); 95 else { 96 sdev_printk(KERN_WARNING, sdev, 97 "%s: sending tur failed with %x\n", 98 HP_SW_NAME, res); 99 ret = SCSI_DH_IO; 100 } 101 } else { 102 h->path_state = HP_SW_PATH_ACTIVE; 103 ret = SCSI_DH_OK; 104 } 105 if (ret == SCSI_DH_IMM_RETRY) 106 goto retry; 107 108 return ret; 109 } 110 111 /* 112 * hp_sw_start_stop - Send START STOP UNIT command 113 * @sdev: sdev command should be sent to 114 * 115 * Sending START STOP UNIT activates the SP. 116 */ 117 static int hp_sw_start_stop(struct hp_sw_dh_data *h) 118 { 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 blk_opf_t req_flags = REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | 125 REQ_FAILFAST_DRIVER; 126 127 retry: 128 res = scsi_execute(sdev, cmd, DMA_NONE, NULL, 0, NULL, &sshdr, 129 HP_SW_TIMEOUT, HP_SW_RETRIES, req_flags, 0, NULL); 130 if (res) { 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); 135 return SCSI_DH_IO; 136 } 137 switch (sshdr.sense_key) { 138 case NOT_READY: 139 if (sshdr.asc == 0x04 && sshdr.ascq == 3) { 140 /* 141 * LUN not ready - manual intervention required 142 * 143 * Switch-over in progress, retry. 144 */ 145 if (--retry_cnt) 146 goto retry; 147 rc = SCSI_DH_RETRY; 148 break; 149 } 150 fallthrough; 151 default: 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); 156 rc = SCSI_DH_IO; 157 } 158 } 159 return rc; 160 } 161 162 static blk_status_t hp_sw_prep_fn(struct scsi_device *sdev, struct request *req) 163 { 164 struct hp_sw_dh_data *h = sdev->handler_data; 165 166 if (h->path_state != HP_SW_PATH_ACTIVE) { 167 req->rq_flags |= RQF_QUIET; 168 return BLK_STS_IOERR; 169 } 170 171 return BLK_STS_OK; 172 } 173 174 /* 175 * hp_sw_activate - Activate a path 176 * @sdev: sdev on the path to be activated 177 * 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). 183 */ 184 static int hp_sw_activate(struct scsi_device *sdev, 185 activate_complete fn, void *data) 186 { 187 int ret = SCSI_DH_OK; 188 struct hp_sw_dh_data *h = sdev->handler_data; 189 190 ret = hp_sw_tur(sdev, h); 191 192 if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE) 193 ret = hp_sw_start_stop(h); 194 195 if (fn) 196 fn(data, ret); 197 return 0; 198 } 199 200 static int hp_sw_bus_attach(struct scsi_device *sdev) 201 { 202 struct hp_sw_dh_data *h; 203 int ret; 204 205 h = kzalloc(sizeof(*h), GFP_KERNEL); 206 if (!h) 207 return SCSI_DH_NOMEM; 208 h->path_state = HP_SW_PATH_UNINITIALIZED; 209 h->retries = HP_SW_RETRIES; 210 h->sdev = sdev; 211 212 ret = hp_sw_tur(sdev, h); 213 if (ret != SCSI_DH_OK) 214 goto failed; 215 if (h->path_state == HP_SW_PATH_UNINITIALIZED) { 216 ret = SCSI_DH_NOSYS; 217 goto failed; 218 } 219 220 sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n", 221 HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE? 222 "active":"passive"); 223 224 sdev->handler_data = h; 225 return SCSI_DH_OK; 226 failed: 227 kfree(h); 228 return ret; 229 } 230 231 static void hp_sw_bus_detach( struct scsi_device *sdev ) 232 { 233 kfree(sdev->handler_data); 234 sdev->handler_data = NULL; 235 } 236 237 static struct scsi_device_handler hp_sw_dh = { 238 .name = HP_SW_NAME, 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, 244 }; 245 246 static int __init hp_sw_init(void) 247 { 248 return scsi_register_device_handler(&hp_sw_dh); 249 } 250 251 static void __exit hp_sw_exit(void) 252 { 253 scsi_unregister_device_handler(&hp_sw_dh); 254 } 255 256 module_init(hp_sw_init); 257 module_exit(hp_sw_exit); 258 259 MODULE_DESCRIPTION("HP Active/Passive driver"); 260 MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu"); 261 MODULE_LICENSE("GPL"); 262