The device tree data structure uses a custom linked list implementation
which is baroque and prone to bugs. Replace the allnodes list with a
list_head and the common list_head accessor functions.

At the same time, move the private for_each_of_allnodes() macro into
drivers/of/base.c. It must not be used outside of the core code because
the macro doesn't take into account device tree locking.

Signed-off-by: Grant Likely <[email protected]>
Cc: Rob Herring <[email protected]>
---
 arch/arm/mach-vexpress/v2m.c        |   2 +-
 drivers/of/base.c                   | 126 +++++++++++++++++++-----------------
 drivers/of/fdt.c                    |  27 ++++----
 drivers/of/pdt.c                    |  26 +++-----
 drivers/pci/hotplug/rpadlpar_core.c |   2 +-
 include/linux/of.h                  |   9 ++-
 include/linux/of_fdt.h              |   3 +-
 include/linux/of_pdt.h              |   3 +-
 8 files changed, 96 insertions(+), 102 deletions(-)

diff --git a/arch/arm/mach-vexpress/v2m.c b/arch/arm/mach-vexpress/v2m.c
index 4f8b8cb17ff5..57151ba5edee 100644
--- a/arch/arm/mach-vexpress/v2m.c
+++ b/arch/arm/mach-vexpress/v2m.c
@@ -412,7 +412,7 @@ void __init v2m_dt_init_early(void)
        vexpress_sysreg_of_early_init();
 
        /* Confirm board type against DT property, if available */
