[PATCH 2/8] make OF node fixup code usable at runtime

Nathan Lynch ntl at pobox.com
Tue Mar 15 13:49:33 EST 2005


Updates since last submission:

o  I decided to use Ben's suggestion to introduce a small wrapper
   function for handling allocations.  At first I thought I would use
   John's idea of checking the dynamic flag in the node to decide
   whether to use kmalloc, but I think this way is better since it
   keeps all that logic out of the interpret_func-style routines.


At boot we recurse through the device tree "fixing up" various fields
and properties in the device nodes.  Long ago, to support DLPAR and
hotplug, we largely duplicated some of this fixup code, the main
difference being that the new code used kmalloc for allocating various
data structures which are attached to the new device nodes.

This patch introduces a helper function (prom_alloc) for handling
allocations at both boot and runtime, kills most of the duplicated
code, and makes finish_node, finish_node_interrupts, and
interpret_pci_props suitable for use at runtime by converting them to
use prom_alloc.

Signed-off-by: Nathan Lynch <ntl at pobox.com>

 arch/ppc64/kernel/prom.c |  177 +++++++++------------------
 1 files changed, 62 insertions(+), 115 deletions(-)

Index: linux-2.6.11-bk10/arch/ppc64/kernel/prom.c
===================================================================
--- linux-2.6.11-bk10.orig/arch/ppc64/kernel/prom.c	2005-03-14 21:49:46.000000000 +0000
+++ linux-2.6.11-bk10/arch/ppc64/kernel/prom.c	2005-03-14 21:54:08.000000000 +0000
@@ -103,6 +103,25 @@ static DEFINE_RWLOCK(devtree_lock);
 struct device_node *of_chosen;
 
 /*
+ * Wrapper for allocating memory for various data that needs to be
+ * attached to device nodes as they are processed at boot or when
+ * added to the device tree later (e.g. DLPAR).  At boot there is
+ * already a region reserved so we just increment *mem_start by size;
+ * otherwise we call kmalloc.
+ */
+static void * prom_alloc(unsigned long size, unsigned long *mem_start)
+{
+	unsigned long tmp;
+
+	if (!mem_start)
+		return kmalloc(size, GFP_KERNEL);
+
+	tmp = *mem_start;
+	*mem_start += size;
+	return (void *)tmp;
+}
+
+/*
  * Find the device_node with a given phandle.
  */
 static struct device_node * find_phandle(phandle ph)
@@ -255,9 +274,9 @@ static int __devinit map_interrupt(unsig
 	return nintrc;
 }
 
-static int __init finish_node_interrupts(struct device_node *np,
-					 unsigned long *mem_start,
-					 int measure_only)
+static int __devinit finish_node_interrupts(struct device_node *np,
+					    unsigned long *mem_start,
+					    int measure_only)
 {
 	unsigned int *ints;
 	int intlen, intrcells, intrcount;
@@ -270,8 +289,10 @@ static int __init finish_node_interrupts
 		return 0;
 	intrcells = prom_n_intr_cells(np);
 	intlen /= intrcells * sizeof(unsigned int);
-	np->intrs = (struct interrupt_info *) (*mem_start);
-	(*mem_start) += intlen * sizeof(struct interrupt_info);
+
+	np->intrs = prom_alloc(intlen * sizeof(*(np->intrs)), mem_start);
+	if (!np->intrs)
+		return -ENOMEM;
 
 	if (measure_only)
 		return 0;
@@ -318,33 +339,39 @@ static int __init finish_node_interrupts
 	return 0;
 }
 
-static int __init interpret_pci_props(struct device_node *np,
-				      unsigned long *mem_start,
-				      int naddrc, int nsizec,
-				      int measure_only)
+static int __devinit interpret_pci_props(struct device_node *np,
+					 unsigned long *mem_start,
+					 int naddrc, int nsizec,
+					 int measure_only)
 {
 	struct address_range *adr;
 	struct pci_reg_property *pci_addrs;
-	int i, l;
+	int i, l, n_addrs;
 
 	pci_addrs = (struct pci_reg_property *)
 		get_property(np, "assigned-addresses", &l);
-	if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) {
-		i = 0;
-		adr = (struct address_range *) (*mem_start);
-		while ((l -= sizeof(struct pci_reg_property)) >= 0) {
-			if (!measure_only) {
-				adr[i].space = pci_addrs[i].addr.a_hi;
-				adr[i].address = pci_addrs[i].addr.a_lo |
-					((u64)pci_addrs[i].addr.a_mid << 32);
-				adr[i].size = pci_addrs[i].size_lo;
-			}
-			++i;
-		}
-		np->addrs = adr;
-		np->n_addrs = i;
-		(*mem_start) += i * sizeof(struct address_range);
+	if (!pci_addrs)
+		return 0;
+
+	n_addrs = l / sizeof(*pci_addrs);
+
+	adr = prom_alloc(n_addrs * sizeof(*adr), mem_start);
+	if (!adr)
+		return -ENOMEM;
+ 
+ 	if (measure_only)
+ 		return 0;
+ 
+ 	np->addrs = adr;
+ 	np->n_addrs = n_addrs;
+ 
+ 	for (i = 0; i < n_addrs; i++) {
+ 		adr[i].space = pci_addrs[i].addr.a_hi;
+ 		adr[i].address = pci_addrs[i].addr.a_lo |
+			((u64)pci_addrs[i].addr.a_mid << 32);
+ 		adr[i].size = pci_addrs[i].size_lo;
 	}
+
 	return 0;
 }
 
