[PATCH 7/7] PCI Hotplug core: fixups for duplicate slot names

Alex Chiang
2008-08-06T05:13:34Z

Commit a86161b3134465f072d965ca7508ec9c1e2e52c7 added a check
to detect platforms with broken firmware that attempt to assign
identical slot names to multiple slots.

This approach is suboptimal because it prints a scary message
and forces the user to reload a hotplug driver with a module
parameter. We can do better here by attempting to 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
The Mth registered slot becomes N-M

This patch also has the effect of making attempts by multiple
drivers claiming the same slot become a more prominent error,
returning -EBUSY in those cases.

Signed-off-by: Alex Chiang <achiang@hp.com>
---
drivers/pci/hotplug/pci_hotplug_core.c | 4 --
drivers/pci/slot.c | 65 ++++++++++++++++++++++++++++---
include/linux/pci.h | 2 +-
3 files changed, 59 insertions(+), 12 deletions(-)

diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index 5f85b1b..4476f0c 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -568,10 +568,6 @@ int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr)
return -EINVAL;
}

- /* Check if we have already registered a slot with the same name. */
- if (get_slot_from_name(slot->name))
- return -EEXIST;
-
/*
* No problems if we call this interface from both ACPI_PCI_SLOT
* driver and call it here again. If we've already created the
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
index 7e5b85c..e35dcae 100644
--- a/drivers/pci/slot.c
+++ b/drivers/pci/slot.c
@@ -84,7 +84,19 @@ static struct kobj_type pci_slot_ktype = {
* either return a new &struct pci_slot to the caller, or if the pci_slot
* already exists, its refcount will be incremented.
*
- * Slots are uniquely identified by a @pci_bus, @slot_nr, @name tuple.
+ * Slots are uniquely identified by a @pci_bus, @slot_nr tuple.
+ *
+ * The kobject API imposes a restriction on us, and does not allow sysfs
+ * entries with duplicate names. There are known platforms with broken
+ * firmware that assign the same name to multiple slots.
+ *
+ * We workaround these broken platforms by renaming the slots on behalf
+ * of the caller. If firmware assigns name N to multiple slots:
+ *
+ * The first slot is assigned N
+ * The second slot is assigned N-1
+ * The third slot is assigned N-2
+ * The Mth slot is assigned N-M
*
* Placeholder slots:
* In most cases, @pci_bus, @slot_nr will be sufficient to uniquely identify
@@ -103,13 +115,15 @@ static struct kobj_type pci_slot_ktype = {
* consist solely of a dddd:bb tuple, where dddd is the PCI domain of the
* %struct pci_bus and bb is the bus number. In other words, the devfn of
* the 'placeholder' slot will not be displayed.
- */
+ **/

struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr,
- const char *name)
+ char *name)
{
+ struct kobject *dup_slot;
struct pci_slot *slot;
- int err;
+ char *old_name;
+ int err, len, width, dup = 1;

down_write(&pci_bus_sem);

@@ -138,8 +152,43 @@ placeholder:

slot->bus = parent;
slot->number = slot_nr;
-
slot->kobj.kset = pci_slots_kset;
+
+ old_name = kstrdup(name, GFP_KERNEL);
+ if (!old_name) {
+ err = -ENOMEM;
+ goto err_oldname;
+ }
+
+ /*
+ * Start off allocating enough room for "name-X"
+ */
+ len = strlen(name) + 2;
+ width = 1;
+duplicate_name:
+ dup_slot = kset_find_obj(pci_slots_kset, name);
+ if (dup_slot) {
+ /*
+ * We hit this the first time through, which gives us
+ * space for terminating NULL, and then every power of 10
+ * afterwards, which gives us space to add another digit
+ * to "name-XX..."
+ */
+ if (dup % width == 0) {
+ len++;
+ width *= 10;
+ }
+ kfree(name);
+ name = kzalloc(len, GFP_KERNEL);
+ if (!name) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ snprintf(name, len, "%s-%d", old_name, dup++);
+ goto duplicate_name;
+ }
+
err = kobject_init_and_add(&slot->kobj, &pci_slot_ktype, NULL,
"%s", name);
if (err) {
@@ -158,6 +207,8 @@ placeholder:
up_write(&pci_bus_sem);
return slot;
err:
+ kfree(old_name);
+ err_oldname:
kfree(slot);
slot = ERR_PTR(err);
goto out;
@@ -172,7 +223,7 @@ EXPORT_SYMBOL_GPL(pci_create_slot);
* The primary purpose of this interface is to allow callers who earlier
* created a placeholder slot in pci_create_slot() by passing a -1 as
* slot_nr, to update their %struct pci_slot with the correct @slot_nr.
- */
+ **/

void pci_update_slot_number(struct pci_slot *slot, int slot_nr)
{
@@ -202,7 +253,7 @@ EXPORT_SYMBOL_GPL(pci_update_slot_number);
* %struct pci_slot is refcounted, so destroying them is really easy; we
* just call kobject_put on its kobj and let our release methods do the
* rest.
- */
+ **/

void pci_destroy_slot(struct pci_slot *slot)
{
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 825be38..4b6e847 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -509,7 +509,7 @@ struct pci_bus *pci_create_bus(struct device *parent, int bus,
struct pci_bus *pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev,
int busnr);
struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr,
- const char *name);
+ char *name);
void pci_destroy_slot(struct pci_slot *slot);
void pci_update_slot_number(struct pci_slot *slot, int slot_nr);
int pci_scan_slot(struct pci_bus *bus, int devfn);
--
1.6.0.rc0.g95f8