aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@HansenPartnership.com>2009-07-31 20:39:36 -0400
committerJames Bottomley <James.Bottomley@suse.de>2009-08-22 18:52:13 -0400
commit163f52b6cf3a639df6a72c7937e0eb88b20f1ef3 (patch)
tree68991096e7ff15e31db28fb0d7bf2e87b1bd01c2
parent0124ca9d8ee58b3cd028a23cef2fe225fcfee3b8 (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.c44
-rw-r--r--drivers/scsi/ses.c10
-rw-r--r--include/linux/enclosure.h3
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);
33static struct class enclosure_class; 33static 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 */
44struct enclosure_device *enclosure_find(struct device *dev) 53struct 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,
123int enclosure_add_device(struct enclosure_device *enclosure, int component, 123int enclosure_add_device(struct enclosure_device *enclosure, int component,
124 struct device *dev); 124 struct device *dev);
125int enclosure_remove_device(struct enclosure_device *enclosure, int component); 125int enclosure_remove_device(struct enclosure_device *enclosure, int component);
126struct enclosure_device *enclosure_find(struct device *dev); 126struct enclosure_device *enclosure_find(struct device *dev,
127 struct enclosure_device *start);
127int enclosure_for_each_device(int (*fn)(struct enclosure_device *, void *), 128int enclosure_for_each_device(int (*fn)(struct enclosure_device *, void *),
128 void *data); 129 void *data);
129 130