diff options
-rw-r--r-- | drivers/scsi/device_handler/scsi_dh.c | 129 |
1 files changed, 105 insertions, 24 deletions
diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index a9159681ff27..a518f2eff19a 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c | |||
@@ -24,8 +24,16 @@ | |||
24 | #include <scsi/scsi_dh.h> | 24 | #include <scsi/scsi_dh.h> |
25 | #include "../scsi_priv.h" | 25 | #include "../scsi_priv.h" |
26 | 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 | |||
27 | static DEFINE_SPINLOCK(list_lock); | 34 | static DEFINE_SPINLOCK(list_lock); |
28 | static LIST_HEAD(scsi_dh_list); | 35 | static LIST_HEAD(scsi_dh_list); |
36 | static LIST_HEAD(scsi_dh_dev_list); | ||
29 | 37 | ||
30 | static struct scsi_device_handler *get_device_handler(const char *name) | 38 | static struct scsi_device_handler *get_device_handler(const char *name) |
31 | { | 39 | { |
@@ -42,21 +50,94 @@ static struct scsi_device_handler *get_device_handler(const char *name) | |||
42 | return found; | 50 | return found; |
43 | } | 51 | } |
44 | 52 | ||
45 | static int device_handler_match(struct scsi_device_handler *tmp, | 53 | |
46 | struct scsi_device *sdev) | 54 | static struct scsi_device_handler * |
55 | scsi_dh_cache_lookup(struct scsi_device *sdev) | ||
47 | { | 56 | { |
48 | int i; | 57 | struct scsi_dh_devinfo_list *tmp; |
49 | 58 | struct scsi_device_handler *found_dh = NULL; | |
50 | for(i = 0; tmp->devlist[i].vendor; i++) { | 59 | |
51 | if (!strncmp(sdev->vendor, tmp->devlist[i].vendor, | 60 | spin_lock(&list_lock); |
52 | strlen(tmp->devlist[i].vendor)) && | 61 | list_for_each_entry(tmp, &scsi_dh_dev_list, node) { |
53 | !strncmp(sdev->model, tmp->devlist[i].model, | 62 | if (!strncmp(sdev->vendor, tmp->vendor, strlen(tmp->vendor)) && |
54 | strlen(tmp->devlist[i].model))) { | 63 | !strncmp(sdev->model, tmp->model, strlen(tmp->model))) { |
55 | return 1; | 64 | found_dh = tmp->handler; |
65 | break; | ||
56 | } | 66 | } |
57 | } | 67 | } |
68 | spin_unlock(&list_lock); | ||
58 | 69 | ||
59 | return 0; | 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; | ||
60 | } | 141 | } |
61 | 142 | ||
62 | /* | 143 | /* |
@@ -203,26 +284,18 @@ static int scsi_dh_notifier(struct notifier_block *nb, | |||
203 | struct device *dev = data; | 284 | struct device *dev = data; |
204 | struct scsi_device *sdev; | 285 | struct scsi_device *sdev; |
205 | int err = 0; | 286 | int err = 0; |
206 | struct scsi_device_handler *tmp, *devinfo = NULL; | 287 | struct scsi_device_handler *devinfo = NULL; |
207 | 288 | ||
208 | if (!scsi_is_sdev_device(dev)) | 289 | if (!scsi_is_sdev_device(dev)) |
209 | return 0; | 290 | return 0; |
210 | 291 | ||
211 | sdev = to_scsi_device(dev); | 292 | sdev = to_scsi_device(dev); |
212 | 293 | ||
213 | spin_lock(&list_lock); | ||
214 | list_for_each_entry(tmp, &scsi_dh_list, list) { | ||
215 | if (device_handler_match(tmp, sdev)) { | ||
216 | devinfo = tmp; | ||
217 | break; | ||
218 | } | ||
219 | } | ||
220 | spin_unlock(&list_lock); | ||
221 | |||
222 | if (!devinfo) | ||
223 | goto out; | ||
224 | |||
225 | if (action == BUS_NOTIFY_ADD_DEVICE) { | 294 | if (action == BUS_NOTIFY_ADD_DEVICE) { |
295 | devinfo = device_handler_match(NULL, sdev); | ||
296 | if (!devinfo) | ||
297 | goto out; | ||
298 | |||
226 | err = scsi_dh_handler_attach(sdev, devinfo); | 299 | err = scsi_dh_handler_attach(sdev, devinfo); |
227 | if (!err) | 300 | if (!err) |
228 | err = device_create_file(dev, &scsi_dh_state_attr); | 301 | err = device_create_file(dev, &scsi_dh_state_attr); |
@@ -312,6 +385,8 @@ EXPORT_SYMBOL_GPL(scsi_register_device_handler); | |||
312 | */ | 385 | */ |
313 | int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) | 386 | int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) |
314 | { | 387 | { |
388 | struct scsi_dh_devinfo_list *tmp, *pos; | ||
389 | |||
315 | if (!get_device_handler(scsi_dh->name)) | 390 | if (!get_device_handler(scsi_dh->name)) |
316 | return -ENODEV; | 391 | return -ENODEV; |
317 | 392 | ||
@@ -320,6 +395,12 @@ int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) | |||
320 | 395 | ||
321 | spin_lock(&list_lock); | 396 | spin_lock(&list_lock); |
322 | list_del(&scsi_dh->list); | 397 | list_del(&scsi_dh->list); |
398 | list_for_each_entry_safe(pos, tmp, &scsi_dh_dev_list, node) { | ||
399 | if (pos->handler == scsi_dh) { | ||
400 | list_del(&pos->node); | ||
401 | kfree(pos); | ||
402 | } | ||
403 | } | ||
323 | spin_unlock(&list_lock); | 404 | spin_unlock(&list_lock); |
324 | printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name); | 405 | printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name); |
325 | 406 | ||