diff options
author | James Bottomley <James.Bottomley@HansenPartnership.com> | 2009-07-31 20:39:36 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2009-08-22 18:52:13 -0400 |
commit | 163f52b6cf3a639df6a72c7937e0eb88b20f1ef3 (patch) | |
tree | 68991096e7ff15e31db28fb0d7bf2e87b1bd01c2 | |
parent | 0124ca9d8ee58b3cd028a23cef2fe225fcfee3b8 (diff) |
[SCSI] ses: fix hotplug with multiple devices and expanders
In a situation either with expanders or with multiple enclosure
devices, hot add doesn't always work. This is because we try to find
a single enclosure device attached to the host. Fix this by looping
over all enclosure devices attached to the host and also by making the
find loop recognise that the enclosure devices may be expander remote
(i.e. not parented by the host).
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
-rw-r--r-- | drivers/misc/enclosure.c | 44 | ||||
-rw-r--r-- | drivers/scsi/ses.c | 10 | ||||
-rw-r--r-- | include/linux/enclosure.h | 3 |
3 files changed, 40 insertions, 17 deletions
diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index 348443bdb23b..789d12128c24 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c | |||
@@ -33,24 +33,44 @@ static DEFINE_MUTEX(container_list_lock); | |||
33 | static struct class enclosure_class; | 33 | static struct class enclosure_class; |
34 | 34 | ||
35 | /** | 35 | /** |
36 | * enclosure_find - find an enclosure given a device | 36 | * enclosure_find - find an enclosure given a parent device |
37 | * @dev: the device to find for | 37 | * @dev: the parent to match against |
38 | * @start: Optional enclosure device to start from (NULL if none) | ||
38 | * | 39 | * |
39 | * Looks through the list of registered enclosures to see | 40 | * Looks through the list of registered enclosures to find all those |
40 | * if it can find a match for a device. Returns NULL if no | 41 | * with @dev as a parent. Returns NULL if no enclosure is |
41 | * enclosure is found. Obtains a reference to the enclosure class | 42 | * found. @start can be used as a starting point to obtain multiple |
42 | * device which must be released with device_put(). | 43 | * enclosures per parent (should begin with NULL and then be set to |
44 | * each returned enclosure device). Obtains a reference to the | ||
45 | * enclosure class device which must be released with device_put(). | ||
46 | * If @start is not NULL, a reference must be taken on it which is | ||
47 | * released before returning (this allows a loop through all | ||
48 | * enclosures to exit with only the reference on the enclosure of | ||
49 | * interest held). Note that the @dev may correspond to the actual | ||
50 | * device housing the enclosure, in which case no iteration via @start | ||
51 | * is required. | ||
43 | */ | 52 | */ |
44 | struct enclosure_device *enclosure_find(struct device *dev) | 53 | struct enclosure_device *enclosure_find(struct device *dev, |
54 | struct enclosure_device *start) | ||
45 | { | 55 | { |
46 | struct enclosure_device *edev; | 56 | struct enclosure_device *edev; |
47 | 57 | ||
48 | mutex_lock(&container_list_lock); | 58 | mutex_lock(&container_list_lock); |
49 | list_for_each_entry(edev, &container_list, node) { | 59 | edev = list_prepare_entry(start, &container_list, node); |
50 | if (edev->edev.parent == dev) { | 60 | if (start) |
51 | get_device(&edev->edev); | 61 | put_device(&start->edev); |
52 | mutex_unlock(&container_list_lock); | 62 | |
53 | return edev; | 63 | list_for_each_entry_continue(edev, &container_list, node) { |
64 | struct device *parent = edev->edev.parent; | ||
65 | /* parent might not be immediate, so iterate up to | ||
66 | * the root of the tree if necessary */ | ||
67 | while (parent) { | ||
68 | if (parent == dev) { | ||
69 | get_device(&edev->edev); | ||
70 | mutex_unlock(&container_list_lock); | ||
71 | return edev; | ||
72 | } | ||
73 | parent = parent->parent; | ||
54 | } | 74 | } |
55 | } | 75 | } |
56 | mutex_unlock(&container_list_lock); | 76 | mutex_unlock(&container_list_lock); |
diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 4f618f487356..e1b8c828f03a 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c | |||
@@ -413,10 +413,11 @@ static int ses_intf_add(struct device *cdev, | |||
413 | 413 | ||
414 | if (!scsi_device_enclosure(sdev)) { | 414 | if (!scsi_device_enclosure(sdev)) { |
415 | /* not an enclosure, but might be in one */ | 415 | /* not an enclosure, but might be in one */ |
416 | edev = enclosure_find(&sdev->host->shost_gendev); | 416 | struct enclosure_device *prev = NULL; |
417 | if (edev) { | 417 | |
418 | while ((edev = enclosure_find(&sdev->host->shost_gendev, prev)) != NULL) { | ||
418 | ses_match_to_enclosure(edev, sdev); | 419 | ses_match_to_enclosure(edev, sdev); |
419 | put_device(&edev->edev); | 420 | prev = edev; |
420 | } | 421 | } |
421 | return -ENODEV; | 422 | return -ENODEV; |
422 | } | 423 | } |
@@ -625,7 +626,8 @@ static void ses_intf_remove(struct device *cdev, | |||
625 | if (!scsi_device_enclosure(sdev)) | 626 | if (!scsi_device_enclosure(sdev)) |
626 | return; | 627 | return; |
627 | 628 | ||
628 | edev = enclosure_find(cdev->parent); | 629 | /* exact match to this enclosure */ |
630 | edev = enclosure_find(cdev->parent, NULL); | ||
629 | if (!edev) | 631 | if (!edev) |
630 | return; | 632 | return; |
631 | 633 | ||
diff --git a/include/linux/enclosure.h b/include/linux/enclosure.h index 4332442b1b57..d77811e9ed84 100644 --- a/include/linux/enclosure.h +++ b/include/linux/enclosure.h | |||
@@ -123,7 +123,8 @@ enclosure_component_register(struct enclosure_device *, unsigned int, | |||
123 | int enclosure_add_device(struct enclosure_device *enclosure, int component, | 123 | int enclosure_add_device(struct enclosure_device *enclosure, int component, |
124 | struct device *dev); | 124 | struct device *dev); |
125 | int enclosure_remove_device(struct enclosure_device *enclosure, int component); | 125 | int enclosure_remove_device(struct enclosure_device *enclosure, int component); |
126 | struct enclosure_device *enclosure_find(struct device *dev); | 126 | struct enclosure_device *enclosure_find(struct device *dev, |
127 | struct enclosure_device *start); | ||
127 | int enclosure_for_each_device(int (*fn)(struct enclosure_device *, void *), | 128 | int enclosure_for_each_device(int (*fn)(struct enclosure_device *, void *), |
128 | void *data); | 129 | void *data); |
129 | 130 | ||