@@ -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;
}
@@ -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;
@@ -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);
}
}
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(-)