diff options
author | Alex Chiang <achiang@hp.com> | 2008-10-20 19:41:02 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2008-10-22 19:42:39 -0400 |
commit | 5fe6cc60680d29740b85278e17a002fa27b7e642 (patch) | |
tree | 3815152b48be7f02bb13b765ea8784ff60bfffcb /drivers/pci/hotplug | |
parent | 95cb9093960b6249fdbe7417bf513a1358aaa51a (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/pci/hotplug')
-rw-r--r-- | drivers/pci/hotplug/pci_hotplug_core.c | 26 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_core.c | 15 | ||||
-rw-r--r-- | drivers/pci/hotplug/shpchp_core.c | 15 |
3 files changed, 2 insertions, 54 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, | |||
612 | out: | 591 | out: |
613 | mutex_unlock(&pci_hp_mutex); | 592 | mutex_unlock(&pci_hp_mutex); |
614 | return result; | 593 | return result; |
615 | cleanup: | ||
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); |
226 | duplicate_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); |
147 | duplicate_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 | } |