diff options
-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 348443b..789d121 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -33,24 +33,44 @@ static DEFINE_MUTEX(container_list_lock); static struct class enclosure_class; /** - * enclosure_find - find an enclosure given a device - * @dev: the device to find for + * enclosure_find - find an enclosure given a parent device + * @dev: the parent to match against + * @start: Optional enclosure device to start from (NULL if none) * - * Looks through the list of registered enclosures to see - * if it can find a match for a device. Returns NULL if no - * enclosure is found. Obtains a reference to the enclosure class - * device which must be released with device_put(). + * Looks through the list of registered enclosures to find all those + * with @dev as a parent. Returns NULL if no enclosure is + * found. @start can be used as a starting point to obtain multiple + * enclosures per parent (should begin with NULL and then be set to + * each returned enclosure device). Obtains a reference to the + * enclosure class device which must be released with device_put(). + * If @start is not NULL, a reference must be taken on it which is + * released before returning (this allows a loop through all + * enclosures to exit with only the reference on the enclosure of + * interest held). Note that the @dev may correspond to the actual + * device housing the enclosure, in which case no iteration via @start + * is required. */ -struct enclosure_device *enclosure_find(struct device *dev) +struct enclosure_device *enclosure_find(struct device *dev, + struct enclosure_device *start) { struct enclosure_device *edev; mutex_lock(&container_list_lock); - list_for_each_entry(edev, &container_list, node) { - if (edev->edev.parent == dev) { - get_device(&edev->edev); - mutex_unlock(&container_list_lock); - return edev; + edev = list_prepare_entry(start, &container_list, node); + if (start) + put_device(&start->edev); + + list_for_each_entry_continue(edev, &container_list, node) { + struct device *parent = edev->edev.parent; + /* parent might not be immediate, so iterate up to + * the root of the tree if necessary */ + while (parent) { + if (parent == dev) { + get_device(&edev->edev); + mutex_unlock(&container_list_lock); + return edev; + } + parent = parent->parent; } } mutex_unlock(&container_list_lock); diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 4f618f4..e1b8c82 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -413,10 +413,11 @@ static int ses_intf_add(struct device *cdev, if (!scsi_device_enclosure(sdev)) { /* not an enclosure, but might be in one */ - edev = enclosure_find(&sdev->host->shost_gendev); - if (edev) { + struct enclosure_device *prev = NULL; + + while ((edev = enclosure_find(&sdev->host->shost_gendev, prev)) != NULL) { ses_match_to_enclosure(edev, sdev); - put_device(&edev->edev); + prev = edev; } return -ENODEV; } @@ -625,7 +626,8 @@ static void ses_intf_remove(struct device *cdev, if (!scsi_device_enclosure(sdev)) return; - edev = enclosure_find(cdev->parent); + /* exact match to this enclosure */ + edev = enclosure_find(cdev->parent, NULL); if (!edev) return; diff --git a/include/linux/enclosure.h b/include/linux/enclosure.h index 4332442..d77811e 100644 --- a/include/linux/enclosure.h +++ b/include/linux/enclosure.h @@ -123,7 +123,8 @@ enclosure_component_register(struct enclosure_device *, unsigned int, int enclosure_add_device(struct enclosure_device *enclosure, int component, struct device *dev); int enclosure_remove_device(struct enclosure_device *enclosure, int component); -struct enclosure_device *enclosure_find(struct device *dev); +struct enclosure_device *enclosure_find(struct device *dev, + struct enclosure_device *start); int enclosure_for_each_device(int (*fn)(struct enclosure_device *, void *), void *data); |