-       if (of_property_read_u32(of_allnodes, "arm,hbi", &dt_hbi) == 0) {
+       if (of_property_read_u32(of_root, "arm,hbi", &dt_hbi) == 0) {
                u32 hbi = vexpress_get_hbi(VEXPRESS_SITE_MASTER);
 
                if (WARN_ON(dt_hbi != hbi))
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 567e6e1b7921..6c0bd86b802f 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -31,14 +31,22 @@
 
 LIST_HEAD(aliases_lookup);
 
-struct device_node *of_allnodes;
+LIST_HEAD(of_allnodes);
 EXPORT_SYMBOL(of_allnodes);
-struct device_node *of_chosen;
-struct device_node *of_aliases;
+struct device_node *of_root = NULL;
+struct device_node *of_chosen = NULL;
+struct device_node *of_aliases = NULL;
 static struct device_node *of_stdout;
 
 static struct kset *of_kset;
 
+#define of_allnodes_prepare(dn) \
+       list_prepare_entry(dn, &of_allnodes, allnext)
+#define for_each_of_allnodes(dn) \
+       list_for_each_entry(dn, &of_allnodes, allnext)
+#define for_each_of_allnodes_continue(dn) \
+       list_for_each_entry_continue(dn, &of_allnodes, allnext)
+
 /*
  * Used to protect the of_aliases; but also overloaded to hold off addition of
  * nodes to sysfs
@@ -299,7 +307,7 @@ static int __init of_init(void)
        mutex_unlock(&of_aliases_mutex);
 
        /* Symlink in /proc as required by userspace ABI */
-       if (of_allnodes)
+       if (of_root)
                proc_symlink("device-tree", NULL, 
"/sys/firmware/devicetree/base");
 
        return 0;
@@ -348,18 +356,18 @@ EXPORT_SYMBOL(of_find_property);
  * Returns a node pointer with refcount incremented, use
  * of_node_put() on it when done.
  */
-struct device_node *of_find_all_nodes(struct device_node *prev)
+struct device_node *of_find_all_nodes(struct device_node *np)
 {
-       struct device_node *np;
        unsigned long flags;
 
        raw_spin_lock_irqsave(&devtree_lock, flags);
-       np = prev ? prev->allnext : of_allnodes;
-       for (; np != NULL; np = np->allnext)
-               if (of_node_get(np))
-                       break;
-       of_node_put(prev);
+       of_node_put(np);
+       np = list_next_entry(of_allnodes_prepare(np), allnext);
+       if (&np->allnext == &of_allnodes)
+               np = NULL;
+       of_node_get(np);
        raw_spin_unlock_irqrestore(&devtree_lock, flags);
+
        return np;
 }
 EXPORT_SYMBOL(of_find_all_nodes);
@@ -831,7 +839,7 @@ struct device_node *of_find_node_by_path(const char *path)
        unsigned long flags;
 
        if (strcmp(path, "/") == 0)
-               return of_node_get(of_allnodes);
+               return of_node_get(of_root);
 
        /* The path could begin with an alias */
        if (*path != '/') {
@@ -856,7 +864,7 @@ struct device_node *of_find_node_by_path(const char *path)
        /* Step down the tree matching path components */
        raw_spin_lock_irqsave(&devtree_lock, flags);
        if (!np)
-               np = of_node_get(of_allnodes);
+               np = of_node_get(of_root);
        while (np && *path == '/') {
                path++; /* Increment past '/' delimiter */
                np = __of_find_node_by_path(np, path);
@@ -881,16 +889,18 @@ EXPORT_SYMBOL(of_find_node_by_path);
 struct device_node *of_find_node_by_name(struct device_node *from,
        const char *name)
 {
-       struct device_node *np;
+       struct device_node *np = NULL;
        unsigned long flags;
 
        raw_spin_lock_irqsave(&devtree_lock, flags);
-       np = from ? from->allnext : of_allnodes;
-       for (; np; np = np->allnext)
-               if (np->name && (of_node_cmp(np->name, name) == 0)
-                   && of_node_get(np))
-                       break;
        of_node_put(from);
+       from = of_allnodes_prepare(from);
+       for_each_of_allnodes_continue(from) {
+               if (from->name && (of_node_cmp(from->name, name) == 0)) {
+                       np = of_node_get(from);
+                       break;
+               }
+       }
        raw_spin_unlock_irqrestore(&devtree_lock, flags);
        return np;
 }
@@ -911,16 +921,18 @@ EXPORT_SYMBOL(of_find_node_by_name);
 struct device_node *of_find_node_by_type(struct device_node *from,
        const char *type)
 {
-       struct device_node *np;
+       struct device_node *np = NULL;
        unsigned long flags;
 
        raw_spin_lock_irqsave(&devtree_lock, flags);
-       np = from ? from->allnext : of_allnodes;
-       for (; np; np = np->allnext)
-               if (np->type && (of_node_cmp(np->type, type) == 0)
-                   && of_node_get(np))
-                       break;
        of_node_put(from);
+       from = of_allnodes_prepare(from);
+       for_each_of_allnodes_continue(from) {
+               if (from->type && (of_node_cmp(from->type, type) == 0)) {
+                       np = of_node_get(from);
+                       break;
+               }
+       }
        raw_spin_unlock_irqrestore(&devtree_lock, flags);
        return np;
 }
@@ -943,18 +955,20 @@ EXPORT_SYMBOL(of_find_node_by_type);
 struct device_node *of_find_compatible_node(struct device_node *from,
        const char *type, const char *compatible)
 {
-       struct device_node *np;
+       struct device_node *np = NULL;
        unsigned long flags;
 
        raw_spin_lock_irqsave(&devtree_lock, flags);
-       np = from ? from->allnext : of_allnodes;
-       for (; np; np = np->allnext) {
-               if (__of_device_is_compatible(np, compatible, type, NULL) &&
-                   of_node_get(np))
+       of_node_put(from);
+       from = of_allnodes_prepare(from);
+       for_each_of_allnodes_continue(from) {
+               if (__of_device_is_compatible(from, compatible, type, NULL)) {
+                       np = of_node_get(from);
                        break;
+               }
        }
-       of_node_put(from);
        raw_spin_unlock_irqrestore(&devtree_lock, flags);
+
        return np;
 }
 EXPORT_SYMBOL(of_find_compatible_node);
@@ -974,22 +988,22 @@ EXPORT_SYMBOL(of_find_compatible_node);
 struct device_node *of_find_node_with_property(struct device_node *from,
        const char *prop_name)
 {
-       struct device_node *np;
+       struct device_node *np = NULL;
        struct property *pp;
        unsigned long flags;
 
        raw_spin_lock_irqsave(&devtree_lock, flags);
-       np = from ? from->allnext : of_allnodes;
-       for (; np; np = np->allnext) {
-               for (pp = np->properties; pp; pp = pp->next) {
+       of_node_put(from);
+       from = of_allnodes_prepare(from);
+       for_each_of_allnodes_continue(from) {
+               for (pp = from->properties; pp; pp = pp->next) {
                        if (of_prop_cmp(pp->name, prop_name) == 0) {
-                               of_node_get(np);
+                               np = of_node_get(from);
                                goto out;
                        }
                }
        }
-out:
-       of_node_put(from);
+ out:
        raw_spin_unlock_irqrestore(&devtree_lock, flags);
        return np;
 }
@@ -1054,7 +1068,7 @@ struct device_node 
*of_find_matching_node_and_match(struct device_node *from,
                                        const struct of_device_id *matches,
                                        const struct of_device_id **match)
 {
-       struct device_node *np;
+       struct device_node *np = NULL;
        const struct of_device_id *m;
        unsigned long flags;
 
@@ -1062,16 +1076,16 @@ struct device_node 
*of_find_matching_node_and_match(struct device_node *from,
                *match = NULL;
 
        raw_spin_lock_irqsave(&devtree_lock, flags);
-       np = from ? from->allnext : of_allnodes;
-       for (; np; np = np->allnext) {
-               m = __of_match_node(matches, np);
-               if (m && of_node_get(np)) {
+       of_node_put(from);
+       from = of_allnodes_prepare(from);
+       for_each_of_allnodes_continue(from) {
+               if ((m = __of_match_node(matches, from))) {
+                       np = of_node_get(from);
                        if (match)
                                *match = m;
                        break;
                }
        }
-       of_node_put(from);
        raw_spin_unlock_irqrestore(&devtree_lock, flags);
        return np;
 }
@@ -1113,14 +1127,16 @@ EXPORT_SYMBOL_GPL(of_modalias_node);
  */
 struct device_node *of_find_node_by_phandle(phandle handle)
 {
-       struct device_node *np;
+       struct device_node *tmp, *np = NULL;
        unsigned long flags;
 
        raw_spin_lock_irqsave(&devtree_lock, flags);
-       for (np = of_allnodes; np; np = np->allnext)
-               if (np->phandle == handle)
+       for_each_of_allnodes(tmp) {
+               if (tmp->phandle == handle) {
+                       np = of_node_get(tmp);
                        break;
-       of_node_get(np);
+               }
+       }
        raw_spin_unlock_irqrestore(&devtree_lock, flags);
        return np;
 }
@@ -1959,10 +1975,9 @@ int of_attach_node(struct device_node *np)
                return rc;
 
        raw_spin_lock_irqsave(&devtree_lock, flags);
+       list_add_tail(&np->allnext, &of_allnodes);
        np->sibling = np->parent->child;
-       np->allnext = of_allnodes;
        np->parent->child = np;
-       of_allnodes = np;
        of_node_clear_flag(np, OF_DETACHED);
        raw_spin_unlock_irqrestore(&devtree_lock, flags);
 
@@ -2000,16 +2015,7 @@ int of_detach_node(struct device_node *np)
                return rc;
        }
 
-       if (of_allnodes == np)
-               of_allnodes = np->allnext;
-       else {
-               struct device_node *prev;
-               for (prev = of_allnodes;
-                    prev->allnext != np;
-                    prev = prev->allnext)
-                       ;
-               prev->allnext = np->allnext;
-       }
+       list_del(&np->allnext);
 
        if (parent->child == np)
                parent->child = np->sibling;
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 17be90f5445f..6876ac3d4b30 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -96,14 +96,14 @@ static void *unflatten_dt_alloc(void **mem, unsigned long 
size,
  * @mem: Memory chunk to use for allocating device nodes and properties
  * @p: pointer to node in flat tree
  * @dad: Parent struct device_node
- * @allnextpp: pointer to ->allnext from last allocated device_node
+ * @allnext: list of ->allnext from this tree
  * @fpsize: Size of the node path up at the current depth.
  */
 static void * unflatten_dt_node(void *blob,
                                void *mem,
                                int *poffset,
                                struct device_node *dad,
-                               struct device_node ***allnextpp,
+                               struct list_head *allnext,
                                unsigned long fpsize)
 {
        const __be32 *p;
@@ -151,7 +151,7 @@ static void * unflatten_dt_node(void *blob,
 
        np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
                                __alignof__(struct device_node));
-       if (allnextpp) {
+       if (allnext) {
                char *fn;
                of_node_init(np);
                np->full_name = fn = ((char *)np) + sizeof(*np);
@@ -173,8 +173,7 @@ static void * unflatten_dt_node(void *blob,
                memcpy(fn, pathp, l);
 
                prev_pp = &np->properties;
-               **allnextpp = np;
-               *allnextpp = &np->allnext;
+               list_add_tail(&np->allnext, allnext);
                if (dad != NULL) {
                        np->parent = dad;
                        /* we temporarily use the next field as `last_child'*/
@@ -205,7 +204,7 @@ static void * unflatten_dt_node(void *blob,
                        has_name = 1;
                pp = unflatten_dt_alloc(&mem, sizeof(struct property),
                                        __alignof__(struct property));
-               if (allnextpp) {
+               if (allnext) {
                        /* We accept flattened tree phandles either in
                         * ePAPR-style "phandle" properties, or the
                         * legacy "linux,phandle" properties.  If both
@@ -247,7 +246,7 @@ static void * unflatten_dt_node(void *blob,
                sz = (pa - ps) + 1;
                pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
                                        __alignof__(struct property));
-               if (allnextpp) {
+               if (allnext) {
                        pp->name = "name";
                        pp->length = sz;
                        pp->value = pp + 1;
@@ -259,7 +258,7 @@ static void * unflatten_dt_node(void *blob,
                                (char *)pp->value);
                }
        }
-       if (allnextpp) {
+       if (allnext) {
                *prev_pp = NULL;
                np->name = of_get_property(np, "name", NULL);
                np->type = of_get_property(np, "device_type", NULL);
@@ -275,7 +274,7 @@ static void * unflatten_dt_node(void *blob,
        if (depth < 0)
                depth = 0;
        while (*poffset > 0 && depth > old_depth)
-               mem = unflatten_dt_node(blob, mem, poffset, np, allnextpp,
+               mem = unflatten_dt_node(blob, mem, poffset, np, allnext,
                                        fpsize);
 
        if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND)
@@ -297,13 +296,12 @@ static void * unflatten_dt_node(void *blob,
  * for the resulting tree
  */
 static void __unflatten_device_tree(void *blob,
-                            struct device_node **mynodes,
+                            struct list_head *allnext,
                             void * (*dt_alloc)(u64 size, u64 align))
 {
        unsigned long size;
        int start;
        void *mem;
-       struct device_node **allnextp = mynodes;
 
        pr_debug(" -> unflatten_device_tree()\n");
 
@@ -339,11 +337,10 @@ static void __unflatten_device_tree(void *blob,
 
        /* Second pass, do actual unflattening */
        start = 0;
-       unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0);
+       unflatten_dt_node(blob, mem, &start, NULL, allnext, 0);
        if (be32_to_cpup(mem + size) != 0xdeadbeef)
                pr_warning("End of tree marker overwritten: %08x\n",
                           be32_to_cpup(mem + size));
-       *allnextp = NULL;
 
        pr_debug(" <- unflatten_device_tree()\n");
 }
@@ -361,8 +358,7 @@ static void *kernel_tree_alloc(u64 size, u64 align)
  * pointers of the nodes so the normal device-tree walking functions
  * can be used.
  */
-void of_fdt_unflatten_tree(unsigned long *blob,
-                       struct device_node **mynodes)
+void of_fdt_unflatten_tree(unsigned long *blob, struct list_head *mynodes)
 {
        __unflatten_device_tree(blob, mynodes, &kernel_tree_alloc);
 }
@@ -904,6 +900,7 @@ void __init unflatten_device_tree(void)
 {
        __unflatten_device_tree(initial_boot_params, &of_allnodes,
                                early_init_dt_alloc_memory_arch);
+       of_root = list_first_entry_or_null(&of_allnodes, struct device_node, 
allnext);
 
        /* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
        of_alias_scan(early_init_dt_alloc_memory_arch);
diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c
index 36b4035881b0..38dd814870b4 100644
--- a/drivers/of/pdt.c
+++ b/drivers/of/pdt.c
@@ -25,8 +25,7 @@
 
 static struct of_pdt_ops *of_pdt_prom_ops __initdata;
 
-void __initdata (*of_pdt_build_more)(struct device_node *dp,
-               struct device_node ***nextp);
+void __initdata (*of_pdt_build_more)(struct device_node *dp);
 
 #if defined(CONFIG_SPARC)
 unsigned int of_pdt_unique_id __initdata;
@@ -192,8 +191,7 @@ static struct device_node * __init 
of_pdt_create_node(phandle node,
 }
 
 static struct device_node * __init of_pdt_build_tree(struct device_node 
*parent,
-                                                  phandle node,
-                                                  struct device_node ***nextp)
+                                                  phandle node)
 {
        struct device_node *ret = NULL, *prev_sibling = NULL;
        struct device_node *dp;
@@ -210,16 +208,15 @@ static struct device_node * __init 
of_pdt_build_tree(struct device_node *parent,
                        ret = dp;
                prev_sibling = dp;
 
-               *(*nextp) = dp;
-               *nextp = &dp->allnext;
+               list_add_tail(&of_allnodes, &np->allnext);
 
                dp->full_name = of_pdt_build_full_name(dp);
 
                dp->child = of_pdt_build_tree(dp,
-                               of_pdt_prom_ops->getchild(node), nextp);
+                               of_pdt_prom_ops->getchild(node));
 
                if (of_pdt_build_more)
-                       of_pdt_build_more(dp, nextp);
+                       of_pdt_build_more(dp);
 
                node = of_pdt_prom_ops->getsibling(node);
        }
@@ -234,20 +231,17 @@ static void * __init kernel_tree_alloc(u64 size, u64 
align)
 
 void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops)
 {
-       struct device_node **nextp;
-
        BUG_ON(!ops);
        of_pdt_prom_ops = ops;
 
-       of_allnodes = of_pdt_create_node(root_node, NULL);
+       of_root = of_pdt_create_node(root_node, NULL);
 #if defined(CONFIG_SPARC)
-       of_allnodes->path_component_name = "";
+       of_root->path_component_name = "";
 #endif
-       of_allnodes->full_name = "/";
+       of_root->full_name = "/";
 
-       nextp = &of_allnodes->allnext;
-       of_allnodes->child = of_pdt_build_tree(of_allnodes,
-                       of_pdt_prom_ops->getchild(of_allnodes->phandle), 
&nextp);
+       list_add_tail(&of_allnodes, &of_root->allnext);
+       of_root->child = of_pdt_build_tree(of_root, 
of_pdt_prom_ops->getchild(of_root->phandle));
 
        /* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
        of_alias_scan(kernel_tree_alloc);
diff --git a/drivers/pci/hotplug/rpadlpar_core.c 
b/drivers/pci/hotplug/rpadlpar_core.c
index 4fcdeedda31b..e25e4d1ec241 100644
--- a/drivers/pci/hotplug/rpadlpar_core.c
+++ b/drivers/pci/hotplug/rpadlpar_core.c
@@ -68,7 +68,7 @@ static struct device_node *find_php_slot_pci_node(char 
*drc_name,
        char *type;
        int rc;
 
-       while ((np = of_find_node_by_name(np, "pci"))) {
+       for_each_node_by_name(np, "pci") {
                rc = rpaphp_get_drc_props(np, NULL, &name, &type, NULL);
                if (rc == 0)
                        if (!strcmp(drc_name, name) && !strcmp(drc_type, type))
diff --git a/include/linux/of.h b/include/linux/of.h
index 3bad8d106e0e..e082db3c284f 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -56,7 +56,7 @@ struct device_node {
        struct  device_node *child;
        struct  device_node *sibling;
        struct  device_node *next;      /* next device of same type */
-       struct  device_node *allnext;   /* next in list of all nodes */
+       struct  list_head allnext;
        struct  kobject kobj;
        unsigned long _flags;
        void    *data;
@@ -110,14 +110,15 @@ static inline void of_node_put(struct device_node *node) 
{ }
 #ifdef CONFIG_OF
 
 /* Pointer for first entry in chain of all nodes. */
-extern struct device_node *of_allnodes;
+extern struct list_head of_allnodes;
+extern struct device_node *of_root;
 extern struct device_node *of_chosen;
 extern struct device_node *of_aliases;
 extern raw_spinlock_t devtree_lock;
 
 static inline bool of_have_populated_dt(void)
 {
-       return of_allnodes != NULL;
+       return !list_empty(&of_allnodes);
 }
 
 static inline bool of_node_is_root(const struct device_node *node)
@@ -208,8 +209,6 @@ static inline const char *of_node_full_name(const struct 
device_node *np)
        return np ? np->full_name : "<no-node>";
 }
 
-#define for_each_of_allnodes(dn) \
-       for (dn = of_allnodes; dn; dn = dn->allnext)
 extern struct device_node *of_find_node_by_name(struct device_node *from,
        const char *name);
 extern struct device_node *of_find_node_by_type(struct device_node *from,
diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h
index 5c0ab057eecf..efa7615a1ce9 100644
--- a/include/linux/of_fdt.h
+++ b/include/linux/of_fdt.h
@@ -35,8 +35,7 @@ extern int of_fdt_is_compatible(const void *blob,
                                const char *compat);
 extern int of_fdt_match(const void *blob, unsigned long node,
                        const char *const *compat);
-extern void of_fdt_unflatten_tree(unsigned long *blob,
-                              struct device_node **mynodes);
+extern void of_fdt_unflatten_tree(unsigned long *blob, struct list_head 
*mynodes);
 
 /* TBD: Temporary export of fdt globals - remove when code fully merged */
 extern int __initdata dt_root_addr_cells;
diff --git a/include/linux/of_pdt.h b/include/linux/of_pdt.h
index c65a18a0cfdf..7e09244bb679 100644
--- a/include/linux/of_pdt.h
+++ b/include/linux/of_pdt.h
@@ -39,7 +39,6 @@ extern void *prom_early_alloc(unsigned long size);
 /* for building the device tree */
 extern void of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops);
 
-extern void (*of_pdt_build_more)(struct device_node *dp,
-               struct device_node ***nextp);
+extern void (*of_pdt_build_more)(struct device_node *dp);
 
 #endif /* _LINUX_OF_PDT_H */
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to