diff options
author | Hannes Reinecke <hare@suse.de> | 2008-07-17 19:52:51 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-07-26 15:14:51 -0400 |
commit | 765cbc6dad16b87724803e359d6be792ddf08614 (patch) | |
tree | 2cedfbe6b55c9f7a3e4cc3fb4f0d1f4d9d18f625 /drivers/scsi/device_handler/scsi_dh.c | |
parent | 6d49f63b415ca02223e01e187076cb69a5a38eaf (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.c | 202 |
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 | ||
45 | static int scsi_dh_notifier_add(struct device *dev, void *data) | 45 | static 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 | */ | ||
67 | static 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 | */ |
60 | int scsi_register_device_handler(struct scsi_device_handler *scsi_dh) | 89 | static 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 | */ | ||
108 | static 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 | ||
76 | done: | 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 | } | ||
138 | out: | ||
139 | return err; | ||
78 | } | 140 | } |
79 | EXPORT_SYMBOL_GPL(scsi_register_device_handler); | ||
80 | 141 | ||
142 | /* | ||
143 | * scsi_dh_notifier_add - Callback for scsi_register_device_handler | ||
144 | */ | ||
145 | static 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 | */ | ||
81 | static int scsi_dh_notifier_remove(struct device *dev, void *data) | 169 | static 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 | */ | ||
196 | int 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 | } | ||
209 | EXPORT_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 | */ |
96 | int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) | 218 | int 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 | ||
113 | done: | 231 | return SCSI_DH_OK; |
114 | return ret; | ||
115 | } | 232 | } |
116 | EXPORT_SYMBOL_GPL(scsi_unregister_device_handler); | 233 | EXPORT_SYMBOL_GPL(scsi_unregister_device_handler); |
117 | 234 | ||
@@ -157,6 +274,27 @@ int scsi_dh_handler_exist(const char *name) | |||
157 | } | 274 | } |
158 | EXPORT_SYMBOL_GPL(scsi_dh_handler_exist); | 275 | EXPORT_SYMBOL_GPL(scsi_dh_handler_exist); |
159 | 276 | ||
277 | static struct notifier_block scsi_dh_nb = { | ||
278 | .notifier_call = scsi_dh_notifier | ||
279 | }; | ||
280 | |||
281 | static 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 | |||
290 | static void __exit scsi_dh_exit(void) | ||
291 | { | ||
292 | bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb); | ||
293 | } | ||
294 | |||
295 | module_init(scsi_dh_init); | ||
296 | module_exit(scsi_dh_exit); | ||
297 | |||
160 | MODULE_DESCRIPTION("SCSI device handler"); | 298 | MODULE_DESCRIPTION("SCSI device handler"); |
161 | MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>"); | 299 | MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>"); |
162 | MODULE_LICENSE("GPL"); | 300 | MODULE_LICENSE("GPL"); |