]> git.ipfire.org Git - ipfire-2.x.git/blob - src/patches/suse-2.6.27.39/patches.fixes/scsi-ses-hotplug-fix
Imported linux-2.6.27.39 suse/xen patches.
[ipfire-2.x.git] / src / patches / suse-2.6.27.39 / patches.fixes / scsi-ses-hotplug-fix
1 From: James Bottomley <James.Bottomley@HansenPartnership.com>
2 Date: Sat, 1 Aug 2009 00:39:36 +0000
3 Subject: [PATCH] [SCSI] ses: fix hotplug with multiple devices and expanders
4 X-Git: 163f52b6cf3a639df6a72c7937e0eb88b20f1ef3
5 References: bnc#549751
6
7 In a situation either with expanders or with multiple enclosure
8 devices, hot add doesn't always work. This is because we try to find
9 a single enclosure device attached to the host. Fix this by looping
10 over all enclosure devices attached to the host and also by making the
11 find loop recognise that the enclosure devices may be expander remote
12 (i.e. not parented by the host).
13
14 Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
15 Signed-off-by: James Bottomley <James.Bottomley@suse.de>
16
17 diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c
18 index 348443b..789d121 100644
19 --- a/drivers/misc/enclosure.c
20 +++ b/drivers/misc/enclosure.c
21 @@ -33,24 +33,44 @@ static DEFINE_MUTEX(container_list_lock);
22 static struct class enclosure_class;
23
24 /**
25 - * enclosure_find - find an enclosure given a device
26 - * @dev: the device to find for
27 + * enclosure_find - find an enclosure given a parent device
28 + * @dev: the parent to match against
29 + * @start: Optional enclosure device to start from (NULL if none)
30 *
31 - * Looks through the list of registered enclosures to see
32 - * if it can find a match for a device. Returns NULL if no
33 - * enclosure is found. Obtains a reference to the enclosure class
34 - * device which must be released with device_put().
35 + * Looks through the list of registered enclosures to find all those
36 + * with @dev as a parent. Returns NULL if no enclosure is
37 + * found. @start can be used as a starting point to obtain multiple
38 + * enclosures per parent (should begin with NULL and then be set to
39 + * each returned enclosure device). Obtains a reference to the
40 + * enclosure class device which must be released with device_put().
41 + * If @start is not NULL, a reference must be taken on it which is
42 + * released before returning (this allows a loop through all
43 + * enclosures to exit with only the reference on the enclosure of
44 + * interest held). Note that the @dev may correspond to the actual
45 + * device housing the enclosure, in which case no iteration via @start
46 + * is required.
47 */
48 -struct enclosure_device *enclosure_find(struct device *dev)
49 +struct enclosure_device *enclosure_find(struct device *dev,
50 + struct enclosure_device *start)
51 {
52 struct enclosure_device *edev;
53
54 mutex_lock(&container_list_lock);
55 - list_for_each_entry(edev, &container_list, node) {
56 - if (edev->edev.parent == dev) {
57 - get_device(&edev->edev);
58 - mutex_unlock(&container_list_lock);
59 - return edev;
60 + edev = list_prepare_entry(start, &container_list, node);
61 + if (start)
62 + put_device(&start->edev);
63 +
64 + list_for_each_entry_continue(edev, &container_list, node) {
65 + struct device *parent = edev->edev.parent;
66 + /* parent might not be immediate, so iterate up to
67 + * the root of the tree if necessary */
68 + while (parent) {
69 + if (parent == dev) {
70 + get_device(&edev->edev);
71 + mutex_unlock(&container_list_lock);
72 + return edev;
73 + }
74 + parent = parent->parent;
75 }
76 }
77 mutex_unlock(&container_list_lock);
78 diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c
79 index 4f618f4..e1b8c82 100644
80 --- a/drivers/scsi/ses.c
81 +++ b/drivers/scsi/ses.c
82 @@ -413,10 +413,11 @@ static int ses_intf_add(struct device *cdev,
83
84 if (!scsi_device_enclosure(sdev)) {
85 /* not an enclosure, but might be in one */
86 - edev = enclosure_find(&sdev->host->shost_gendev);
87 - if (edev) {
88 + struct enclosure_device *prev = NULL;
89 +
90 + while ((edev = enclosure_find(&sdev->host->shost_gendev, prev)) != NULL) {
91 ses_match_to_enclosure(edev, sdev);
92 - put_device(&edev->edev);
93 + prev = edev;
94 }
95 return -ENODEV;
96 }
97 @@ -625,7 +626,8 @@ static void ses_intf_remove(struct device *cdev,
98 if (!scsi_device_enclosure(sdev))
99 return;
100
101 - edev = enclosure_find(cdev->parent);
102 + /* exact match to this enclosure */
103 + edev = enclosure_find(cdev->parent, NULL);
104 if (!edev)
105 return;
106
107 diff --git a/include/linux/enclosure.h b/include/linux/enclosure.h
108 index 4332442..d77811e 100644
109 --- a/include/linux/enclosure.h
110 +++ b/include/linux/enclosure.h
111 @@ -123,7 +123,8 @@ enclosure_component_register(struct enclosure_device *, unsigned int,
112 int enclosure_add_device(struct enclosure_device *enclosure, int component,
113 struct device *dev);
114 int enclosure_remove_device(struct enclosure_device *enclosure, int component);
115 -struct enclosure_device *enclosure_find(struct device *dev);
116 +struct enclosure_device *enclosure_find(struct device *dev,
117 + struct enclosure_device *start);
118 int enclosure_for_each_device(int (*fn)(struct enclosure_device *, void *),
119 void *data);
120