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