aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/device_handler/scsi_dh_hp_sw.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/device_handler/scsi_dh_hp_sw.c')
-rw-r--r--drivers/scsi/device_handler/scsi_dh_hp_sw.c272
1 files changed, 234 insertions, 38 deletions
diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c
index 78259bc5dfc..9c7a1f8ebb7 100644
--- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c
+++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c
@@ -4,6 +4,7 @@
4 * 4 *
5 * Copyright (C) 2006 Red Hat, Inc. All rights reserved. 5 * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
6 * Copyright (C) 2006 Mike Christie 6 * Copyright (C) 2006 Mike Christie
7 * Copyright (C) 2008 Hannes Reinecke <hare@suse.de>
7 * 8 *
8 * This program is free software; you can redistribute it and/or modify 9 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by 10 * it under the terms of the GNU General Public License as published by
@@ -25,13 +26,18 @@
25#include <scsi/scsi_eh.h> 26#include <scsi/scsi_eh.h>
26#include <scsi/scsi_dh.h> 27#include <scsi/scsi_dh.h>
27 28
28#define HP_SW_NAME "hp_sw" 29#define HP_SW_NAME "hp_sw"
29 30
30#define HP_SW_TIMEOUT (60 * HZ) 31#define HP_SW_TIMEOUT (60 * HZ)
31#define HP_SW_RETRIES 3 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
32 37
33struct hp_sw_dh_data { 38struct hp_sw_dh_data {
34 unsigned char sense[SCSI_SENSE_BUFFERSIZE]; 39 unsigned char sense[SCSI_SENSE_BUFFERSIZE];
40 int path_state;
35 int retries; 41 int retries;
36}; 42};
37 43
@@ -42,51 +48,161 @@ static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev)
42 return ((struct hp_sw_dh_data *) scsi_dh_data->buf); 48 return ((struct hp_sw_dh_data *) scsi_dh_data->buf);
43} 49}
44 50
45static int hp_sw_done(struct scsi_device *sdev) 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 */
58static int tur_done(struct scsi_device *sdev, unsigned char *sense)
46{ 59{
47 struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
48 struct scsi_sense_hdr sshdr; 60 struct scsi_sense_hdr sshdr;
49 int rc; 61 int ret;
50
51 sdev_printk(KERN_INFO, sdev, "hp_sw_done\n");
52 62
53 rc = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sshdr); 63 ret = scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr);
54 if (!rc) 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;
55 goto done; 69 goto done;
70 }
56 switch (sshdr.sense_key) { 71 switch (sshdr.sense_key) {
72 case UNIT_ATTENTION:
73 ret = SCSI_DH_IMM_RETRY;
74 break;
57 case NOT_READY: 75 case NOT_READY:
58 if ((sshdr.asc == 0x04) && (sshdr.ascq == 3)) { 76 if ((sshdr.asc == 0x04) && (sshdr.ascq == 2)) {
59 rc = SCSI_DH_RETRY; 77 /*
60 h->retries++; 78 * LUN not ready - Initialization command required
79 *
80 * This is the passive path
81 */
82 ret = SCSI_DH_DEV_OFFLINED;
61 break; 83 break;
62 } 84 }
63 /* fall through */ 85 /* Fallthrough */
64 default: 86 default:
65 h->retries++; 87 sdev_printk(KERN_WARNING, sdev,
66 rc = SCSI_DH_IMM_RETRY; 88 "%s: sending tur failed, sense %x/%x/%x\n",
89 HP_SW_NAME, sshdr.sense_key, sshdr.asc,
90 sshdr.ascq);
91 break;
67 } 92 }
68 93
69done: 94done:
70 if (rc == SCSI_DH_OK || rc == SCSI_DH_IO) 95 return ret;
71 h->retries = 0; 96}
72 else if (h->retries > HP_SW_RETRIES) { 97
73 h->retries = 0; 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 */
105static int hp_sw_tur(struct scsi_device *sdev, struct hp_sw_dh_data *h)
106{
107 struct request *req;
108 int ret;
109
110 req = blk_get_request(sdev->request_queue, WRITE, GFP_NOIO);
111 if (!req)
112 return SCSI_DH_RES_TEMP_UNAVAIL;
113
114 req->cmd_type = REQ_TYPE_BLOCK_PC;
115 req->cmd_flags |= REQ_FAILFAST;
116 req->cmd_len = COMMAND_SIZE(TEST_UNIT_READY);
117 memset(req->cmd, 0, MAX_COMMAND_SIZE);
118 req->cmd[0] = TEST_UNIT_READY;
119 req->timeout = HP_SW_TIMEOUT;
120 req->sense = h->sense;
121 memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
122 req->sense_len = 0;
123
124retry:
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 goto retry;
141 if (ret == SCSI_DH_DEV_OFFLINED) {
142 h->path_state = HP_SW_PATH_PASSIVE;
143 ret = SCSI_DH_OK;
144 }
145
146 blk_put_request(req);
147
148 return ret;
149}
150
151/*
152 * start_done - Handle START STOP UNIT return status
153 * @sdev: sdev the command has been sent to
154 * @errors: blk error code
155 */
156static int start_done(struct scsi_device *sdev, unsigned char *sense)
157{
158 struct scsi_sense_hdr sshdr;
159 int rc;
160
161 rc = scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr);
162 if (!rc) {
163 sdev_printk(KERN_WARNING, sdev,
164 "%s: sending start_stop_unit failed, "
165 "no sense available\n",
166 HP_SW_NAME);
167 return SCSI_DH_IO;
168 }
169 switch (sshdr.sense_key) {
170 case NOT_READY:
171 if ((sshdr.asc == 0x04) && (sshdr.ascq == 3)) {
172 /*
173 * LUN not ready - manual intervention required
174 *
175 * Switch-over in progress, retry.
176 */
177 rc = SCSI_DH_RETRY;
178 break;
179 }
180 /* fall through */
181 default:
182 sdev_printk(KERN_WARNING, sdev,
183 "%s: sending start_stop_unit failed, sense %x/%x/%x\n",
184 HP_SW_NAME, sshdr.sense_key, sshdr.asc,
185 sshdr.ascq);
74 rc = SCSI_DH_IO; 186 rc = SCSI_DH_IO;
75 } 187 }
188
76 return rc; 189 return rc;
77} 190}
78 191
79static int hp_sw_activate(struct scsi_device *sdev) 192/*
193 * hp_sw_start_stop - Send START STOP UNIT command
194 * @sdev: sdev command should be sent to
195 *
196 * Sending START STOP UNIT activates the SP.
197 */
198static int hp_sw_start_stop(struct scsi_device *sdev, struct hp_sw_dh_data *h)
80{ 199{
81 struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
82 struct request *req; 200 struct request *req;
83 int ret = SCSI_DH_RES_TEMP_UNAVAIL; 201 int ret, retry;
84 202
85 req = blk_get_request(sdev->request_queue, WRITE, GFP_ATOMIC); 203 req = blk_get_request(sdev->request_queue, WRITE, GFP_NOIO);
86 if (!req) 204 if (!req)
87 goto done; 205 return SCSI_DH_RES_TEMP_UNAVAIL;
88
89 sdev_printk(KERN_INFO, sdev, "sending START_STOP.");
90 206
91 req->cmd_type = REQ_TYPE_BLOCK_PC; 207 req->cmd_type = REQ_TYPE_BLOCK_PC;
92 req->cmd_flags |= REQ_FAILFAST; 208 req->cmd_flags |= REQ_FAILFAST;
@@ -98,19 +214,78 @@ static int hp_sw_activate(struct scsi_device *sdev)
98 req->sense = h->sense; 214 req->sense = h->sense;
99 memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE); 215 memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
100 req->sense_len = 0; 216 req->sense_len = 0;
217 retry = h->retries;
101 218
219retry:
102 ret = blk_execute_rq(req->q, NULL, req, 1); 220 ret = blk_execute_rq(req->q, NULL, req, 1);
103 if (!ret) /* SUCCESS */ 221 if (ret == -EIO) {
104 ret = hp_sw_done(sdev); 222 if (req->sense_len > 0) {
105 else 223 ret = start_done(sdev, h->sense);
224 } else {
225 sdev_printk(KERN_WARNING, sdev,
226 "%s: sending start_stop_unit failed with %x\n",
227 HP_SW_NAME, req->errors);
228 ret = SCSI_DH_IO;
229 }
230 } else
231 ret = SCSI_DH_OK;
232
233 if (ret == SCSI_DH_RETRY) {
234 if (--retry)
235 goto retry;
106 ret = SCSI_DH_IO; 236 ret = SCSI_DH_IO;
107done: 237 }
238
239 blk_put_request(req);
240
241 return ret;
242}
243
244static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req)
245{
246 struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
247 int ret = BLKPREP_OK;
248
249 if (h->path_state != HP_SW_PATH_ACTIVE) {
250 ret = BLKPREP_KILL;
251 req->cmd_flags |= REQ_QUIET;
252 }
253 return ret;
254
255}
256
257/*
258 * hp_sw_activate - Activate a path
259 * @sdev: sdev on the path to be activated
260 *
261 * The HP Active/Passive firmware is pretty simple;
262 * the passive path reports NOT READY with sense codes
263 * 0x04/0x02; a START STOP UNIT command will then
264 * activate the passive path (and deactivate the
265 * previously active one).
266 */
267static int hp_sw_activate(struct scsi_device *sdev)
268{
269 int ret = SCSI_DH_OK;
270 struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
271
272 ret = hp_sw_tur(sdev, h);
273
274 if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE) {
275 ret = hp_sw_start_stop(sdev, h);
276 if (ret == SCSI_DH_OK)
277 sdev_printk(KERN_INFO, sdev,
278 "%s: activated path\n",
279 HP_SW_NAME);
280 }
281
108 return ret; 282 return ret;
109} 283}
110 284
111const struct scsi_dh_devlist hp_sw_dh_data_list[] = { 285const struct scsi_dh_devlist hp_sw_dh_data_list[] = {
112 {"COMPAQ", "MSA"}, 286 {"COMPAQ", "MSA1000 VOLUME"},
113 {"HP", "HSV"}, 287 {"COMPAQ", "HSV110"},
288 {"HP", "HSV100"},
114 {"DEC", "HSG80"}, 289 {"DEC", "HSG80"},
115 {NULL, NULL}, 290 {NULL, NULL},
116}; 291};
@@ -125,30 +300,51 @@ static struct scsi_device_handler hp_sw_dh = {
125 .attach = hp_sw_bus_attach, 300 .attach = hp_sw_bus_attach,
126 .detach = hp_sw_bus_detach, 301 .detach = hp_sw_bus_detach,
127 .activate = hp_sw_activate, 302 .activate = hp_sw_activate,
303 .prep_fn = hp_sw_prep_fn,
128}; 304};
129 305
130static int hp_sw_bus_attach(struct scsi_device *sdev) 306static int hp_sw_bus_attach(struct scsi_device *sdev)
131{ 307{
132 struct scsi_dh_data *scsi_dh_data; 308 struct scsi_dh_data *scsi_dh_data;
309 struct hp_sw_dh_data *h;
133 unsigned long flags; 310 unsigned long flags;
311 int ret;
134 312
135 scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) 313 scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
136 + sizeof(struct hp_sw_dh_data) , GFP_KERNEL); 314 + sizeof(struct hp_sw_dh_data) , GFP_KERNEL);
137 if (!scsi_dh_data) { 315 if (!scsi_dh_data) {
138 sdev_printk(KERN_ERR, sdev, "Attach Failed %s.\n", 316 sdev_printk(KERN_ERR, sdev, "%s: Attach Failed\n",
139 HP_SW_NAME); 317 HP_SW_NAME);
140 return 0; 318 return 0;
141 } 319 }
142 320
143 scsi_dh_data->scsi_dh = &hp_sw_dh; 321 scsi_dh_data->scsi_dh = &hp_sw_dh;
322 h = (struct hp_sw_dh_data *) scsi_dh_data->buf;
323 h->path_state = HP_SW_PATH_UNINITIALIZED;
324 h->retries = HP_SW_RETRIES;
325
326 ret = hp_sw_tur(sdev, h);
327 if (ret != SCSI_DH_OK || h->path_state == HP_SW_PATH_UNINITIALIZED)
328 goto failed;
329
330 if (!try_module_get(THIS_MODULE))
331 goto failed;
332
144 spin_lock_irqsave(sdev->request_queue->queue_lock, flags); 333 spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
145 sdev->scsi_dh_data = scsi_dh_data; 334 sdev->scsi_dh_data = scsi_dh_data;
146 spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); 335 spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
147 try_module_get(THIS_MODULE);
148 336
149 sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", HP_SW_NAME); 337 sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n",
338 HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE?
339 "active":"passive");
150 340
151 return 0; 341 return 0;
342
343failed:
344 kfree(scsi_dh_data);
345 sdev_printk(KERN_ERR, sdev, "%s: not attached\n",
346 HP_SW_NAME);
347 return -EINVAL;
152} 348}
153 349
154static void hp_sw_bus_detach( struct scsi_device *sdev ) 350static void hp_sw_bus_detach( struct scsi_device *sdev )
@@ -162,7 +358,7 @@ static void hp_sw_bus_detach( struct scsi_device *sdev )
162 spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); 358 spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
163 module_put(THIS_MODULE); 359 module_put(THIS_MODULE);
164 360
165 sdev_printk(KERN_NOTICE, sdev, "Detached %s\n", HP_SW_NAME); 361 sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", HP_SW_NAME);
166 362
167 kfree(scsi_dh_data); 363 kfree(scsi_dh_data);
168} 364}
@@ -180,6 +376,6 @@ static void __exit hp_sw_exit(void)
180module_init(hp_sw_init); 376module_init(hp_sw_init);
181module_exit(hp_sw_exit); 377module_exit(hp_sw_exit);
182 378
183MODULE_DESCRIPTION("HP MSA 1000"); 379MODULE_DESCRIPTION("HP Active/Passive driver");
184MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu"); 380MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu");
185MODULE_LICENSE("GPL"); 381MODULE_LICENSE("GPL");