Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

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