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