[RFC/PATCH 1/2] use notifier chain for device node addition and removal

Nathan Lynch nathanl at austin.ibm.com
Wed Jan 26 16:06:31 EST 2005


This patch attempts to clean up the code which handles changes to the
Open Firmware device tree during PCI hotplug or DLPAR operations by
replacing the explicit fixups (e.g. of_finish_dynamic_node,
of_cleanup_node) with a notifier call chain.  It doesn't make all that
much of a dent in the ugliness -- note that I've simply folded
of_finish_dynamic_node into a high-priority notifier block while
leaving most of the function intact.

My ulterior motive here is that I want to be notified when processor
device nodes are added to the system, and I don't want to add yet more
special-case code to prom.c.  I'll be following up with a patch for this.

We could probably go further with the notifier chain approach, even to
the point of moving of_finish_dynamic_node and friends to a separate
module which could be config'd out for non-pSeries builds.

I haven't tested this with anything but adding and removing processors
from a Power5 partition, btw.  I'd appreciate any other testing
(e.g. PCI, VIO).

Thoughts?

Signed-off-by: Nathan Lynch <nathanl at austin.ibm.com>


---


diff -puN arch/ppc64/kernel/pSeries_iommu.c~of-dlpar-notifier arch/ppc64/kernel/pSeries_iommu.c
--- linux-2.6.11-rc2-mm1/arch/ppc64/kernel/pSeries_iommu.c~of-dlpar-notifier	2005-01-25 22:56:46.000000000 -0600
+++ linux-2.6.11-rc2-mm1-nathanl/arch/ppc64/kernel/pSeries_iommu.c	2005-01-25 22:56:46.000000000 -0600
@@ -34,6 +34,7 @@
 #include <linux/string.h>
 #include <linux/pci.h>
 #include <linux/dma-mapping.h>
+#include <linux/notifier.h>
 #include <asm/io.h>
 #include <asm/prom.h>
 #include <asm/rtas.h>
@@ -439,6 +440,29 @@ static void iommu_dev_setup_pSeries(stru
 	}
 }
 
+static int iommu_of_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *_node)
+{
+	struct device_node *node = (struct device_node *)_node;
+	int err = NOTIFY_DONE;
+
+	switch (action) {
+	case OF_RECONFIG_REMOVE:
+		if (node->iommu_table &&
+		    get_property(node, "ibm,dma-window", NULL)) {
+			iommu_free_table(node);
+			err = NOTIFY_OK;
+		}
+		break;
+	default:
+		break;
+	}
+	return err;
+}
+
+static struct notifier_block iommu_of_reconfig_nb = {
+	.notifier_call = iommu_of_reconfig_notifier,
+};
+
 static void iommu_bus_setup_null(struct pci_bus *b) { }
 static void iommu_dev_setup_null(struct pci_dev *d) { }
 
@@ -471,6 +495,8 @@ void iommu_init_early_pSeries(void)
 
 	ppc_md.iommu_dev_setup = iommu_dev_setup_pSeries;
 
+	register_of_reconfig_notifier(&iommu_of_reconfig_nb);
+
 	pci_iommu_init();
 }
 
diff -puN arch/ppc64/kernel/pci_dn.c~of-dlpar-notifier arch/ppc64/kernel/pci_dn.c
--- linux-2.6.11-rc2-mm1/arch/ppc64/kernel/pci_dn.c~of-dlpar-notifier	2005-01-25 22:56:46.000000000 -0600
+++ linux-2.6.11-rc2-mm1-nathanl/arch/ppc64/kernel/pci_dn.c	2005-01-25 22:56:46.000000000 -0600
@@ -23,6 +23,7 @@
 #include <linux/pci.h>
 #include <linux/string.h>
 #include <linux/init.h>
+#include <linux/notifier.h>
 
 #include <asm/io.h>
 #include <asm/prom.h>
@@ -158,6 +159,25 @@ struct device_node *fetch_dev_dn(struct 
 }
 EXPORT_SYMBOL(fetch_dev_dn);
 
+static int pci_of_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *_node)
+{
+	struct device_node *node = (struct device_node *)_node;
+	int err = NOTIFY_OK;
+
+	switch (action) {
+	case OF_RECONFIG_ADD:
+		update_dn_pci_info(node, node->parent->phb);
+		break;
+	default:
+		err = NOTIFY_DONE;
+		break;
+	}
+	return err;
+}
+
+static struct notifier_block pci_of_reconfig_nb = {
+	.notifier_call = pci_of_reconfig_notifier,
+};
 
 /*
  * Actually initialize the phbs.
@@ -170,4 +190,7 @@ void __init pci_devs_phb_init(void)
 	/* This must be done first so the device nodes have valid pci info! */
 	list_for_each_entry_safe(phb, tmp, &hose_list, list_node)
 		pci_devs_phb_init_dynamic(phb);
+
+	if (systemcfg->platform & PLATFORM_PSERIES)
+		register_of_reconfig_notifier(&pci_of_reconfig_nb);
 }
diff -puN arch/ppc64/kernel/prom.c~of-dlpar-notifier arch/ppc64/kernel/prom.c
--- linux-2.6.11-rc2-mm1/arch/ppc64/kernel/prom.c~of-dlpar-notifier	2005-01-25 22:56:46.000000000 -0600
+++ linux-2.6.11-rc2-mm1-nathanl/arch/ppc64/kernel/prom.c	2005-01-25 22:56:46.000000000 -0600
@@ -32,6 +32,7 @@
 #include <linux/delay.h>
 #include <linux/initrd.h>
 #include <linux/bitops.h>
