Merge remote branch 'nouveau/for-airlied' of nouveau-2.6
[pandora-kernel.git] / drivers / scsi / device_handler / scsi_dh.c
1 /*
2  * SCSI device handler infrastruture.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  *
18  * Copyright IBM Corporation, 2007
19  *      Authors:
20  *               Chandra Seetharaman <sekharan@us.ibm.com>
21  *               Mike Anderson <andmike@linux.vnet.ibm.com>
22  */
23
24 #include <scsi/scsi_dh.h>
25 #include "../scsi_priv.h"
26
27 struct scsi_dh_devinfo_list {
28         struct list_head node;
29         char vendor[9];
30         char model[17];
31         struct scsi_device_handler *handler;
32 };
33
34 static DEFINE_SPINLOCK(list_lock);
35 static LIST_HEAD(scsi_dh_list);
36 static LIST_HEAD(scsi_dh_dev_list);
37
38 static struct scsi_device_handler *get_device_handler(const char *name)
39 {
40         struct scsi_device_handler *tmp, *found = NULL;
41
42         spin_lock(&list_lock);
43         list_for_each_entry(tmp, &scsi_dh_list, list) {
44                 if (!strncmp(tmp->name, name, strlen(tmp->name))) {
45                         found = tmp;
46                         break;
47                 }
48         }
49         spin_unlock(&list_lock);
50         return found;
51 }
52
53
54 static struct scsi_device_handler *
55 scsi_dh_cache_lookup(struct scsi_device *sdev)
56 {
57         struct scsi_dh_devinfo_list *tmp;
58         struct scsi_device_handler *found_dh = NULL;
59
60         spin_lock(&list_lock);
61         list_for_each_entry(tmp, &scsi_dh_dev_list, node) {
62                 if (!strncmp(sdev->vendor, tmp->vendor, strlen(tmp->vendor)) &&
63                     !strncmp(sdev->model, tmp->model, strlen(tmp->model))) {
64                         found_dh = tmp->handler;
65                         break;
66                 }
67         }
68         spin_unlock(&list_lock);
69
70         return found_dh;
71 }
72
73 static int scsi_dh_handler_lookup(struct scsi_device_handler *scsi_dh,
74                                   struct scsi_device *sdev)
75 {
76         int i, found = 0;
77
78         for(i = 0; scsi_dh->devlist[i].vendor; i++) {
79                 if (!strncmp(sdev->vendor, scsi_dh->devlist[i].vendor,
80                              strlen(scsi_dh->devlist[i].vendor)) &&
81                     !strncmp(sdev->model, scsi_dh->devlist[i].model,
82                              strlen(scsi_dh->devlist[i].model))) {
83                         found = 1;
84                         break;
85                 }
86         }
87         return found;
88 }
89
90 /*
91  * device_handler_match - Attach a device handler to a device
92  * @scsi_dh - The device handler to match against or NULL
93  * @sdev - SCSI device to be tested against @scsi_dh
94  *
95  * Tests @sdev against the device handler @scsi_dh or against
96  * all registered device_handler if @scsi_dh == NULL.
97  * Returns the found device handler or NULL if not found.
98  */
99 static struct scsi_device_handler *
100 device_handler_match(struct scsi_device_handler *scsi_dh,
101                      struct scsi_device *sdev)
102 {
103         struct scsi_device_handler *found_dh = NULL;
104         struct scsi_dh_devinfo_list *tmp;
105
106         found_dh = scsi_dh_cache_lookup(sdev);
107         if (found_dh)
108                 return found_dh;
109
110         if (scsi_dh) {
111                 if (scsi_dh_handler_lookup(scsi_dh, sdev))
112                         found_dh = scsi_dh;
113         } else {
114                 struct scsi_device_handler *tmp_dh;
115
116                 spin_lock(&list_lock);
117                 list_for_each_entry(tmp_dh, &scsi_dh_list, list) {
118                         if (scsi_dh_handler_lookup(tmp_dh, sdev))
119                                 found_dh = tmp_dh;
120                 }
121                 spin_unlock(&list_lock);
122         }
123
124         if (found_dh) { /* If device is found, add it to the cache */
125                 tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
126                 if (tmp) {
127                         strncpy(tmp->vendor, sdev->vendor, 8);
128                         strncpy(tmp->model, sdev->model, 16);
129                         tmp->vendor[8] = '\0';
130                         tmp->model[16] = '\0';
131                         tmp->handler = found_dh;
132                         spin_lock(&list_lock);
133                         list_add(&tmp->node, &scsi_dh_dev_list);
134                         spin_unlock(&list_lock);
135                 } else {
136                         found_dh = NULL;
137                 }
138         }
139
140         return found_dh;
141 }
142
143 /*
144  * scsi_dh_handler_attach - Attach a device handler to a device
145  * @sdev - SCSI device the device handler should attach to
146  * @scsi_dh - The device handler to attach
147  */
148 static int scsi_dh_handler_attach(struct scsi_device *sdev,
149                                   struct scsi_device_handler *scsi_dh)
150 {
151         int err = 0;
152
153         if (sdev->scsi_dh_data) {
154                 if (sdev->scsi_dh_data->scsi_dh != scsi_dh)
155                         err = -EBUSY;
156                 else
157                         kref_get(&sdev->scsi_dh_data->kref);
158         } else if (scsi_dh->attach) {
159                 err = scsi_dh->attach(sdev);
160                 if (!err) {
161                         kref_init(&sdev->scsi_dh_data->kref);
162                         sdev->scsi_dh_data->sdev = sdev;
163                 }
164         }
165         return err;
166 }
167
168 static void __detach_handler (struct kref *kref)
169 {
170         struct scsi_dh_data *scsi_dh_data = container_of(kref, struct scsi_dh_data, kref);
171         scsi_dh_data->scsi_dh->detach(scsi_dh_data->sdev);
172 }
173
174 /*
175  * scsi_dh_handler_detach - Detach a device handler from a device
176  * @sdev - SCSI device the device handler should be detached from
177  * @scsi_dh - Device handler to be detached
178  *
179  * Detach from a device handler. If a device handler is specified,
180  * only detach if the currently attached handler matches @scsi_dh.
181  */
182 static void scsi_dh_handler_detach(struct scsi_device *sdev,
183                                    struct scsi_device_handler *scsi_dh)
184 {
185         if (!sdev->scsi_dh_data)
186                 return;
187
188         if (scsi_dh && scsi_dh != sdev->scsi_dh_data->scsi_dh)
189                 return;
190
191         if (!scsi_dh)
192                 scsi_dh = sdev->scsi_dh_data->scsi_dh;
193
194         if (scsi_dh && scsi_dh->detach)
195                 kref_put(&sdev->scsi_dh_data->kref, __detach_handler);
196 }
197
198 /*
199  * Functions for sysfs attribute 'dh_state'
200  */
201 static ssize_t
202 store_dh_state(struct device *dev, struct device_attribute *attr,
203                const char *buf, size_t count)
204 {
205         struct scsi_device *sdev = to_scsi_device(dev);
206         struct scsi_device_handler *scsi_dh;
207         int err = -EINVAL;
208
209         if (!sdev->scsi_dh_data) {
210                 /*
211                  * Attach to a device handler
212                  */
213                 if (!(scsi_dh = get_device_handler(buf)))
214                         return err;
215                 err = scsi_dh_handler_attach(sdev, scsi_dh);
216         } else {
217                 scsi_dh = sdev->scsi_dh_data->scsi_dh;
218                 if (!strncmp(buf, "detach", 6)) {
219                         /*
220                          * Detach from a device handler
221                          */
222                         scsi_dh_handler_detach(sdev, scsi_dh);
223                         err = 0;
224                 } else if (!strncmp(buf, "activate", 8)) {
225                         /*
226                          * Activate a device handler
227                          */
228                         if (scsi_dh->activate)
229                                 err = scsi_dh->activate(sdev, NULL, NULL);
230                         else
231                                 err = 0;
232                 }
233         }
234
235         return err<0?err:count;
236 }
237
238 static ssize_t
239 show_dh_state(struct device *dev, struct device_attribute *attr, char *buf)
240 {
241         struct scsi_device *sdev = to_scsi_device(dev);
242
243         if (!sdev->scsi_dh_data)
244                 return snprintf(buf, 20, "detached\n");
245
246         return snprintf(buf, 20, "%s\n", sdev->scsi_dh_data->scsi_dh->name);
247 }
248
249 static struct device_attribute scsi_dh_state_attr =
250         __ATTR(dh_state, S_IRUGO | S_IWUSR, show_dh_state,
251                store_dh_state);
252
253 /*
254  * scsi_dh_sysfs_attr_add - Callback for scsi_init_dh
255  */
256 static int scsi_dh_sysfs_attr_add(struct device *dev, void *data)
257 {
258         struct scsi_device *sdev;
259         int err;
260
261         if (!scsi_is_sdev_device(dev))
262                 return 0;
263
264         sdev = to_scsi_device(dev);
265
266         err = device_create_file(&sdev->sdev_gendev,
267                                  &scsi_dh_state_attr);
268
269         return 0;
270 }
271
272 /*
273  * scsi_dh_sysfs_attr_remove - Callback for scsi_exit_dh
274  */
275 static int scsi_dh_sysfs_attr_remove(struct device *dev, void *data)
276 {
277         struct scsi_device *sdev;
278
279         if (!scsi_is_sdev_device(dev))
280                 return 0;
281
282         sdev = to_scsi_device(dev);
283
284         device_remove_file(&sdev->sdev_gendev,
285                            &scsi_dh_state_attr);
286
287         return 0;
288 }
289
290 /*
291  * scsi_dh_notifier - notifier chain callback
292  */
293 static int scsi_dh_notifier(struct notifier_block *nb,
294                             unsigned long action, void *data)
295 {
296         struct device *dev = data;
297         struct scsi_device *sdev;
298         int err = 0;
299         struct scsi_device_handler *devinfo = NULL;
300
301         if (!scsi_is_sdev_device(dev))
302                 return 0;
303
304         sdev = to_scsi_device(dev);
305
306         if (action == BUS_NOTIFY_ADD_DEVICE) {
307                 err = device_create_file(dev, &scsi_dh_state_attr);
308                 /* don't care about err */
309                 devinfo = device_handler_match(NULL, sdev);
310                 if (devinfo)
311                         err = scsi_dh_handler_attach(sdev, devinfo);
312         } else if (action == BUS_NOTIFY_DEL_DEVICE) {
313                 device_remove_file(dev, &scsi_dh_state_attr);
314                 scsi_dh_handler_detach(sdev, NULL);
315         }
316         return err;
317 }
318
319 /*
320  * scsi_dh_notifier_add - Callback for scsi_register_device_handler
321  */
322 static int scsi_dh_notifier_add(struct device *dev, void *data)
323 {
324         struct scsi_device_handler *scsi_dh = data;
325         struct scsi_device *sdev;
326
327         if (!scsi_is_sdev_device(dev))
328                 return 0;
329
330         if (!get_device(dev))
331                 return 0;
332
333         sdev = to_scsi_device(dev);
334
335         if (device_handler_match(scsi_dh, sdev))
336                 scsi_dh_handler_attach(sdev, scsi_dh);
337
338         put_device(dev);
339
340         return 0;
341 }
342
343 /*
344  * scsi_dh_notifier_remove - Callback for scsi_unregister_device_handler
345  */
346 static int scsi_dh_notifier_remove(struct device *dev, void *data)
347 {
348         struct scsi_device_handler *scsi_dh = data;
349         struct scsi_device *sdev;
350
351         if (!scsi_is_sdev_device(dev))
352                 return 0;
353
354         if (!get_device(dev))
355                 return 0;
356
357         sdev = to_scsi_device(dev);
358
359         scsi_dh_handler_detach(sdev, scsi_dh);
360
361         put_device(dev);
362
363         return 0;
364 }
365
366 /*
367  * scsi_register_device_handler - register a device handler personality
368  *      module.
369  * @scsi_dh - device handler to be registered.
370  *
371  * Returns 0 on success, -EBUSY if handler already registered.
372  */
373 int scsi_register_device_handler(struct scsi_device_handler *scsi_dh)
374 {
375         if (get_device_handler(scsi_dh->name))
376                 return -EBUSY;
377
378         spin_lock(&list_lock);
379         list_add(&scsi_dh->list, &scsi_dh_list);
380         spin_unlock(&list_lock);
381         bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add);
382         printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name);
383
384         return SCSI_DH_OK;
385 }
386 EXPORT_SYMBOL_GPL(scsi_register_device_handler);
387
388 /*
389  * scsi_unregister_device_handler - register a device handler personality
390  *      module.
391  * @scsi_dh - device handler to be unregistered.
392  *
393  * Returns 0 on success, -ENODEV if handler not registered.
394  */
395 int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
396 {
397         struct scsi_dh_devinfo_list *tmp, *pos;
398
399         if (!get_device_handler(scsi_dh->name))
400                 return -ENODEV;
401
402         bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh,
403                          scsi_dh_notifier_remove);
404
405         spin_lock(&list_lock);
406         list_del(&scsi_dh->list);
407         list_for_each_entry_safe(pos, tmp, &scsi_dh_dev_list, node) {
408                 if (pos->handler == scsi_dh) {
409                         list_del(&pos->node);
410                         kfree(pos);
411                 }
412         }
413         spin_unlock(&list_lock);
414         printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name);
415
416         return SCSI_DH_OK;
417 }
418 EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
419
420 /*
421  * scsi_dh_activate - activate the path associated with the scsi_device
422  *      corresponding to the given request queue.
423  *     Returns immediately without waiting for activation to be completed.
424  * @q    - Request queue that is associated with the scsi_device to be
425  *         activated.
426  * @fn   - Function to be called upon completion of the activation.
427  *         Function fn is called with data (below) and the error code.
428  *         Function fn may be called from the same calling context. So,
429  *         do not hold the lock in the caller which may be needed in fn.
430  * @data - data passed to the function fn upon completion.
431  *
432  */
433 int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data)
434 {
435         int err = 0;
436         unsigned long flags;
437         struct scsi_device *sdev;
438         struct scsi_device_handler *scsi_dh = NULL;
439
440         spin_lock_irqsave(q->queue_lock, flags);
441         sdev = q->queuedata;
442         if (sdev && sdev->scsi_dh_data)
443                 scsi_dh = sdev->scsi_dh_data->scsi_dh;
444         if (!scsi_dh || !get_device(&sdev->sdev_gendev))
445                 err = SCSI_DH_NOSYS;
446         spin_unlock_irqrestore(q->queue_lock, flags);
447
448         if (err)
449                 return err;
450
451         if (scsi_dh->activate)
452                 err = scsi_dh->activate(sdev, fn, data);
453         put_device(&sdev->sdev_gendev);
454         return err;
455 }
456 EXPORT_SYMBOL_GPL(scsi_dh_activate);
457
458 /*
459  * scsi_dh_set_params - set the parameters for the device as per the
460  *      string specified in params.
461  * @q - Request queue that is associated with the scsi_device for
462  *      which the parameters to be set.
463  * @params - parameters in the following format
464  *      "no_of_params\0param1\0param2\0param3\0...\0"
465  *      for example, string for 2 parameters with value 10 and 21
466  *      is specified as "2\010\021\0".
467  */
468 int scsi_dh_set_params(struct request_queue *q, const char *params)
469 {
470         int err = -SCSI_DH_NOSYS;
471         unsigned long flags;
472         struct scsi_device *sdev;
473         struct scsi_device_handler *scsi_dh = NULL;
474
475         spin_lock_irqsave(q->queue_lock, flags);
476         sdev = q->queuedata;
477         if (sdev && sdev->scsi_dh_data)
478                 scsi_dh = sdev->scsi_dh_data->scsi_dh;
479         if (scsi_dh && scsi_dh->set_params && get_device(&sdev->sdev_gendev))
480                 err = 0;
481         spin_unlock_irqrestore(q->queue_lock, flags);
482
483         if (err)
484                 return err;
485         err = scsi_dh->set_params(sdev, params);
486         put_device(&sdev->sdev_gendev);
487         return err;
488 }
489 EXPORT_SYMBOL_GPL(scsi_dh_set_params);
490
491 /*
492  * scsi_dh_handler_exist - Return TRUE(1) if a device handler exists for
493  *      the given name. FALSE(0) otherwise.
494  * @name - name of the device handler.
495  */
496 int scsi_dh_handler_exist(const char *name)
497 {
498         return (get_device_handler(name) != NULL);
499 }
500 EXPORT_SYMBOL_GPL(scsi_dh_handler_exist);
501
502 /*
503  * scsi_dh_handler_attach - Attach device handler
504  * @sdev - sdev the handler should be attached to
505  * @name - name of the handler to attach
506  */
507 int scsi_dh_attach(struct request_queue *q, const char *name)
508 {
509         unsigned long flags;
510         struct scsi_device *sdev;
511         struct scsi_device_handler *scsi_dh;
512         int err = 0;
513
514         scsi_dh = get_device_handler(name);
515         if (!scsi_dh)
516                 return -EINVAL;
517
518         spin_lock_irqsave(q->queue_lock, flags);
519         sdev = q->queuedata;
520         if (!sdev || !get_device(&sdev->sdev_gendev))
521                 err = -ENODEV;
522         spin_unlock_irqrestore(q->queue_lock, flags);
523
524         if (!err) {
525                 err = scsi_dh_handler_attach(sdev, scsi_dh);
526                 put_device(&sdev->sdev_gendev);
527         }
528         return err;
529 }
530 EXPORT_SYMBOL_GPL(scsi_dh_attach);
531
532 /*
533  * scsi_dh_handler_detach - Detach device handler
534  * @sdev - sdev the handler should be detached from
535  *
536  * This function will detach the device handler only
537  * if the sdev is not part of the internal list, ie
538  * if it has been attached manually.
539  */
540 void scsi_dh_detach(struct request_queue *q)
541 {
542         unsigned long flags;
543         struct scsi_device *sdev;
544         struct scsi_device_handler *scsi_dh = NULL;
545
546         spin_lock_irqsave(q->queue_lock, flags);
547         sdev = q->queuedata;
548         if (!sdev || !get_device(&sdev->sdev_gendev))
549                 sdev = NULL;
550         spin_unlock_irqrestore(q->queue_lock, flags);
551
552         if (!sdev)
553                 return;
554
555         if (sdev->scsi_dh_data) {
556                 scsi_dh = sdev->scsi_dh_data->scsi_dh;
557                 scsi_dh_handler_detach(sdev, scsi_dh);
558         }
559         put_device(&sdev->sdev_gendev);
560 }
561 EXPORT_SYMBOL_GPL(scsi_dh_detach);
562
563 static struct notifier_block scsi_dh_nb = {
564         .notifier_call = scsi_dh_notifier
565 };
566
567 static int __init scsi_dh_init(void)
568 {
569         int r;
570
571         r = bus_register_notifier(&scsi_bus_type, &scsi_dh_nb);
572
573         if (!r)
574                 bus_for_each_dev(&scsi_bus_type, NULL, NULL,
575                                  scsi_dh_sysfs_attr_add);
576
577         return r;
578 }
579
580 static void __exit scsi_dh_exit(void)
581 {
582         bus_for_each_dev(&scsi_bus_type, NULL, NULL,
583                          scsi_dh_sysfs_attr_remove);
584         bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb);
585 }
586
587 module_init(scsi_dh_init);
588 module_exit(scsi_dh_exit);
589
590 MODULE_DESCRIPTION("SCSI device handler");
591 MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>");
592 MODULE_LICENSE("GPL");