@@ -490,11 +517,11 @@ static int __init interpret_root_props(s
 	return 0;
 }
 
-static int __init finish_node(struct device_node *np,
-			      unsigned long *mem_start,
-			      interpret_func *ifunc,
-			      int naddrc, int nsizec,
-			      int measure_only)
+static int __devinit finish_node(struct device_node *np,
+				 unsigned long *mem_start,
+				 interpret_func *ifunc,
+				 int naddrc, int nsizec,
+				 int measure_only)
 {
 	struct device_node *child;
 	int *ip, rc = 0;
@@ -1627,54 +1654,6 @@ static void remove_node_proc_entries(str
 #endif /* CONFIG_PROC_DEVICETREE */
 
 /*
- * Fix up n_intrs and intrs fields in a new device node
- *
- */
-static int of_finish_dynamic_node_interrupts(struct device_node *node)
-{
-	int intrcells, intlen, i;
-	unsigned *irq, *ints, virq;
-	struct device_node *ic;
-
-	ints = (unsigned int *)get_property(node, "interrupts", &intlen);
-	intrcells = prom_n_intr_cells(node);
-	intlen /= intrcells * sizeof(unsigned int);
-	node->n_intrs = intlen;
-	node->intrs = kmalloc(sizeof(struct interrupt_info) * intlen,
-			      GFP_KERNEL);
-	if (!node->intrs)
-		return -ENOMEM;
-
-	for (i = 0; i < intlen; ++i) {
-		int n, j;
-		node->intrs[i].line = 0;
-		node->intrs[i].sense = 1;
-		n = map_interrupt(&irq, &ic, node, ints, intrcells);
-		if (n <= 0)
-			continue;
-		virq = virt_irq_create_mapping(irq[0]);
-		if (virq == NO_IRQ) {
-			printk(KERN_CRIT "Could not allocate interrupt "
-			       "number for %s\n", node->full_name);
-			return -ENOMEM;
-		}
-		node->intrs[i].line = irq_offset_up(virq);
-		if (n > 1)
-			node->intrs[i].sense = irq[1];
-		if (n > 2) {
-			printk(KERN_DEBUG "hmmm, got %d intr cells for %s:", n,
-			       node->full_name);
-			for (j = 0; j < n; ++j)
-				printk(" %d", irq[j]);
-			printk("\n");
-		}
-		ints += intrcells;
-	}
-	return 0;
-}
-
-
-/*
  * Fix up the uninitialized fields in a new device node:
  * name, type, n_addrs, addrs, n_intrs, intrs, and pci-specific fields
  *
@@ -1685,7 +1664,9 @@ static int of_finish_dynamic_node_interr
  * This should probably be split up into smaller chunks.
  */
 
-static int of_finish_dynamic_node(struct device_node *node)
+static int of_finish_dynamic_node(struct device_node *node,
+				  unsigned long *unused1, int unused2,
+				  int unused3, int unused4)
 {
 	struct device_node *parent = of_get_parent(node);
 	u32 *regs;
@@ -1710,41 +1691,6 @@ static int of_finish_dynamic_node(struct
 	if ((ibm_phandle = (unsigned int *)get_property(node, "ibm,phandle", NULL)))
 		node->linux_phandle = *ibm_phandle;
 
-	/* do the work of interpret_pci_props */
-	if (parent->type && !strcmp(parent->type, "pci")) {
-		struct address_range *adr;
-		struct pci_reg_property *pci_addrs;
-		int i, l;
-
-		pci_addrs = (struct pci_reg_property *)
-			get_property(node, "assigned-addresses", &l);
-		if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) {
-			i = 0;
-			adr = kmalloc(sizeof(struct address_range) * 
-				      (l / sizeof(struct pci_reg_property)),
-				      GFP_KERNEL);
-			if (!adr) {
-				err = -ENOMEM;
-				goto out;
-			}
-			while ((l -= sizeof(struct pci_reg_property)) >= 0) {
-				adr[i].space = pci_addrs[i].addr.a_hi;
-				adr[i].address = pci_addrs[i].addr.a_lo |
-					((u64)pci_addrs[i].addr.a_mid << 32);
-				adr[i].size = pci_addrs[i].size_lo;
-				++i;
-			}
-			node->addrs = adr;
-			node->n_addrs = i;
-		}
-	}
-
-	/* now do the work of finish_node_interrupts */
-	if (get_property(node, "interrupts", NULL)) {
-		err = of_finish_dynamic_node_interrupts(node);
-		if (err) goto out;
-	}
-
 	/* 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.
@@ -1796,7 +1742,8 @@ int of_add_node(const char *path, struct
 		return -EINVAL; /* could also be ENOMEM, though */
 	}
 
-	if (0 != (err = of_finish_dynamic_node(np))) {
+	err = finish_node(np, NULL, of_finish_dynamic_node, 0, 0, 0);
+	if (err < 0) {
 		kfree(np);
 		return err;
 	}



More information about the Linuxppc64-dev mailing list