Merge branch 'master' into for-linus
[sfrench/cifs-2.6.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);
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                 devinfo = device_handler_match(NULL, sdev);
308                 if (!devinfo)
309                         goto out;
310
311                 err = scsi_dh_handler_attach(sdev, devinfo);
312                 if (!err)
313                         err = device_create_file(dev, &scsi_dh_state_attr);
314         } else if (action == BUS_NOTIFY_DEL_DEVICE) {
315                 device_remove_file(dev, &scsi_dh_state_attr);
316                 scsi_dh_handler_detach(sdev, NULL);
317         }
318 out:
319         return err;
320 }
321
322 /*
323  * scsi_dh_notifier_add - Callback for scsi_register_device_handler
324  */
325 static int scsi_dh_notifier_add(struct device *dev, void *data)
326 {
327         struct scsi_device_handler *scsi_dh = data;
328         struct scsi_device *sdev;
329
330         if (!scsi_is_sdev_device(dev))
331                 return 0;
332
333         if (!get_device(dev))
334                 return 0;
335
336         sdev = to_scsi_device(dev);
337
338         if (device_handler_match(scsi_dh, sdev))
339                 scsi_dh_handler_attach(sdev, scsi_dh);
340
341         put_device(dev);
342
343         return 0;
344 }
345
346 /*
347  * scsi_dh_notifier_remove - Callback for scsi_unregister_device_handler
348  */
349 static int scsi_dh_notifier_remove(struct device *dev, void *data)
350 {
351         struct scsi_device_handler *scsi_dh = data;
352         struct scsi_device *sdev;
353
354         if (!scsi_is_sdev_device(dev))
355                 return 0;
356
357         if (!get_device(dev))
358                 return 0;
359
360         sdev = to_scsi_device(dev);
361
362         scsi_dh_handler_detach(sdev, scsi_dh);
363
364         put_device(dev);
365
366         return 0;
367 }
368
369 /*
370  * scsi_register_device_handler - register a device handler personality
371  *      module.
372  * @scsi_dh - device handler to be registered.
373  *
374  * Returns 0 on success, -EBUSY if handler already registered.
375  */
376 int scsi_register_device_handler(struct scsi_device_handler *scsi_dh)
377 {
378         if (get_device_handler(scsi_dh->name))
379                 return -EBUSY;
380
381         spin_lock(&list_lock);
382         list_add(&scsi_dh->list, &scsi_dh_list);
383         spin_unlock(&list_lock);
384         bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add);
385         printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name);
386
387         return SCSI_DH_OK;
388 }
389 EXPORT_SYMBOL_GPL(scsi_register_device_handler);
390
391 /*
392  * scsi_unregister_device_handler - register a device handler personality
393  *      module.
394  * @scsi_dh - device handler to be unregistered.
395  *
396  * Returns 0 on success, -ENODEV if handler not registered.
397  */
398 int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
399 {
400         struct scsi_dh_devinfo_list *tmp, *pos;
401
402         if (!get_device_handler(scsi_dh->name))
403                 return -ENODEV;
404
405         bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh,
406                          scsi_dh_notifier_remove);
407
408         spin_lock(&list_lock);
409         list_del(&scsi_dh->list);
410         list_for_each_entry_safe(pos, tmp, &scsi_dh_dev_list, node) {
411                 if (pos->handler == scsi_dh) {
412                         list_del(&pos->node);
413                         kfree(pos);
414                 }
415         }
416         spin_unlock(&list_lock);
417         printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name);
418
419         return SCSI_DH_OK;
420 }
421 EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
422
423 /*
424  * scsi_dh_activate - activate the path associated with the scsi_device
425  *      corresponding to the given request queue.
426  * @q - Request queue that is associated with the scsi_device to be
427  *      activated.
428  */
429 int scsi_dh_activate(struct request_queue *q)
430 {
431         int err = 0;
432         unsigned long flags;
433         struct scsi_device *sdev;
434         struct scsi_device_handler *scsi_dh = NULL;
435
436         spin_lock_irqsave(q->queue_lock, flags);
437         sdev = q->queuedata;
438         if (sdev && sdev->scsi_dh_data)
439                 scsi_dh = sdev->scsi_dh_data->scsi_dh;
440         if (!scsi_dh || !get_device(&sdev->sdev_gendev))
441                 err = SCSI_DH_NOSYS;
442         spin_unlock_irqrestore(q->queue_lock, flags);
443
444         if (err)
445                 return err;
446
447         if (scsi_dh->activate)
448                 err = scsi_dh->activate(sdev);
449         put_device(&sdev->sdev_gendev);
450         return err;
451 }
452 EXPORT_SYMBOL_GPL(scsi_dh_activate);
453
454 /*
455  * scsi_dh_set_params - set the parameters for the device as per the
456  *      string specified in params.
457  * @q - Request queue that is associated with the scsi_device for
458  *      which the parameters to be set.
459  * @params - parameters in the following format
460  *      "no_of_params\0param1\0param2\0param3\0...\0"
461  *      for example, string for 2 parameters with value 10 and 21
462  *      is specified as "2\010\021\0".
463  */
464 int scsi_dh_set_params(struct request_queue *q, const char *params)
465 {
466         int err = -SCSI_DH_NOSYS;
467         unsigned long flags;
468         struct scsi_device *sdev;
469         struct scsi_device_handler *scsi_dh = NULL;
470
471         spin_lock_irqsave(q->queue_lock, flags);
472         sdev = q->queuedata;
473         if (sdev && sdev->scsi_dh_data)
474                 scsi_dh = sdev->scsi_dh_data->scsi_dh;
475         if (scsi_dh && scsi_dh->set_params && get_device(&sdev->sdev_gendev))
476                 err = 0;
477         spin_unlock_irqrestore(q->queue_lock, flags);
478
479         if (err)
480                 return err;
481         err = scsi_dh->set_params(sdev, params);
482         put_device(&sdev->sdev_gendev);
483         return err;
484 }
485 EXPORT_SYMBOL_GPL(scsi_dh_set_params);
486
487 /*
488  * scsi_dh_handler_exist - Return TRUE(1) if a device handler exists for
489  *      the given name. FALSE(0) otherwise.
490  * @name - name of the device handler.
491  */
492 int scsi_dh_handler_exist(const char *name)
493 {
494         return (get_device_handler(name) != NULL);
495 }
496 EXPORT_SYMBOL_GPL(scsi_dh_handler_exist);
497
498 /*
499  * scsi_dh_handler_attach - Attach device handler
500  * @sdev - sdev the handler should be attached to
501  * @name - name of the handler to attach
502  */
503 int scsi_dh_attach(struct request_queue *q, const char *name)
504 {
505         unsigned long flags;
506         struct scsi_device *sdev;
507         struct scsi_device_handler *scsi_dh;
508         int err = 0;
509
510         scsi_dh = get_device_handler(name);
511         if (!scsi_dh)
512                 return -EINVAL;
513
514         spin_lock_irqsave(q->queue_lock, flags);
515         sdev = q->queuedata;
516         if (!sdev || !get_device(&sdev->sdev_gendev))
517                 err = -ENODEV;
518         spin_unlock_irqrestore(q->queue_lock, flags);
519
520         if (!err) {
521                 err = scsi_dh_handler_attach(sdev, scsi_dh);
522                 put_device(&sdev->sdev_gendev);
523         }
524         return err;
525 }
526 EXPORT_SYMBOL_GPL(scsi_dh_attach);
527
528 /*
529  * scsi_dh_handler_detach - Detach device handler
530  * @sdev - sdev the handler should be detached from
531  *
532  * This function will detach the device handler only
533  * if the sdev is not part of the internal list, ie
534  * if it has been attached manually.
535  */
536 void scsi_dh_detach(struct request_queue *q)
537 {
538         unsigned long flags;
539         struct scsi_device *sdev;
540         struct scsi_device_handler *scsi_dh = NULL;
541
542         spin_lock_irqsave(q->queue_lock, flags);
543         sdev = q->queuedata;
544         if (!sdev || !get_device(&sdev->sdev_gendev))
545                 sdev = NULL;
546         spin_unlock_irqrestore(q->queue_lock, flags);
547
548         if (!sdev)
549                 return;
550
551         if (sdev->scsi_dh_data) {
552                 scsi_dh = sdev->scsi_dh_data->scsi_dh;
553                 scsi_dh_handler_detach(sdev, scsi_dh);
554         }
555         put_device(&sdev->sdev_gendev);
556 }
557 EXPORT_SYMBOL_GPL(scsi_dh_detach);
558
559 static struct notifier_block scsi_dh_nb = {
560         .notifier_call = scsi_dh_notifier
561 };
562
563 static int __init scsi_dh_init(void)
564 {
565         int r;
566
567         r = bus_register_notifier(&scsi_bus_type, &scsi_dh_nb);
568
569         if (!r)
570                 bus_for_each_dev(&scsi_bus_type, NULL, NULL,
571                                  scsi_dh_sysfs_attr_add);
572
573         return r;
574 }
575
576 static void __exit scsi_dh_exit(void)
577 {
578         bus_for_each_dev(&scsi_bus_type, NULL, NULL,
579                          scsi_dh_sysfs_attr_remove);
580         bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb);
581 }
582
583 module_init(scsi_dh_init);
584 module_exit(scsi_dh_exit);
585
586 MODULE_DESCRIPTION("SCSI device handler");
587 MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>");
588 MODULE_LICENSE("GPL");