aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/device_handler/scsi_dh.c
diff options
context:
space:
mode:
authorHannes Reinecke <hare@suse.de>2008-07-17 19:52:51 -0400
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2008-07-26 15:14:51 -0400
commit765cbc6dad16b87724803e359d6be792ddf08614 (patch)
tree2cedfbe6b55c9f7a3e4cc3fb4f0d1f4d9d18f625 /drivers/scsi/device_handler/scsi_dh.c
parent6d49f63b415ca02223e01e187076cb69a5a38eaf (diff)
[SCSI] scsi_dh: Implement common device table handling
Instead of having each and every driver implement its own device table scanning code we should rather implement a common routine and scan the device tables there. This allows us also to implement a general notifier chain callback for all device handler instead for one per handler. [sekharan: Fix rejections caused by conflicting bug fix] Signed-off-by: Hannes Reinecke <hare@suse.de> Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi/device_handler/scsi_dh.c')
-rw-r--r--drivers/scsi/device_handler/scsi_dh.c202
1 files changed, 170 insertions, 32 deletions
diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c
index ab6c21cd9689..3f798171ed69 100644
--- a/drivers/scsi/device_handler/scsi_dh.c
+++ b/drivers/scsi/device_handler/scsi_dh.c
@@ -33,7 +33,7 @@ static struct scsi_device_handler *get_device_handler(const char *name)
33 33
34 spin_lock(&list_lock); 34 spin_lock(&list_lock);
35 list_for_each_entry(tmp, &scsi_dh_list, list) { 35 list_for_each_entry(tmp, &scsi_dh_list, list) {
36 if (!strcmp(tmp->name, name)) { 36 if (!strncmp(tmp->name, name, strlen(tmp->name))) {
37 found = tmp; 37 found = tmp;
38 break; 38 break;
39 } 39 }
@@ -42,51 +42,173 @@ static struct scsi_device_handler *get_device_handler(const char *name)
42 return found; 42 return found;
43} 43}
44 44
45static int scsi_dh_notifier_add(struct device *dev, void *data) 45static int device_handler_match(struct scsi_device_handler *tmp,
46 struct scsi_device *sdev)
46{ 47{
47 struct scsi_device_handler *scsi_dh = data; 48 int i;
49
50 for(i = 0; tmp->devlist[i].vendor; i++) {
51 if (!strncmp(sdev->vendor, tmp->devlist[i].vendor,
52 strlen(tmp->devlist[i].vendor)) &&
53 !strncmp(sdev->model, tmp->devlist[i].model,
54 strlen(tmp->devlist[i].model))) {
55 return 1;
56 }
57 }
48 58
49 scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_ADD_DEVICE, dev);
50 return 0; 59 return 0;
51} 60}
52 61
53/* 62/*
54 * scsi_register_device_handler - register a device handler personality 63 * scsi_dh_handler_attach - Attach a device handler to a device
55 * module. 64 * @sdev - SCSI device the device handler should attach to
56 * @scsi_dh - device handler to be registered. 65 * @scsi_dh - The device handler to attach
66 */
67static int scsi_dh_handler_attach(struct scsi_device *sdev,
68 struct scsi_device_handler *scsi_dh)
69{
70 int err = 0;
71
72 if (sdev->scsi_dh_data) {
73 if (sdev->scsi_dh_data->scsi_dh != scsi_dh)
74 err = -EBUSY;
75 } else if (scsi_dh->attach)
76 err = scsi_dh->attach(sdev);
77
78 return err;
79}
80
81/*
82 * scsi_dh_handler_detach - Detach a device handler from a device
83 * @sdev - SCSI device the device handler should be detached from
84 * @scsi_dh - Device handler to be detached
57 * 85 *
58 * Returns 0 on success, -EBUSY if handler already registered. 86 * Detach from a device handler. If a device handler is specified,
87 * only detach if the currently attached handler is equal to it.
59 */ 88 */
60int scsi_register_device_handler(struct scsi_device_handler *scsi_dh) 89static void scsi_dh_handler_detach(struct scsi_device *sdev,
90 struct scsi_device_handler *scsi_dh)
61{ 91{
62 int ret = -EBUSY; 92 if (!sdev->scsi_dh_data)
63 struct scsi_device_handler *tmp; 93 return;
64 94
65 tmp = get_device_handler(scsi_dh->name); 95 if (scsi_dh && scsi_dh != sdev->scsi_dh_data->scsi_dh)
66 if (tmp) 96 return;
67 goto done;
68 97
69 ret = bus_register_notifier(&scsi_bus_type, &scsi_dh->nb); 98 if (!scsi_dh)
99 scsi_dh = sdev->scsi_dh_data->scsi_dh;
100
101 if (scsi_dh && scsi_dh->detach)
102 scsi_dh->detach(sdev);
103}
104
105/*
106 * scsi_dh_notifier - notifier chain callback
107 */
108static int scsi_dh_notifier(struct notifier_block *nb,
109 unsigned long action, void *data)
110{
111 struct device *dev = data;
112 struct scsi_device *sdev;
113 int err = 0;
114 struct scsi_device_handler *tmp, *devinfo = NULL;
115
116 if (!scsi_is_sdev_device(dev))
117 return 0;
118
119 sdev = to_scsi_device(dev);
70 120
71 bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add);
72 spin_lock(&list_lock); 121 spin_lock(&list_lock);
73 list_add(&scsi_dh->list, &scsi_dh_list); 122 list_for_each_entry(tmp, &scsi_dh_list, list) {
123 if (device_handler_match(tmp, sdev)) {
124 devinfo = tmp;
125 break;
126 }
127 }
74 spin_unlock(&list_lock); 128 spin_unlock(&list_lock);
75 129
76done: 130 if (!devinfo)
77 return ret; 131 goto out;
132
133 if (action == BUS_NOTIFY_ADD_DEVICE) {
134 err = scsi_dh_handler_attach(sdev, devinfo);
135 } else if (action == BUS_NOTIFY_DEL_DEVICE) {
136 scsi_dh_handler_detach(sdev, NULL);
137 }
138out:
139 return err;
78} 140}
79EXPORT_SYMBOL_GPL(scsi_register_device_handler);
80 141
142/*
143 * scsi_dh_notifier_add - Callback for scsi_register_device_handler
144 */
145static int scsi_dh_notifier_add(struct device *dev, void *data)
146{
147 struct scsi_device_handler *scsi_dh = data;
148 struct scsi_device *sdev;
149
150 if (!scsi_is_sdev_device(dev))
151 return 0;
152
153 if (!get_device(dev))
154 return 0;
155
156 sdev = to_scsi_device(dev);
157
158 if (device_handler_match(scsi_dh, sdev))
159 scsi_dh_handler_attach(sdev, scsi_dh);
160
161 put_device(dev);
162
163 return 0;
164}
165
166/*
167 * scsi_dh_notifier_remove - Callback for scsi_unregister_device_handler
168 */
81static int scsi_dh_notifier_remove(struct device *dev, void *data) 169static int scsi_dh_notifier_remove(struct device *dev, void *data)
82{ 170{
83 struct scsi_device_handler *scsi_dh = data; 171 struct scsi_device_handler *scsi_dh = data;
172 struct scsi_device *sdev;
173
174 if (!scsi_is_sdev_device(dev))
175 return 0;
176
177 if (!get_device(dev))
178 return 0;
179
180 sdev = to_scsi_device(dev);
181
182 scsi_dh_handler_detach(sdev, scsi_dh);
183
184 put_device(dev);
84 185
85 scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_DEL_DEVICE, dev);
86 return 0; 186 return 0;
87} 187}
88 188
89/* 189/*
190 * scsi_register_device_handler - register a device handler personality
191 * module.
192 * @scsi_dh - device handler to be registered.
193 *
194 * Returns 0 on success, -EBUSY if handler already registered.
195 */
196int scsi_register_device_handler(struct scsi_device_handler *scsi_dh)
197{
198 if (get_device_handler(scsi_dh->name))
199 return -EBUSY;
200
201 spin_lock(&list_lock);
202 list_add(&scsi_dh->list, &scsi_dh_list);
203 spin_unlock(&list_lock);
204 bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add);
205 printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name);
206
207 return SCSI_DH_OK;
208}
209EXPORT_SYMBOL_GPL(scsi_register_device_handler);
210
211/*
90 * scsi_unregister_device_handler - register a device handler personality 212 * scsi_unregister_device_handler - register a device handler personality
91 * module. 213 * module.
92 * @scsi_dh - device handler to be unregistered. 214 * @scsi_dh - device handler to be unregistered.
@@ -95,23 +217,18 @@ static int scsi_dh_notifier_remove(struct device *dev, void *data)
95 */ 217 */
96int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) 218int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
97{ 219{
98 int ret = -ENODEV; 220 if (!get_device_handler(scsi_dh->name))
99 struct scsi_device_handler *tmp; 221 return -ENODEV;
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 222
107 bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, 223 bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh,
108 scsi_dh_notifier_remove); 224 scsi_dh_notifier_remove);
225
109 spin_lock(&list_lock); 226 spin_lock(&list_lock);
110 list_del(&scsi_dh->list); 227 list_del(&scsi_dh->list);
111 spin_unlock(&list_lock); 228 spin_unlock(&list_lock);
229 printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name);
112 230
113done: 231 return SCSI_DH_OK;
114 return ret;
115} 232}
116EXPORT_SYMBOL_GPL(scsi_unregister_device_handler); 233EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
117 234
@@ -157,6 +274,27 @@ int scsi_dh_handler_exist(const char *name)
157} 274}
158EXPORT_SYMBOL_GPL(scsi_dh_handler_exist); 275EXPORT_SYMBOL_GPL(scsi_dh_handler_exist);
159 276
277static struct notifier_block scsi_dh_nb = {
278 .notifier_call = scsi_dh_notifier
279};
280
281static int __init scsi_dh_init(void)
282{
283 int r;
284
285 r = bus_register_notifier(&scsi_bus_type, &scsi_dh_nb);
286
287 return r;
288}
289
290static void __exit scsi_dh_exit(void)
291{
292 bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb);
293}
294
295module_init(scsi_dh_init);
296module_exit(scsi_dh_exit);
297
160MODULE_DESCRIPTION("SCSI device handler"); 298MODULE_DESCRIPTION("SCSI device handler");
161MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>"); 299MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>");
162MODULE_LICENSE("GPL"); 300MODULE_LICENSE("GPL");