+#include <linux/notifier.h>
 #include <asm/prom.h>
 #include <asm/rtas.h>
 #include <asm/lmb.h>
@@ -1671,7 +1672,6 @@ static int of_finish_dynamic_node_interr
 static int of_finish_dynamic_node(struct device_node *node)
 {
 	struct device_node *parent = of_get_parent(node);
-	u32 *regs;
 	int err = 0;
 	phandle *ibm_phandle;
 
@@ -1726,25 +1726,53 @@ static int of_finish_dynamic_node(struct
 		err = of_finish_dynamic_node_interrupts(node);
 		if (err) goto out;
 	}
+out:
+	of_node_put(parent);
+	return err;
+}
 
-	/* now do the rough equivalent of update_dn_pci_info, this
-	 * probably is not correct for phb's, but should work for
-	 * IOAs and slots.
-	 */
+static struct notifier_block *of_reconfig_chain;
+
+int register_of_reconfig_notifier(struct notifier_block *nb)
+{
+	return notifier_chain_register(&of_reconfig_chain, nb);
+}
 
-	node->phb = parent->phb;
+void unregister_of_reconfig_notifier(struct notifier_block *nb)
+{
+	notifier_chain_unregister(&of_reconfig_chain, nb);
+}
 
-	regs = (u32 *)get_property(node, "reg", NULL);
-	if (regs) {
-		node->busno = (regs[0] >> 16) & 0xff;
-		node->devfn = (regs[0] >> 8) & 0xff;
-	}
+static int of_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *_node)
+{
+	struct device_node *node = (struct device_node *)_node;
+	int err = NOTIFY_OK;
 
-out:
-	of_node_put(parent);
+	switch (action) {
+	case OF_RECONFIG_ADD:
+		if (of_finish_dynamic_node(node))
+			err = NOTIFY_BAD;
+		break;
+	default:
+		err = NOTIFY_DONE;
+		break;
+	}
 	return err;
 }
 
+static struct notifier_block of_reconfig_nb = {
+	.notifier_call = of_reconfig_notifier,
+	.priority = 10, /* This one needs to run first */
+};
+
+static int __init of_reconfig_setup(void)
+{
+	if (systemcfg->platform & PLATFORM_PSERIES)
+		register_of_reconfig_notifier(&of_reconfig_nb);
+	return 0;
+}
+__initcall(of_reconfig_setup);
+
 /*
  * Given a path and a property list, construct an OF device node, add
  * it to the device tree and global list, and place it in
@@ -1778,9 +1806,11 @@ int of_add_node(const char *path, struct
 		return -EINVAL; /* could also be ENOMEM, though */
 	}
 
-	if (0 != (err = of_finish_dynamic_node(np))) {
+	err = notifier_call_chain(&of_reconfig_chain, OF_RECONFIG_ADD, np);
+	if (err == NOTIFY_BAD) {
+		printk(KERN_WARNING "Failed to add device node %s\n", path);
 		kfree(np);
-		return err;
+		return -EINVAL;
 	}
 
 	write_lock(&devtree_lock);
@@ -1798,15 +1828,6 @@ int of_add_node(const char *path, struct
 }
 
 /*
- * Prepare an OF node for removal from system
- */
-static void of_cleanup_node(struct device_node *np)
-{
-	if (np->iommu_table && get_property(np, "ibm,dma-window", NULL))
-		iommu_free_table(np);
-}
-
-/*
  * "Unplug" a node from the device tree.  The caller must hold
  * a reference to the node.  The memory associated with the node
  * is not freed until its refcount goes to zero.
@@ -1814,6 +1835,7 @@ static void of_cleanup_node(struct devic
 int of_remove_node(struct device_node *np)
 {
 	struct device_node *parent, *child;
+	int err;
 
 	parent = of_get_parent(np);
 	if (!parent)
@@ -1824,7 +1846,9 @@ int of_remove_node(struct device_node *n
 		return -EBUSY;
 	}
 
-	of_cleanup_node(np);
+	err = notifier_call_chain(&of_reconfig_chain, OF_RECONFIG_REMOVE, np);
+	if (err == NOTIFY_BAD)
+		return -EBUSY;
 
 	write_lock(&devtree_lock);
 	remove_node_proc_entries(np);
diff -puN include/asm-ppc64/prom.h~of-dlpar-notifier include/asm-ppc64/prom.h
--- linux-2.6.11-rc2-mm1/include/asm-ppc64/prom.h~of-dlpar-notifier	2005-01-25 22:56:46.000000000 -0600
+++ linux-2.6.11-rc2-mm1-nathanl/include/asm-ppc64/prom.h	2005-01-25 22:56:46.000000000 -0600
@@ -211,6 +211,14 @@ extern void of_node_put(struct device_no
 extern int of_add_node(const char *path, struct property *proplist);
 extern int of_remove_node(struct device_node *np);
 
+/* For notification of device node addition and removal */
+extern int register_of_reconfig_notifier(struct notifier_block *nb);
+extern void unregister_of_reconfig_notifier(struct notifier_block *nb);
+
+/* Notification codes for users of the above */
+#define OF_RECONFIG_ADD       0x0001
+#define OF_RECONFIG_REMOVE    0x0002
+
 /* Other Prototypes */
 extern unsigned long prom_init(unsigned long, unsigned long, unsigned long,
 	unsigned long, unsigned long);

_





More information about the Linuxppc64-dev mailing list