aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAlex Chiang <achiang@hp.com>2008-10-20 19:41:02 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2008-10-22 19:42:39 -0400
commit5fe6cc60680d29740b85278e17a002fa27b7e642 (patch)
tree3815152b48be7f02bb13b765ea8784ff60bfffcb /drivers
parent95cb9093960b6249fdbe7417bf513a1358aaa51a (diff)
PCI: prevent duplicate slot names
Prevent callers of pci_create_slot() from registering slots with duplicate names. This condition occurs most often when PCI hotplug drivers are loaded on platforms with broken firmware that assigns identical names to multiple slots. We now rename these duplicate slots on behalf of the user. If firmware assigns the name N to multiple slots, then: The first registered slot is assigned N The second registered slot is assigned N-1 The third registered slot is assigned N-2 etc. This is the permanent fix mentioned in earlier commits d6a9e9b4 and 167e782e (shpchp/pciehp: Rename duplicate slot name...). We take advantage of the new 'hotplug' parameter in pci_create_slot() to prevent a slot create/rename race between hotplug drivers and detection drivers. Scenario A: hotplug driver detection driver -------------- ---------------- pci_create_slot(hotplug=set) pci_create_slot(hotplug=NULL) The hotplug driver creates the slot with its desired name, and then releases the semaphore. Now, the detection driver tries to create the same slot, but it already exists. We don't care about renaming, so return the existing slot. Scenario B: hotplug driver detection driver -------------- ---------------- pci_create_slot(hotplug=NULL) pci_create_slot(hotplug=set) The detection driver creates the slot with name "X". Then the hotplug driver tries to create the same slot, but wants the name "Y" instead. We detect that we're trying to create the same slot and that we also want a rename, so rename the slot to "Y" and return. Scenario C: hotplug driver hotplug driver -------------- ---------------- pci_create_slot(hotplug=set) pci_create_slot(hotplug=set) Two separate hotplug drivers are attempting to claim the slot and are passing valid hotplug_slot args to pci_create_slot(). We detect that the slot already has a ->hotplug callback, prevent a rename, and return -EBUSY. Cc: kristen.c.accardi@intel.com Cc: matthew@wil.cx Acked-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> Signed-off-by: Alex Chiang <achiang@hp.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/pci/hotplug/pci_hotplug_core.c26
-rw-r--r--drivers/pci/hotplug/pciehp_core.c15
-rw-r--r--drivers/pci/hotplug/shpchp_core.c15
-rw-r--r--drivers/pci/slot.c141
4 files changed, 115 insertions, 82 deletions
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index e71524825180..a6f1f282b683 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -569,12 +569,6 @@ int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr,
569 569
570 mutex_lock(&pci_hp_mutex); 570 mutex_lock(&pci_hp_mutex);
571 571
572 /* Check if we have already registered a slot with the same name. */
573 if (get_slot_from_name(name)) {
574 result = -EEXIST;
575 goto out;
576 }
577
578 /* 572 /*
579 * No problems if we call this interface from both ACPI_PCI_SLOT 573 * No problems if we call this interface from both ACPI_PCI_SLOT
580 * driver and call it here again. If we've already created the 574 * driver and call it here again. If we've already created the
@@ -583,27 +577,12 @@ int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr,
583 pci_slot = pci_create_slot(bus, slot_nr, name, slot); 577 pci_slot = pci_create_slot(bus, slot_nr, name, slot);
584 if (IS_ERR(pci_slot)) { 578 if (IS_ERR(pci_slot)) {
585 result = PTR_ERR(pci_slot); 579 result = PTR_ERR(pci_slot);
586 goto cleanup; 580 goto out;
587 }
588
589 if (pci_slot->hotplug) {
590 dbg("%s: already claimed\n", __func__);
591 result = -EBUSY;
592 goto cleanup;
593 } 581 }
594 582
595 slot->pci_slot = pci_slot; 583 slot->pci_slot = pci_slot;
596 pci_slot->hotplug = slot; 584 pci_slot->hotplug = slot;
597 585
598 /*
599 * Allow pcihp drivers to override the ACPI_PCI_SLOT name.
600 */
601 if (strcmp(kobject_name(&pci_slot->kobj), name)) {
602 result = kobject_rename(&pci_slot->kobj, name);
603 if (result)
604 goto cleanup;
605 }
606
607 list_add(&slot->slot_list, &pci_hotplug_slot_list); 586 list_add(&slot->slot_list, &pci_hotplug_slot_list);
608 587
609 result = fs_add_slot(pci_slot); 588 result = fs_add_slot(pci_slot);
@@ -612,9 +591,6 @@ int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr,
612out: 591out:
613 mutex_unlock(&pci_hp_mutex); 592 mutex_unlock(&pci_hp_mutex);
614 return result; 593 return result;
615cleanup:
616 pci_destroy_slot(pci_slot);
617 goto out;
618} 594}
619 595
620/** 596/**
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 3ace5e057601..af89d7bd1edd 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -196,7 +196,6 @@ static int init_slots(struct controller *ctrl)
196 struct slot *slot; 196 struct slot *slot;
197 struct hotplug_slot *hotplug_slot; 197 struct hotplug_slot *hotplug_slot;
198 struct hotplug_slot_info *info; 198 struct hotplug_slot_info *info;
199 int len, dup = 1;
200 int retval = -ENOMEM; 199 int retval = -ENOMEM;
201 200
202 list_for_each_entry(slot, &ctrl->slot_list, slot_list) { 201 list_for_each_entry(slot, &ctrl->slot_list, slot_list) {
@@ -223,25 +222,11 @@ static int init_slots(struct controller *ctrl)
223 ctrl_dbg(ctrl, "Registering bus=%x dev=%x hp_slot=%x sun=%x " 222 ctrl_dbg(ctrl, "Registering bus=%x dev=%x hp_slot=%x sun=%x "
224 "slot_device_offset=%x\n", slot->bus, slot->device, 223 "slot_device_offset=%x\n", slot->bus, slot->device,
225 slot->hp_slot, slot->number, ctrl->slot_device_offset); 224 slot->hp_slot, slot->number, ctrl->slot_device_offset);
226duplicate_name:
227 retval = pci_hp_register(hotplug_slot, 225 retval = pci_hp_register(hotplug_slot,
228 ctrl->pci_dev->subordinate, 226 ctrl->pci_dev->subordinate,
229 slot->device, 227 slot->device,
230 slot->name); 228 slot->name);
231 if (retval) { 229 if (retval) {
232 /*
233 * If slot N already exists, we'll try to create
234 * slot N-1, N-2 ... N-M, until we overflow.
235 */
236 if (retval == -EEXIST) {
237 len = snprintf(slot->name, SLOT_NAME_SIZE,
238 "%d-%d", slot->number, dup++);
239 if (len < SLOT_NAME_SIZE)
240 goto duplicate_name;
241 else
242 ctrl_err(ctrl, "duplicate slot name "
243 "overflow\n");
244 }
245 ctrl_err(ctrl, "pci_hp_register failed with error %d\n", 230 ctrl_err(ctrl, "pci_hp_register failed with error %d\n",
246 retval); 231 retval);
247 goto error_info; 232 goto error_info;
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c
index bf5096612aab..cfdd07963641 100644
--- a/drivers/pci/hotplug/shpchp_core.c
+++ b/drivers/pci/hotplug/shpchp_core.c
@@ -102,7 +102,7 @@ static int init_slots(struct controller *ctrl)
102 struct hotplug_slot *hotplug_slot; 102 struct hotplug_slot *hotplug_slot;
103 struct hotplug_slot_info *info; 103 struct hotplug_slot_info *info;
104 int retval = -ENOMEM; 104 int retval = -ENOMEM;
105 int i, len, dup = 1; 105 int i;
106 106
107 for (i = 0; i < ctrl->num_slots; i++) { 107 for (i = 0; i < ctrl->num_slots; i++) {
108 slot = kzalloc(sizeof(*slot), GFP_KERNEL); 108 slot = kzalloc(sizeof(*slot), GFP_KERNEL);
@@ -144,23 +144,10 @@ static int init_slots(struct controller *ctrl)
144 dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x " 144 dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x "
145 "slot_device_offset=%x\n", slot->bus, slot->device, 145 "slot_device_offset=%x\n", slot->bus, slot->device,
146 slot->hp_slot, slot->number, ctrl->slot_device_offset); 146 slot->hp_slot, slot->number, ctrl->slot_device_offset);
147duplicate_name:
148 retval = pci_hp_register(slot->hotplug_slot, 147 retval = pci_hp_register(slot->hotplug_slot,
149 ctrl->pci_dev->subordinate, slot->device, 148 ctrl->pci_dev->subordinate, slot->device,
150 hotplug_slot->name); 149 hotplug_slot->name);
151 if (retval) { 150 if (retval) {
152 /*
153 * If slot N already exists, we'll try to create
154 * slot N-1, N-2 ... N-M, until we overflow.
155 */
156 if (retval == -EEXIST) {
157 len = snprintf(slot->name, SLOT_NAME_SIZE,
158 "%d-%d", slot->number, dup++);
159 if (len < SLOT_NAME_SIZE)
160 goto duplicate_name;
161 else
162 err("duplicate slot name overflow\n");
163 }
164 err("pci_hp_register failed with error %d\n", retval); 151 err("pci_hp_register failed with error %d\n", retval);
165 goto error_info; 152 goto error_info;
166 } 153 }
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
index 0e009c3ba5fd..b6ee352ae459 100644
--- a/drivers/pci/slot.c
+++ b/drivers/pci/slot.c
@@ -78,6 +78,77 @@ static struct kobj_type pci_slot_ktype = {
78 .default_attrs = pci_slot_default_attrs, 78 .default_attrs = pci_slot_default_attrs,
79}; 79};
80 80
81static char *make_slot_name(const char *name)
82{
83 char *new_name;
84 int len, max, dup;
85
86 new_name = kstrdup(name, GFP_KERNEL);
87 if (!new_name)
88 return NULL;
89
90 /*
91 * Make sure we hit the realloc case the first time through the
92 * loop. 'len' will be strlen(name) + 3 at that point which is
93 * enough space for "name-X" and the trailing NUL.
94 */
95 len = strlen(name) + 2;
96 max = 1;
97 dup = 1;
98
99 for (;;) {
100 struct kobject *dup_slot;
101 dup_slot = kset_find_obj(pci_slots_kset, new_name);
102 if (!dup_slot)
103 break;
104 kobject_put(dup_slot);
105 if (dup == max) {
106 len++;
107 max *= 10;
108 kfree(new_name);
109 new_name = kmalloc(len, GFP_KERNEL);
110 if (!new_name)
111 break;
112 }
113 sprintf(new_name, "%s-%d", name, dup++);
114 }
115
116 return new_name;
117}
118
119static int rename_slot(struct pci_slot *slot, const char *name)
120{
121 int result = 0;
122 char *slot_name;
123
124 if (strcmp(kobject_name(&slot->kobj), name) == 0)
125 return result;
126
127 slot_name = make_slot_name(name);
128 if (!slot_name)
129 return -ENOMEM;
130
131 result = kobject_rename(&slot->kobj, slot_name);
132 kfree(slot_name);
133
134 return result;
135}
136
137static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr)
138{
139 struct pci_slot *slot;
140 /*
141 * We already hold pci_bus_sem so don't worry
142 */
143 list_for_each_entry(slot, &parent->slots, list)
144 if (slot->number == slot_nr) {
145 kobject_get(&slot->kobj);
146 return slot;
147 }
148
149 return NULL;
150}
151
81/** 152/**
82 * pci_create_slot - create or increment refcount for physical PCI slot 153 * pci_create_slot - create or increment refcount for physical PCI slot
83 * @parent: struct pci_bus of parent bridge 154 * @parent: struct pci_bus of parent bridge
@@ -90,7 +161,17 @@ static struct kobj_type pci_slot_ktype = {
90 * either return a new &struct pci_slot to the caller, or if the pci_slot 161 * either return a new &struct pci_slot to the caller, or if the pci_slot
91 * already exists, its refcount will be incremented. 162 * already exists, its refcount will be incremented.
92 * 163 *
93 * Slots are uniquely identified by a @pci_bus, @slot_nr, @name tuple. 164 * Slots are uniquely identified by a @pci_bus, @slot_nr tuple.
165 *
166 * There are known platforms with broken firmware that assign the same
167 * name to multiple slots. Workaround these broken platforms by renaming
168 * the slots on behalf of the caller. If firmware assigns name N to
169 * multiple slots:
170 *
171 * The first slot is assigned N
172 * The second slot is assigned N-1
173 * The third slot is assigned N-2
174 * etc.
94 * 175 *
95 * Placeholder slots: 176 * Placeholder slots:
96 * In most cases, @pci_bus, @slot_nr will be sufficient to uniquely identify 177 * In most cases, @pci_bus, @slot_nr will be sufficient to uniquely identify
@@ -99,62 +180,67 @@ static struct kobj_type pci_slot_ktype = {
99 * the slot. In this scenario, the caller may pass -1 for @slot_nr. 180 * the slot. In this scenario, the caller may pass -1 for @slot_nr.
100 * 181 *
101 * The following semantics are imposed when the caller passes @slot_nr == 182 * The following semantics are imposed when the caller passes @slot_nr ==
102 * -1. First, the check for existing %struct pci_slot is skipped, as the 183 * -1. First, we no longer check for an existing %struct pci_slot, as there
103 * caller may know about several unpopulated slots on a given %struct 184 * may be many slots with @slot_nr of -1. The other change in semantics is
104 * pci_bus, and each slot would have a @slot_nr of -1. Uniqueness for
105 * these slots is then determined by the @name parameter. We expect
106 * kobject_init_and_add() to warn us if the caller attempts to create
107 * multiple slots with the same name. The other change in semantics is
108 * user-visible, which is the 'address' parameter presented in sysfs will 185 * user-visible, which is the 'address' parameter presented in sysfs will
109 * consist solely of a dddd:bb tuple, where dddd is the PCI domain of the 186 * consist solely of a dddd:bb tuple, where dddd is the PCI domain of the
110 * %struct pci_bus and bb is the bus number. In other words, the devfn of 187 * %struct pci_bus and bb is the bus number. In other words, the devfn of
111 * the 'placeholder' slot will not be displayed. 188 * the 'placeholder' slot will not be displayed.
112 */ 189 */
113
114struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, 190struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr,
115 const char *name, 191 const char *name,
116 struct hotplug_slot *hotplug) 192 struct hotplug_slot *hotplug)
117{ 193{
118 struct pci_dev *dev; 194 struct pci_dev *dev;
119 struct pci_slot *slot; 195 struct pci_slot *slot;
120 int err; 196 int err = 0;
197 char *slot_name = NULL;
121 198
122 down_write(&pci_bus_sem); 199 down_write(&pci_bus_sem);
123 200
124 if (slot_nr == -1) 201 if (slot_nr == -1)
125 goto placeholder; 202 goto placeholder;
126 203
127 /* If we've already created this slot, bump refcount and return. */ 204 /*
128 list_for_each_entry(slot, &parent->slots, list) { 205 * Hotplug drivers are allowed to rename an existing slot,
129 if (slot->number == slot_nr) { 206 * but only if not already claimed.
130 kobject_get(&slot->kobj); 207 */
131 pr_debug("%s: inc refcount to %d on %04x:%02x:%02x\n", 208 slot = get_slot(parent, slot_nr);
132 __func__, 209 if (slot) {
133 atomic_read(&slot->kobj.kref.refcount), 210 if (hotplug) {
134 pci_domain_nr(parent), parent->number, 211 if ((err = slot->hotplug ? -EBUSY : 0)
135 slot_nr); 212 || (err = rename_slot(slot, name))) {
136 goto out; 213 kobject_put(&slot->kobj);
214 slot = NULL;
215 goto err;
216 }
137 } 217 }
218 goto out;
138 } 219 }
139 220
140placeholder: 221placeholder:
141 slot = kzalloc(sizeof(*slot), GFP_KERNEL); 222 slot = kzalloc(sizeof(*slot), GFP_KERNEL);
142 if (!slot) { 223 if (!slot) {
143 slot = ERR_PTR(-ENOMEM); 224 err = -ENOMEM;
144 goto out; 225 goto err;
145 } 226 }
146 227
147 slot->bus = parent; 228 slot->bus = parent;
148 slot->number = slot_nr; 229 slot->number = slot_nr;
149 230
150 slot->kobj.kset = pci_slots_kset; 231 slot->kobj.kset = pci_slots_kset;
151 err = kobject_init_and_add(&slot->kobj, &pci_slot_ktype, NULL, 232
152 "%s", name); 233 slot_name = make_slot_name(name);
153 if (err) { 234 if (!slot_name) {
154 printk(KERN_ERR "Unable to register kobject %s\n", name); 235 err = -ENOMEM;
155 goto err; 236 goto err;
156 } 237 }
157 238
239 err = kobject_init_and_add(&slot->kobj, &pci_slot_ktype, NULL,
240 "%s", slot_name);
241 if (err)
242 goto err;
243
158 INIT_LIST_HEAD(&slot->list); 244 INIT_LIST_HEAD(&slot->list);
159 list_add(&slot->list, &parent->slots); 245 list_add(&slot->list, &parent->slots);
160 246
@@ -166,10 +252,10 @@ placeholder:
166 pr_debug("%s: created pci_slot on %04x:%02x:%02x\n", 252 pr_debug("%s: created pci_slot on %04x:%02x:%02x\n",
167 __func__, pci_domain_nr(parent), parent->number, slot_nr); 253 __func__, pci_domain_nr(parent), parent->number, slot_nr);
168 254
169 out: 255out:
170 up_write(&pci_bus_sem); 256 up_write(&pci_bus_sem);
171 return slot; 257 return slot;
172 err: 258err:
173 kfree(slot); 259 kfree(slot);
174 slot = ERR_PTR(err); 260 slot = ERR_PTR(err);
175 goto out; 261 goto out;
@@ -210,7 +296,6 @@ EXPORT_SYMBOL_GPL(pci_renumber_slot);
210 * just call kobject_put on its kobj and let our release methods do the 296 * just call kobject_put on its kobj and let our release methods do the
211 * rest. 297 * rest.
212 */ 298 */
213
214void pci_destroy_slot(struct pci_slot *slot) 299void pci_destroy_slot(struct pci_slot *slot)
215{ 300{
216 pr_debug("%s: dec refcount to %d on %04x:%02x:%02x\n", __func__, 301 pr_debug("%s: dec refcount to %d on %04x:%02x:%02x\n", __func__,