]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/scsi/device_handler/scsi_dh_hp_sw.c
Merge branch 'linus' into genirq
[linux-2.6-omap-h63xx.git] / drivers / scsi / device_handler / scsi_dh_hp_sw.c
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         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         req->cmd[0] = TEST_UNIT_READY;
118         req->timeout = HP_SW_TIMEOUT;
119         req->sense = h->sense;
120         memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
121         req->sense_len = 0;
122
123 retry:
124         ret = blk_execute_rq(req->q, NULL, req, 1);
125         if (ret == -EIO) {
126                 if (req->sense_len > 0) {
127                         ret = tur_done(sdev, h->sense);
128                 } else {
129                         sdev_printk(KERN_WARNING, sdev,
130                                     "%s: sending tur failed with %x\n",
131                                     HP_SW_NAME, req->errors);
132                         ret = SCSI_DH_IO;
133                 }
134         } else {
135                 h->path_state = HP_SW_PATH_ACTIVE;
136                 ret = SCSI_DH_OK;
137         }
138         if (ret == SCSI_DH_IMM_RETRY)
139                 goto retry;
140         if (ret == SCSI_DH_DEV_OFFLINED) {
141                 h->path_state = HP_SW_PATH_PASSIVE;
142                 ret = SCSI_DH_OK;
143         }
144
145         blk_put_request(req);
146
147         return ret;
148 }
149
150 /*
151  * start_done - Handle START STOP UNIT return status
152  * @sdev: sdev the command has been sent to
153  * @errors: blk error code
154  */
155 static int start_done(struct scsi_device *sdev, unsigned char *sense)
156 {
157         struct scsi_sense_hdr sshdr;
158         int rc;
159
160         rc = scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr);
161         if (!rc) {
162                 sdev_printk(KERN_WARNING, sdev,
163                             "%s: sending start_stop_unit failed, "
164                             "no sense available\n",
165                             HP_SW_NAME);
166                 return SCSI_DH_IO;
167         }
168         switch (sshdr.sense_key) {
169         case NOT_READY:
170                 if ((sshdr.asc == 0x04) && (sshdr.ascq == 3)) {
171                         /*
172                          * LUN not ready - manual intervention required
173                          *
174                          * Switch-over in progress, retry.
175                          */
176                         rc = SCSI_DH_RETRY;
177                         break;
178                 }
179                 /* fall through */
180         default:
181                 sdev_printk(KERN_WARNING, sdev,
182                            "%s: sending start_stop_unit failed, sense %x/%x/%x\n",
183                            HP_SW_NAME, sshdr.sense_key, sshdr.asc,
184                            sshdr.ascq);
185                 rc = SCSI_DH_IO;
186         }
187
188         return rc;
189 }
190
191 /*
192  * hp_sw_start_stop - Send START STOP UNIT command
193  * @sdev: sdev command should be sent to
194  *
195  * Sending START STOP UNIT activates the SP.
196  */
197 static int hp_sw_start_stop(struct scsi_device *sdev, struct hp_sw_dh_data *h)
198 {
199         struct request *req;
200         int ret, retry;
201
202         req = blk_get_request(sdev->request_queue, WRITE, GFP_NOIO);
203         if (!req)
204                 return SCSI_DH_RES_TEMP_UNAVAIL;
205
206         req->cmd_type = REQ_TYPE_BLOCK_PC;
207         req->cmd_flags |= REQ_FAILFAST;
208         req->cmd_len = COMMAND_SIZE(START_STOP);
209         req->cmd[0] = START_STOP;
210         req->cmd[4] = 1;        /* Start spin cycle */
211         req->timeout = HP_SW_TIMEOUT;
212         req->sense = h->sense;
213         memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
214         req->sense_len = 0;
215         retry = h->retries;
216
217 retry:
218         ret = blk_execute_rq(req->q, NULL, req, 1);
219         if (ret == -EIO) {
220                 if (req->sense_len > 0) {
221                         ret = start_done(sdev, h->sense);
222                 } else {
223                         sdev_printk(KERN_WARNING, sdev,
224                                     "%s: sending start_stop_unit failed with %x\n",
225                                     HP_SW_NAME, req->errors);
226                         ret = SCSI_DH_IO;
227                 }
228         } else
229                 ret = SCSI_DH_OK;
230
231         if (ret == SCSI_DH_RETRY) {
232                 if (--retry)
233                         goto retry;
234                 ret = SCSI_DH_IO;
235         }
236
237         blk_put_request(req);
238
239         return ret;
240 }
241
242 static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req)
243 {
244         struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
245         int ret = BLKPREP_OK;
246
247         if (h->path_state != HP_SW_PATH_ACTIVE) {
248                 ret = BLKPREP_KILL;
249                 req->cmd_flags |= REQ_QUIET;
250         }
251         return ret;
252
253 }
254
255 /*
256  * hp_sw_activate - Activate a path
257  * @sdev: sdev on the path to be activated
258  *
259  * The HP Active/Passive firmware is pretty simple;
260  * the passive path reports NOT READY with sense codes
261  * 0x04/0x02; a START STOP UNIT command will then
262  * activate the passive path (and deactivate the
263  * previously active one).
264  */
265 static int hp_sw_activate(struct scsi_device *sdev)
266 {
267         int ret = SCSI_DH_OK;
268         struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
269
270         ret = hp_sw_tur(sdev, h);
271
272         if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE) {
273                 ret = hp_sw_start_stop(sdev, h);
274                 if (ret == SCSI_DH_OK)
275                         sdev_printk(KERN_INFO, sdev,
276                                     "%s: activated path\n",
277                                     HP_SW_NAME);
278         }
279
280         return ret;
281 }
282
283 static const struct scsi_dh_devlist hp_sw_dh_data_list[] = {
284         {"COMPAQ", "MSA1000 VOLUME"},
285         {"COMPAQ", "HSV110"},
286         {"HP", "HSV100"},
287         {"DEC", "HSG80"},
288         {NULL, NULL},
289 };
290
291 static int hp_sw_bus_attach(struct scsi_device *sdev);
292 static void hp_sw_bus_detach(struct scsi_device *sdev);
293
294 static struct scsi_device_handler hp_sw_dh = {
295         .name           = HP_SW_NAME,
296         .module         = THIS_MODULE,
297         .devlist        = hp_sw_dh_data_list,
298         .attach         = hp_sw_bus_attach,
299         .detach         = hp_sw_bus_detach,
300         .activate       = hp_sw_activate,
301         .prep_fn        = hp_sw_prep_fn,
302 };
303
304 static int hp_sw_bus_attach(struct scsi_device *sdev)
305 {
306         struct scsi_dh_data *scsi_dh_data;
307         struct hp_sw_dh_data *h;
308         unsigned long flags;
309         int ret;
310
311         scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
312                                + sizeof(struct hp_sw_dh_data) , GFP_KERNEL);
313         if (!scsi_dh_data) {
314                 sdev_printk(KERN_ERR, sdev, "%s: Attach Failed\n",
315                             HP_SW_NAME);
316                 return 0;
317         }
318
319         scsi_dh_data->scsi_dh = &hp_sw_dh;
320         h = (struct hp_sw_dh_data *) scsi_dh_data->buf;
321         h->path_state = HP_SW_PATH_UNINITIALIZED;
322         h->retries = HP_SW_RETRIES;
323
324         ret = hp_sw_tur(sdev, h);
325         if (ret != SCSI_DH_OK || h->path_state == HP_SW_PATH_UNINITIALIZED)
326                 goto failed;
327
328         if (!try_module_get(THIS_MODULE))
329                 goto failed;
330
331         spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
332         sdev->scsi_dh_data = scsi_dh_data;
333         spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
334
335         sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n",
336                     HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE?
337                     "active":"passive");
338
339         return 0;
340
341 failed:
342         kfree(scsi_dh_data);
343         sdev_printk(KERN_ERR, sdev, "%s: not attached\n",
344                     HP_SW_NAME);
345         return -EINVAL;
346 }
347
348 static void hp_sw_bus_detach( struct scsi_device *sdev )
349 {
350         struct scsi_dh_data *scsi_dh_data;
351         unsigned long flags;
352
353         spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
354         scsi_dh_data = sdev->scsi_dh_data;
355         sdev->scsi_dh_data = NULL;
356         spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
357         module_put(THIS_MODULE);
358
359         sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", HP_SW_NAME);
360
361         kfree(scsi_dh_data);
362 }
363
364 static int __init hp_sw_init(void)
365 {
366         return scsi_register_device_handler(&hp_sw_dh);
367 }
368
369 static void __exit hp_sw_exit(void)
370 {
371         scsi_unregister_device_handler(&hp_sw_dh);
372 }
373
374 module_init(hp_sw_init);
375 module_exit(hp_sw_exit);
376
377 MODULE_DESCRIPTION("HP Active/Passive driver");
378 MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu");
379 MODULE_LICENSE("GPL");