diff mbox

[7/8] PCI/hotplug/rpa: Hierarchial slots

Message ID 1416869365-7671-8-git-send-email-gwshan@linux.vnet.ibm.com (mailing list archive)
State Changes Requested
Headers show

Commit Message

Gavin Shan Nov. 24, 2014, 10:49 p.m. UTC
The direct or indirect parent of hotpluggable slot has possibility
to be hotpluggable. Unfortunately, current implementation doesn't
cover it. The patch fixes the issue:

   * When adding slots based on the given device node, the child
     device nodes are scanned to see if they're hotpluggable and
     add slots for them if applicable.
   * When unregistering slot, its children slots will be removed
     automatically.
   * Parent slot is added prior to child slots in addition path,
     while child slots should be removed before parent slot in
     removal path.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 drivers/pci/hotplug/rpadlpar_core.c |   6 +--
 drivers/pci/hotplug/rpaphp.h        |  22 ++++----
 drivers/pci/hotplug/rpaphp_core.c   | 105 ++++++++++++++++++++++++------------
 3 files changed, 85 insertions(+), 48 deletions(-)
diff mbox

Patch

diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c
index a36d2c9..f375e92 100644
--- a/drivers/pci/hotplug/rpadlpar_core.c
+++ b/drivers/pci/hotplug/rpadlpar_core.c
@@ -119,11 +119,9 @@  static struct device_node *find_dlpar_node(char *drc_name, int *node_type)
  */
 static struct rpa_php_slot *find_php_slot(struct device_node *dn)
 {
-	struct list_head *tmp, *n;
-	struct rpa_php_slot *slot;
+	struct rpa_php_slot *slot, *tmp;
 
-	list_for_each_safe(tmp, n, &rpaphp_slot_head) {
-		slot = list_entry(tmp, struct rpa_php_slot, rpaphp_slot_list);
+	list_for_each_entry_safe(slot, tmp, &rpaphp_slot_head, link) {
 		if (slot->dn == dn)
 			return slot;
 	}
diff --git a/drivers/pci/hotplug/rpaphp.h b/drivers/pci/hotplug/rpaphp.h
index 09dd516..0354572 100644
--- a/drivers/pci/hotplug/rpaphp.h
+++ b/drivers/pci/hotplug/rpaphp.h
@@ -57,19 +57,21 @@  extern bool rpaphp_debug;
 #define RPA_PHP_SLOT_PRESENT	1	/* Presented     */
 
 struct rpa_php_slot {
-	struct list_head rpaphp_slot_list;
-	int state;
+	char			*name;
+	int			state;
 #define RPA_PHP_SLOT_NOT_CONFIGURED	0
 #define RPA_PHP_SLOT_CONFIGURED		1
 #define RPA_PHP_SLOT_NOT_VALID		2
-	u32 index;
-	u32 type;
-	u32 power_domain;
-	char *name;
-	struct device_node *dn;
-	struct pci_bus *bus;
-	struct list_head *pci_devs;
-	struct hotplug_slot *hotplug_slot;
+	u32			index;
+	u32			type;
+	u32			power_domain;
+	struct device_node	*dn;
+	struct pci_bus		*bus;
+	struct list_head	*pci_devs;
+	struct hotplug_slot	*hotplug_slot;
+	struct list_head	link;
+	struct list_head	list;
+	struct list_head	children;
 };
 
 extern struct list_head rpaphp_slot_head;
diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c
index 91eff8f..ba28212 100644
--- a/drivers/pci/hotplug/rpaphp_core.c
+++ b/drivers/pci/hotplug/rpaphp_core.c
@@ -25,6 +25,7 @@ 
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/list.h>
 #include <linux/pci.h>
 #include <linux/pci_hotplug.h>
 #include <linux/smp.h>
@@ -87,6 +88,9 @@  struct rpa_php_slot *alloc_slot_struct(struct device_node *dn,
 	slot->power_domain = power_domain;
 	slot->hotplug_slot->private = slot;
 	slot->hotplug_slot->release = &rpaphp_release_slot;
+	INIT_LIST_HEAD(&slot->link);
+	INIT_LIST_HEAD(&slot->list);
+	INIT_LIST_HEAD(&slot->children);
 
 	slot->hotplug_slot->info->power_status = RPA_PHP_SLOT_POWER_ON;
 	slot->hotplug_slot->info->attention_status = RPA_PHP_SLOT_ATTEN_OFF;
@@ -107,16 +111,17 @@  error_nomem:
 
 int rpaphp_register_slot(struct rpa_php_slot *slot)
 {
+	struct device_node *dn;
 	struct hotplug_slot *php_slot = slot->hotplug_slot;
-	struct rpa_php_slot *tmp;
-	int slotno, retval;
+	struct rpa_php_slot *parent, *tmp;
+	int slotno, ret;
 
 	dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n",
 	    __func__, slot->dn->full_name, slot->index, slot->name,
 	    slot->power_domain, slot->type);
 
 	/* Should not try to register the same slot twice */
-	list_for_each_entry(tmp, &rpaphp_slot_head, rpaphp_slot_list) {
+	list_for_each_entry(tmp, &rpaphp_slot_head, link) {
 		if (!strcmp(tmp->name, slot->name)) {
 			err("%s: Slot[%s] is already registered\n",
 			    __func__, slot->name);
@@ -127,34 +132,62 @@  int rpaphp_register_slot(struct rpa_php_slot *slot)
 		slotno = PCI_SLOT(PCI_DN(slot->dn->child)->devfn);
 	else
 		slotno = -1;
-	retval = pci_hp_register(php_slot, slot->bus, slotno, slot->name);
-	if (retval) {
-		err("pci_hp_register failed with error %d\n", retval);
-		return retval;
+	ret = pci_hp_register(php_slot, slot->bus, slotno, slot->name);
+	if (ret) {
+		err("pci_hp_register failed with error %d\n", ret);
+		return ret;
+	}
+
+	/* Search parent slot */
+	parent = NULL;
+	dn = slot->dn;
+	while (!parent && (dn = of_get_parent(dn))) {
+		if (!PCI_DN(dn)) {
+			of_node_put(dn);
+			break;
+		}
+
+		list_for_each_entry(tmp, &rpaphp_slot_head, link) {
+			if (tmp->dn != dn) {
+				parent = tmp;
+				break;
+			}
+		}
 	}
 
-	/* add slot to our internal list */
-	list_add(&slot->rpaphp_slot_list, &rpaphp_slot_head);
+	/* Add slot to parent list */
+	if (parent)
+		list_add(&slot->list, &parent->children);
+
+	/* Add slot to global list */
+	list_add(&slot->link, &rpaphp_slot_head);
 	info("Slot [%s] registered\n", slot->name);
 	return 0;
 }
 
 int rpaphp_deregister_slot(struct rpa_php_slot *slot)
 {
+	struct rpa_php_slot *child, *tmp;
 	struct hotplug_slot *php_slot = slot->hotplug_slot;
-	int retval = 0;
+	int ret;
 
-	dbg("%s - Entry: deregistering slot=%s\n",
-	    __func__, slot->name);
+	/* Unregister children firstly */
+	list_for_each_entry_safe(child, tmp, &slot->children, list) {
+		ret = rpaphp_deregister_slot(child);
+		if (ret)
+			return ret;
+	}
 
-	list_del(&slot->rpaphp_slot_list);
+	/* Remove from the parent and global lists */
+	list_del(&slot->list);
+	list_del(&slot->link);
 
-	retval = pci_hp_deregister(php_slot);
-	if (retval)
-		err("Problem unregistering a slot %s\n", slot->name);
+	ret = pci_hp_deregister(php_slot);
+	if (ret)
+		err("%s: Error %d unregistering slot[%s]\n",
+		    __func__, ret, slot->name);
 
-	dbg("%s - Exit: rc[%d]\n", __func__, retval);
-	return retval;
+	return ret;
 }
 EXPORT_SYMBOL_GPL(rpaphp_deregister_slot);
 
@@ -176,24 +209,31 @@  EXPORT_SYMBOL_GPL(rpaphp_deregister_slot);
  */
 int rpaphp_add_slot(struct device_node *dn)
 {
+	struct device_node *child;
 	struct rpa_php_slot *slot = NULL;
 	int ret;
 
 	/* Create slot */
 	if (machine_is(pseries))
 		slot = rpaphp_rtas_add_slot(dn);
-	if (!slot)
-		return -EIO;
 
-	/* Enable slot */
-	ret = slot->hotplug_slot->ops->enable_slot(slot->hotplug_slot);
-	if (ret)
-		goto fail;
+	if (slot) {
+		/* Enable slot */
+		ret = slot->hotplug_slot->ops->enable_slot(slot->hotplug_slot);
+		if (ret)
+			goto fail;
 
-	/* Register slot */
-	ret = rpaphp_register_slot(slot);
-	if (ret)
-		goto fail;
+		/* Register slot */
+		ret = rpaphp_register_slot(slot);
+		if (ret)
+			goto fail;
+	}
+
+	for_each_child_of_node(dn, child) {
+		ret = rpaphp_add_slot(child);
+		if (ret)
+			return ret;
+	}
 
 	return 0;
 fail:
@@ -204,18 +244,15 @@  EXPORT_SYMBOL_GPL(rpaphp_add_slot);
 
 static void __exit cleanup_slots(void)
 {
-	struct list_head *tmp, *n;
-	struct rpa_php_slot *slot;
+	struct rpa_php_slot *slot, *tmp;
 
 	/*
 	 * Unregister all of our slots with the pci_hotplug subsystem,
 	 * and free up all memory that we had allocated.
 	 * memory will be freed in release_slot callback.
 	 */
-
-	list_for_each_safe(tmp, n, &rpaphp_slot_head) {
-		slot = list_entry(tmp, struct rpa_php_slot, rpaphp_slot_list);
-		list_del(&slot->rpaphp_slot_list);
+	list_for_each_entry_safe(slot, tmp, &rpaphp_slot_head, link) {
+		list_del(&slot->link);
 		pci_hp_deregister(slot->hotplug_slot);
 	}
 }