|
@@ -78,6 +78,77 @@ static struct kobj_type pci_slot_ktype = {
|
|
|
.default_attrs = pci_slot_default_attrs,
|
|
|
};
|
|
|
|
|
|
+static char *make_slot_name(const char *name)
|
|
|
+{
|
|
|
+ char *new_name;
|
|
|
+ int len, max, dup;
|
|
|
+
|
|
|
+ new_name = kstrdup(name, GFP_KERNEL);
|
|
|
+ if (!new_name)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Make sure we hit the realloc case the first time through the
|
|
|
+ * loop. 'len' will be strlen(name) + 3 at that point which is
|
|
|
+ * enough space for "name-X" and the trailing NUL.
|
|
|
+ */
|
|
|
+ len = strlen(name) + 2;
|
|
|
+ max = 1;
|
|
|
+ dup = 1;
|
|
|
+
|
|
|
+ for (;;) {
|
|
|
+ struct kobject *dup_slot;
|
|
|
+ dup_slot = kset_find_obj(pci_slots_kset, new_name);
|
|
|
+ if (!dup_slot)
|
|
|
+ break;
|
|
|
+ kobject_put(dup_slot);
|
|
|
+ if (dup == max) {
|
|
|
+ len++;
|
|
|
+ max *= 10;
|
|
|
+ kfree(new_name);
|
|
|
+ new_name = kmalloc(len, GFP_KERNEL);
|
|
|
+ if (!new_name)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ sprintf(new_name, "%s-%d", name, dup++);
|
|
|
+ }
|
|
|
+
|
|
|
+ return new_name;
|
|
|
+}
|
|
|
+
|
|
|
+static int rename_slot(struct pci_slot *slot, const char *name)
|
|
|
+{
|
|
|
+ int result = 0;
|
|
|
+ char *slot_name;
|
|
|
+
|
|
|
+ if (strcmp(kobject_name(&slot->kobj), name) == 0)
|
|
|
+ return result;
|
|
|
+
|
|
|
+ slot_name = make_slot_name(name);
|
|
|
+ if (!slot_name)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ result = kobject_rename(&slot->kobj, slot_name);
|
|
|
+ kfree(slot_name);
|
|
|
+
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr)
|
|
|
+{
|
|
|
+ struct pci_slot *slot;
|
|
|
+ /*
|
|
|
+ * We already hold pci_bus_sem so don't worry
|
|
|
+ */
|
|
|
+ list_for_each_entry(slot, &parent->slots, list)
|
|
|
+ if (slot->number == slot_nr) {
|
|
|
+ kobject_get(&slot->kobj);
|
|
|
+ return slot;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* pci_create_slot - create or increment refcount for physical PCI slot
|
|
|
* @parent: struct pci_bus of parent bridge
|
|
@@ -90,7 +161,17 @@ 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.
|
|
|
+ *
|
|
|
+ * There are known platforms with broken firmware that assign the same
|
|
|
+ * name to multiple slots. 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
|
|
|
+ * etc.
|
|
|
*
|
|
|
* Placeholder slots:
|
|
|
* In most cases, @pci_bus, @slot_nr will be sufficient to uniquely identify
|
|
@@ -99,62 +180,67 @@ static struct kobj_type pci_slot_ktype = {
|
|
|
* the slot. In this scenario, the caller may pass -1 for @slot_nr.
|
|
|
*
|
|
|
* The following semantics are imposed when the caller passes @slot_nr ==
|
|
|
- * -1. First, the check for existing %struct pci_slot is skipped, as the
|
|
|
- * caller may know about several unpopulated slots on a given %struct
|
|
|
- * pci_bus, and each slot would have a @slot_nr of -1. Uniqueness for
|
|
|
- * these slots is then determined by the @name parameter. We expect
|
|
|
- * kobject_init_and_add() to warn us if the caller attempts to create
|
|
|
- * multiple slots with the same name. The other change in semantics is
|
|
|
+ * -1. First, we no longer check for an existing %struct pci_slot, as there
|
|
|
+ * may be many slots with @slot_nr of -1. The other change in semantics is
|
|
|
* user-visible, which is the 'address' parameter presented in sysfs will
|
|
|
* 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,
|
|
|
struct hotplug_slot *hotplug)
|
|
|
{
|
|
|
struct pci_dev *dev;
|
|
|
struct pci_slot *slot;
|
|
|
- int err;
|
|
|
+ int err = 0;
|
|
|
+ char *slot_name = NULL;
|
|
|
|
|
|
down_write(&pci_bus_sem);
|
|
|
|
|
|
if (slot_nr == -1)
|
|
|
goto placeholder;
|
|
|
|
|
|
- /* If we've already created this slot, bump refcount and return. */
|
|
|
- list_for_each_entry(slot, &parent->slots, list) {
|
|
|
- if (slot->number == slot_nr) {
|
|
|
- kobject_get(&slot->kobj);
|
|
|
- pr_debug("%s: inc refcount to %d on %04x:%02x:%02x\n",
|
|
|
- __func__,
|
|
|
- atomic_read(&slot->kobj.kref.refcount),
|
|
|
- pci_domain_nr(parent), parent->number,
|
|
|
- slot_nr);
|
|
|
- goto out;
|
|
|
+ /*
|
|
|
+ * Hotplug drivers are allowed to rename an existing slot,
|
|
|
+ * but only if not already claimed.
|
|
|
+ */
|
|
|
+ slot = get_slot(parent, slot_nr);
|
|
|
+ if (slot) {
|
|
|
+ if (hotplug) {
|
|
|
+ if ((err = slot->hotplug ? -EBUSY : 0)
|
|
|
+ || (err = rename_slot(slot, name))) {
|
|
|
+ kobject_put(&slot->kobj);
|
|
|
+ slot = NULL;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
}
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
placeholder:
|
|
|
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
|
|
|
if (!slot) {
|
|
|
- slot = ERR_PTR(-ENOMEM);
|
|
|
- goto out;
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto err;
|
|
|
}
|
|
|
|
|
|
slot->bus = parent;
|
|
|
slot->number = slot_nr;
|
|
|
|
|
|
slot->kobj.kset = pci_slots_kset;
|
|
|
- err = kobject_init_and_add(&slot->kobj, &pci_slot_ktype, NULL,
|
|
|
- "%s", name);
|
|
|
- if (err) {
|
|
|
- printk(KERN_ERR "Unable to register kobject %s\n", name);
|
|
|
+
|
|
|
+ slot_name = make_slot_name(name);
|
|
|
+ if (!slot_name) {
|
|
|
+ err = -ENOMEM;
|
|
|
goto err;
|
|
|
}
|
|
|
|
|
|
+ err = kobject_init_and_add(&slot->kobj, &pci_slot_ktype, NULL,
|
|
|
+ "%s", slot_name);
|
|
|
+ if (err)
|
|
|
+ goto err;
|
|
|
+
|
|
|
INIT_LIST_HEAD(&slot->list);
|
|
|
list_add(&slot->list, &parent->slots);
|
|
|
|
|
@@ -166,10 +252,10 @@ placeholder:
|
|
|
pr_debug("%s: created pci_slot on %04x:%02x:%02x\n",
|
|
|
__func__, pci_domain_nr(parent), parent->number, slot_nr);
|
|
|
|
|
|
- out:
|
|
|
+out:
|
|
|
up_write(&pci_bus_sem);
|
|
|
return slot;
|
|
|
- err:
|
|
|
+err:
|
|
|
kfree(slot);
|
|
|
slot = ERR_PTR(err);
|
|
|
goto out;
|
|
@@ -210,7 +296,6 @@ EXPORT_SYMBOL_GPL(pci_renumber_slot);
|
|
|
* just call kobject_put on its kobj and let our release methods do the
|
|
|
* rest.
|
|
|
*/
|
|
|
-
|
|
|
void pci_destroy_slot(struct pci_slot *slot)
|
|
|
{
|
|
|
pr_debug("%s: dec refcount to %d on %04x:%02x:%02x\n", __func__,
|