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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxx>
---
 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

--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux