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