scsi_dh: Verify "dev" is a sdev before accessing it.
[safe/jmp/linux-2.6] / 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 static DEFINE_SPINLOCK(list_lock);
28 static LIST_HEAD(scsi_dh_list);
29
30 static struct scsi_device_handler *get_device_handler(const char *name)
31 {
32         struct scsi_device_handler *tmp, *found = NULL;
33
34         spin_lock(&list_lock);
35         list_for_each_entry(tmp, &scsi_dh_list, list) {
36                 if (!strcmp(tmp->name, name)) {
37                         found = tmp;
38                         break;
39                 }
40         }
41         spin_unlock(&list_lock);
42         return found;
43 }
44
45 static int scsi_dh_notifier_add(struct device *dev, void *data)
46 {
47         struct scsi_device_handler *scsi_dh = data;
48
49         scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_ADD_DEVICE, dev);
50         return 0;
51 }
52
53 /*
54  * scsi_register_device_handler - register a device handler personality
55  *      module.
56  * @scsi_dh - device handler to be registered.
57  *
58  * Returns 0 on success, -EBUSY if handler already registered.
59  */
60 int scsi_register_device_handler(struct scsi_device_handler *scsi_dh)
61 {
62         int ret = -EBUSY;
63         struct scsi_device_handler *tmp;
64
65         tmp = get_device_handler(scsi_dh->name);
66         if (tmp)
67                 goto done;
68
69         ret = bus_register_notifier(&scsi_bus_type, &scsi_dh->nb);
70
71         bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add);
72         spin_lock(&list_lock);
73         list_add(&scsi_dh->list, &scsi_dh_list);
74         spin_unlock(&list_lock);
75
76 done:
77         return ret;
78 }
79 EXPORT_SYMBOL_GPL(scsi_register_device_handler);
80
81 static int scsi_dh_notifier_remove(struct device *dev, void *data)
82 {
83         struct scsi_device_handler *scsi_dh = data;
84
85         scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_DEL_DEVICE, dev);
86         return 0;
87 }
88
89 /*
90  * scsi_unregister_device_handler - register a device handler personality
91  *      module.
92  * @scsi_dh - device handler to be unregistered.
93  *
94  * Returns 0 on success, -ENODEV if handler not registered.
95  */
96 int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
97 {
98         int ret = -ENODEV;
99         struct scsi_device_handler *tmp;
100
101         tmp = get_device_handler(scsi_dh->name);
102         if (!tmp)
103                 goto done;
104
105         ret = bus_unregister_notifier(&scsi_bus_type, &scsi_dh->nb);
106
107         bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh,
108                                         scsi_dh_notifier_remove);
109         spin_lock(&list_lock);
110         list_del(&scsi_dh->list);
111         spin_unlock(&list_lock);
112
113 done:
114         return ret;
115 }
116 EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
117
118 /*
119  * scsi_dh_activate - activate the path associated with the scsi_device
120  *      corresponding to the given request queue.
121  * @q - Request queue that is associated with the scsi_device to be
122  *      activated.
123  */
124 int scsi_dh_activate(struct request_queue *q)
125 {
126         int err = 0;
127         unsigned long flags;
128         struct scsi_device *sdev;
129         struct scsi_device_handler *scsi_dh = NULL;
130
131         spin_lock_irqsave(q->queue_lock, flags);
132         sdev = q->queuedata;
133         if (sdev && sdev->scsi_dh_data)
134                 scsi_dh = sdev->scsi_dh_data->scsi_dh;
135         if (!scsi_dh || !get_device(&sdev->sdev_gendev))
136                 err = SCSI_DH_NOSYS;
137         spin_unlock_irqrestore(q->queue_lock, flags);
138
139         if (err)
140                 return err;
141
142         if (scsi_dh->activate)
143                 err = scsi_dh->activate(sdev);
144         put_device(&sdev->sdev_gendev);
145         return err;
146 }
147 EXPORT_SYMBOL_GPL(scsi_dh_activate);
148
149 /*
150  * scsi_dh_handler_exist - Return TRUE(1) if a device handler exists for
151  *      the given name. FALSE(0) otherwise.
152  * @name - name of the device handler.
153  */
154 int scsi_dh_handler_exist(const char *name)
155 {
156         return (get_device_handler(name) != NULL);
157 }
158 EXPORT_SYMBOL_GPL(scsi_dh_handler_exist);
159
160 MODULE_DESCRIPTION("SCSI device handler");
161 MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>");
162 MODULE_LICENSE("GPL");