Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.0-rc5 274 lines 6.8 kB view raw
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 40struct hp_sw_dh_data { 41 int path_state; 42 int retries; 43 int retry_cnt; 44 struct scsi_device *sdev; 45}; 46 47static 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 */ 56static 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 */ 94static 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 102retry: 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 */ 130static 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 140retry: 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 175static blk_status_t hp_sw_prep_fn(struct scsi_device *sdev, struct request *req) 176{ 177 struct hp_sw_dh_data *h = sdev->handler_data; 178 179 if (h->path_state != HP_SW_PATH_ACTIVE) { 180 req->rq_flags |= RQF_QUIET; 181 return BLK_STS_IOERR; 182 } 183 184 return BLK_STS_OK; 185} 186 187/* 188 * hp_sw_activate - Activate a path 189 * @sdev: sdev on the path to be activated 190 * 191 * The HP Active/Passive firmware is pretty simple; 192 * the passive path reports NOT READY with sense codes 193 * 0x04/0x02; a START STOP UNIT command will then 194 * activate the passive path (and deactivate the 195 * previously active one). 196 */ 197static int hp_sw_activate(struct scsi_device *sdev, 198 activate_complete fn, void *data) 199{ 200 int ret = SCSI_DH_OK; 201 struct hp_sw_dh_data *h = sdev->handler_data; 202 203 ret = hp_sw_tur(sdev, h); 204 205 if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE) 206 ret = hp_sw_start_stop(h); 207 208 if (fn) 209 fn(data, ret); 210 return 0; 211} 212 213static int hp_sw_bus_attach(struct scsi_device *sdev) 214{ 215 struct hp_sw_dh_data *h; 216 int ret; 217 218 h = kzalloc(sizeof(*h), GFP_KERNEL); 219 if (!h) 220 return SCSI_DH_NOMEM; 221 h->path_state = HP_SW_PATH_UNINITIALIZED; 222 h->retries = HP_SW_RETRIES; 223 h->sdev = sdev; 224 225 ret = hp_sw_tur(sdev, h); 226 if (ret != SCSI_DH_OK) 227 goto failed; 228 if (h->path_state == HP_SW_PATH_UNINITIALIZED) { 229 ret = SCSI_DH_NOSYS; 230 goto failed; 231 } 232 233 sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n", 234 HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE? 235 "active":"passive"); 236 237 sdev->handler_data = h; 238 return SCSI_DH_OK; 239failed: 240 kfree(h); 241 return ret; 242} 243 244static void hp_sw_bus_detach( struct scsi_device *sdev ) 245{ 246 kfree(sdev->handler_data); 247 sdev->handler_data = NULL; 248} 249 250static struct scsi_device_handler hp_sw_dh = { 251 .name = HP_SW_NAME, 252 .module = THIS_MODULE, 253 .attach = hp_sw_bus_attach, 254 .detach = hp_sw_bus_detach, 255 .activate = hp_sw_activate, 256 .prep_fn = hp_sw_prep_fn, 257}; 258 259static int __init hp_sw_init(void) 260{ 261 return scsi_register_device_handler(&hp_sw_dh); 262} 263 264static void __exit hp_sw_exit(void) 265{ 266 scsi_unregister_device_handler(&hp_sw_dh); 267} 268 269module_init(hp_sw_init); 270module_exit(hp_sw_exit); 271 272MODULE_DESCRIPTION("HP Active/Passive driver"); 273MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu"); 274MODULE_LICENSE("GPL");