1 /* 2 * Basic HP/COMPAQ MSA 1000 support. This is only needed if your HW cannot be 3 * upgraded. 4 * 5 * Copyright (C) 2006 Red Hat, Inc. All rights reserved. 6 * Copyright (C) 2006 Mike Christie 7 * Copyright (C) 2008 Hannes Reinecke <hare@suse.de> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2, or (at your option) 12 * any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; see the file COPYING. If not, write to 21 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 22 */ 23 24 #include <scsi/scsi.h> 25 #include <scsi/scsi_dbg.h> 26 #include <scsi/scsi_eh.h> 27 #include <scsi/scsi_dh.h> 28 29 #define HP_SW_NAME "hp_sw" 30 31 #define HP_SW_TIMEOUT (60 * HZ) 32 #define HP_SW_RETRIES 3 33 34 #define HP_SW_PATH_UNINITIALIZED -1 35 #define HP_SW_PATH_ACTIVE 0 36 #define HP_SW_PATH_PASSIVE 1 37 38 struct hp_sw_dh_data { 39 unsigned char sense[SCSI_SENSE_BUFFERSIZE]; 40 int path_state; 41 int retries; 42 }; 43 44 static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev) 45 { 46 struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data; 47 BUG_ON(scsi_dh_data == NULL); 48 return ((struct hp_sw_dh_data *) scsi_dh_data->buf); 49 } 50 51 /* 52 * tur_done - Handle TEST UNIT READY return status 53 * @sdev: sdev the command has been sent to 54 * @errors: blk error code 55 * 56 * Returns SCSI_DH_DEV_OFFLINED if the sdev is on the passive path 57 */ 58 static int tur_done(struct scsi_device *sdev, unsigned char *sense) 59 { 60 struct scsi_sense_hdr sshdr; 61 int ret; 62 63 ret = scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr); 64 if (!ret) { 65 sdev_printk(KERN_WARNING, sdev, 66 "%s: sending tur failed, no sense available\n", 67 HP_SW_NAME); 68 ret = SCSI_DH_IO; 69 goto done; 70 } 71 switch (sshdr.sense_key) { 72 case UNIT_ATTENTION: 73 ret = SCSI_DH_IMM_RETRY; 74 break; 75 case NOT_READY: 76 if ((sshdr.asc == 0x04) && (sshdr.ascq == 2)) { 77 /* 78 * LUN not ready - Initialization command required 79 * 80 * This is the passive path 81 */ 82 ret = SCSI_DH_DEV_OFFLINED; 83 break; 84 } 85 /* Fallthrough */ 86 default: 87 sdev_printk(KERN_WARNING, sdev, 88 "%s: sending tur failed, sense %x/%x/%x\n", 89 HP_SW_NAME, sshdr.sense_key, sshdr.asc, 90 sshdr.ascq); 91 break; 92 } 93 94 done: 95 return ret; 96 } 97 98 /* 99 * hp_sw_tur - Send TEST UNIT READY 100 * @sdev: sdev command should be sent to 101 * 102 * Use the TEST UNIT READY command to determine 103 * the path state. 104 */ 105 static int hp_sw_tur(struct scsi_device *sdev, struct hp_sw_dh_data *h) 106 { 107 struct request *req; 108 int ret; 109 110 retry: 111 req = blk_get_request(sdev->request_queue, WRITE, GFP_NOIO); 112 if (!req) 113 return SCSI_DH_RES_TEMP_UNAVAIL; 114 115 req->cmd_type = REQ_TYPE_BLOCK_PC; 116 req->cmd_flags |= REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | 117 REQ_FAILFAST_DRIVER; 118 req->cmd_len = COMMAND_SIZE(TEST_UNIT_READY); 119 req->cmd[0] = TEST_UNIT_READY; 120 req->timeout = HP_SW_TIMEOUT; 121 req->sense = h->sense; 122 memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE); 123 req->sense_len = 0; 124 125 ret = blk_execute_rq(req->q, NULL, req, 1); 126 if (ret == -EIO) { 127 if (req->sense_len > 0) { 128 ret = tur_done(sdev, h->sense); 129 } else { 130 sdev_printk(KERN_WARNING, sdev, 131 "%s: sending tur failed with %x\n", 132 HP_SW_NAME, req->errors); 133 ret = SCSI_DH_IO; 134 } 135 } else { 136 h->path_state = HP_SW_PATH_ACTIVE; 137 ret = SCSI_DH_OK; 138 } 139 if (ret == SCSI_DH_IMM_RETRY) { 140 blk_put_request(req); 141 goto retry; 142 } 143 if (ret == SCSI_DH_DEV_OFFLINED) { 144 h->path_state = HP_SW_PATH_PASSIVE; 145 ret = SCSI_DH_OK; 146 } 147 148 blk_put_request(req); 149 150 return ret; 151 } 152 153 /* 154 * start_done - Handle START STOP UNIT return status 155 * @sdev: sdev the command has been sent to 156 * @errors: blk error code 157 */ 158 static int start_done(struct scsi_device *sdev, unsigned char *sense) 159 { 160 struct scsi_sense_hdr sshdr; 161 int rc; 162 163 rc = scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr); 164 if (!rc) { 165 sdev_printk(KERN_WARNING, sdev, 166 "%s: sending start_stop_unit failed, " 167 "no sense available\n", 168 HP_SW_NAME); 169 return SCSI_DH_IO; 170 } 171 switch (sshdr.sense_key) { 172 case NOT_READY: 173 if ((sshdr.asc == 0x04) && (sshdr.ascq == 3)) { 174 /* 175 * LUN not ready - manual intervention required 176 * 177 * Switch-over in progress, retry. 178 */ 179 rc = SCSI_DH_RETRY; 180 break; 181 } 182 /* fall through */ 183 default: 184 sdev_printk(KERN_WARNING, sdev, 185 "%s: sending start_stop_unit failed, sense %x/%x/%x\n", 186 HP_SW_NAME, sshdr.sense_key, sshdr.asc, 187 sshdr.ascq); 188 rc = SCSI_DH_IO; 189 } 190 191 return rc; 192 } 193 194 /* 195 * hp_sw_start_stop - Send START STOP UNIT command 196 * @sdev: sdev command should be sent to 197 * 198 * Sending START STOP UNIT activates the SP. 199 */ 200 static int hp_sw_start_stop(struct scsi_device *sdev, struct hp_sw_dh_data *h) 201 { 202 struct request *req; 203 int ret, retry; 204 205 retry: 206 req = blk_get_request(sdev->request_queue, WRITE, GFP_NOIO); 207 if (!req) 208 return SCSI_DH_RES_TEMP_UNAVAIL; 209 210 req->cmd_type = REQ_TYPE_BLOCK_PC; 211 req->cmd_flags |= REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | 212 REQ_FAILFAST_DRIVER; 213 req->cmd_len = COMMAND_SIZE(START_STOP); 214 req->cmd[0] = START_STOP; 215 req->cmd[4] = 1; /* Start spin cycle */ 216 req->timeout = HP_SW_TIMEOUT; 217 req->sense = h->sense; 218 memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE); 219 req->sense_len = 0; 220 retry = h->retries; 221 222 ret = blk_execute_rq(req->q, NULL, req, 1); 223 if (ret == -EIO) { 224 if (req->sense_len > 0) { 225 ret = start_done(sdev, h->sense); 226 } else { 227 sdev_printk(KERN_WARNING, sdev, 228 "%s: sending start_stop_unit failed with %x\n", 229 HP_SW_NAME, req->errors); 230 ret = SCSI_DH_IO; 231 } 232 } else 233 ret = SCSI_DH_OK; 234 235 if (ret == SCSI_DH_RETRY) { 236 if (--retry) { 237 blk_put_request(req); 238 goto retry; 239 } 240 ret = SCSI_DH_IO; 241 } 242 243 blk_put_request(req); 244 245 return ret; 246 } 247 248 static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req) 249 { 250 struct hp_sw_dh_data *h = get_hp_sw_data(sdev); 251 int ret = BLKPREP_OK; 252 253 if (h->path_state != HP_SW_PATH_ACTIVE) { 254 ret = BLKPREP_KILL; 255 req->cmd_flags |= REQ_QUIET; 256 } 257 return ret; 258 259 } 260 261 /* 262 * hp_sw_activate - Activate a path 263 * @sdev: sdev on the path to be activated 264 * 265 * The HP Active/Passive firmware is pretty simple; 266 * the passive path reports NOT READY with sense codes 267 * 0x04/0x02; a START STOP UNIT command will then 268 * activate the passive path (and deactivate the 269 * previously active one). 270 */ 271 static int hp_sw_activate(struct scsi_device *sdev) 272 { 273 int ret = SCSI_DH_OK; 274 struct hp_sw_dh_data *h = get_hp_sw_data(sdev); 275 276 ret = hp_sw_tur(sdev, h); 277 278 if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE) { 279 ret = hp_sw_start_stop(sdev, h); 280 if (ret == SCSI_DH_OK) 281 sdev_printk(KERN_INFO, sdev, 282 "%s: activated path\n", 283 HP_SW_NAME); 284 } 285 286 return ret; 287 } 288 289 static const struct scsi_dh_devlist hp_sw_dh_data_list[] = { 290 {"COMPAQ", "MSA1000 VOLUME"}, 291 {"COMPAQ", "HSV110"}, 292 {"HP", "HSV100"}, 293 {"DEC", "HSG80"}, 294 {NULL, NULL}, 295 }; 296 297 static int hp_sw_bus_attach(struct scsi_device *sdev); 298 static void hp_sw_bus_detach(struct scsi_device *sdev); 299 300 static struct scsi_device_handler hp_sw_dh = { 301 .name = HP_SW_NAME, 302 .module = THIS_MODULE, 303 .devlist = hp_sw_dh_data_list, 304 .attach = hp_sw_bus_attach, 305 .detach = hp_sw_bus_detach, 306 .activate = hp_sw_activate, 307 .prep_fn = hp_sw_prep_fn, 308 }; 309 310 static int hp_sw_bus_attach(struct scsi_device *sdev) 311 { 312 struct scsi_dh_data *scsi_dh_data; 313 struct hp_sw_dh_data *h; 314 unsigned long flags; 315 int ret; 316 317 scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) 318 + sizeof(struct hp_sw_dh_data) , GFP_KERNEL); 319 if (!scsi_dh_data) { 320 sdev_printk(KERN_ERR, sdev, "%s: Attach Failed\n", 321 HP_SW_NAME); 322 return 0; 323 } 324 325 scsi_dh_data->scsi_dh = &hp_sw_dh; 326 h = (struct hp_sw_dh_data *) scsi_dh_data->buf; 327 h->path_state = HP_SW_PATH_UNINITIALIZED; 328 h->retries = HP_SW_RETRIES; 329 330 ret = hp_sw_tur(sdev, h); 331 if (ret != SCSI_DH_OK || h->path_state == HP_SW_PATH_UNINITIALIZED) 332 goto failed; 333 334 if (!try_module_get(THIS_MODULE)) 335 goto failed; 336 337 spin_lock_irqsave(sdev->request_queue->queue_lock, flags); 338 sdev->scsi_dh_data = scsi_dh_data; 339 spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); 340 341 sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n", 342 HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE? 343 "active":"passive"); 344 345 return 0; 346 347 failed: 348 kfree(scsi_dh_data); 349 sdev_printk(KERN_ERR, sdev, "%s: not attached\n", 350 HP_SW_NAME); 351 return -EINVAL; 352 } 353 354 static void hp_sw_bus_detach( struct scsi_device *sdev ) 355 { 356 struct scsi_dh_data *scsi_dh_data; 357 unsigned long flags; 358 359 spin_lock_irqsave(sdev->request_queue->queue_lock, flags); 360 scsi_dh_data = sdev->scsi_dh_data; 361 sdev->scsi_dh_data = NULL; 362 spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); 363 module_put(THIS_MODULE); 364 365 sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", HP_SW_NAME); 366 367 kfree(scsi_dh_data); 368 } 369 370 static int __init hp_sw_init(void) 371 { 372 return scsi_register_device_handler(&hp_sw_dh); 373 } 374 375 static void __exit hp_sw_exit(void) 376 { 377 scsi_unregister_device_handler(&hp_sw_dh); 378 } 379 380 module_init(hp_sw_init); 381 module_exit(hp_sw_exit); 382 383 MODULE_DESCRIPTION("HP Active/Passive driver"); 384 MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu"); 385 MODULE_LICENSE("GPL"); 386