This patch improves the implementation of device tree structure. Traditional child and sibling linked list in device tree structure is removed and is replaced by list_head (i.e. circular doubly linked list) already available in Linux kernel. Signed-off-by: Gaurav Minocha <gaurav.minocha.os@xxxxxxxxx> --- drivers/of/base.c | 36 ++++++++++---- drivers/of/dynamic.c | 18 ++----- drivers/of/fdt.c | 7 ++- drivers/of/selftest.c | 130 +++++++++++++++++++++++--------------------------- include/linux/of.h | 4 +- 5 files changed, 97 insertions(+), 98 deletions(-) diff --git a/drivers/of/base.c b/drivers/of/base.c index 2305dc0..7acaeca 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -603,16 +603,25 @@ EXPORT_SYMBOL(of_get_next_parent); static struct device_node *__of_get_next_child(const struct device_node *node, struct device_node *prev) { - struct device_node *next; + struct device_node *next = NULL, *np; + struct list_head *pos; + int found = 0; if (!node) return NULL; - next = prev ? prev->sibling : node->child; - for (; next; next = next->sibling) - if (of_node_get(next)) - break; + // move through the children list, and find the next node + list_for_each(pos, &node->children) { + np = list_entry(pos, struct device_node, siblings); + if ((!prev || (found == 1)) && of_node_get(np)) { + next = np; + break; + } + if (prev && strcmp(np->full_name, prev->full_name) == 0) + found = 1; + } of_node_put(prev); + return next; } #define __for_each_child_of_node(parent, child) \ @@ -651,22 +660,29 @@ EXPORT_SYMBOL(of_get_next_child); struct device_node *of_get_next_available_child(const struct device_node *node, struct device_node *prev) { - struct device_node *next; + struct device_node *next = NULL, *np; + struct list_head *pos; unsigned long flags; + int found = 0; if (!node) return NULL; raw_spin_lock_irqsave(&devtree_lock, flags); - next = prev ? prev->sibling : node->child; - for (; next; next = next->sibling) { - if (!__of_device_is_available(next)) + list_for_each(pos, &node->children) { + np = list_entry(pos, struct device_node, siblings); + if (!__of_device_is_available(np)) continue; - if (of_node_get(next)) + if ((!prev || (found == 1)) && of_node_get(np)) { + next = np; break; + } + if (prev && strcmp(np->full_name, prev->full_name) == 0) + found = 1; } of_node_put(prev); raw_spin_unlock_irqrestore(&devtree_lock, flags); + return next; } EXPORT_SYMBOL(of_get_next_available_child); diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index f297891..3ffc726 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -115,11 +115,12 @@ void __of_attach_node(struct device_node *np) phandle = __of_get_property(np, "ibm,phandle", &sz); np->phandle = (phandle && (sz >= 4)) ? be32_to_cpup(phandle) : 0; - np->child = NULL; - np->sibling = np->parent->child; + INIT_LIST_HEAD(&np->siblings); + INIT_LIST_HEAD(&np->children); + list_add_tail(&(np->siblings), &(np->parent->children)); + np->allnext = np->parent->allnext; np->parent->allnext = np; - np->parent->child = np; of_node_clear_flag(np, OF_DETACHED); } @@ -165,16 +166,7 @@ void __of_detach_node(struct device_node *np) prev->allnext = np->allnext; } - if (parent->child == np) - parent->child = np->sibling; - else { - struct device_node *prevsib; - for (prevsib = np->parent->child; - prevsib->sibling != np; - prevsib = prevsib->sibling) - ; - prevsib->sibling = np->sibling; - } + list_del(&np->siblings); of_node_set_flag(np, OF_DETACHED); } diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index d1ffca8..4cf2356 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -224,13 +224,12 @@ static void * unflatten_dt_node(void *blob, prev_pp = &np->properties; **allnextpp = np; *allnextpp = &np->allnext; + INIT_LIST_HEAD(&np->siblings); + INIT_LIST_HEAD(&np->children); if (dad != NULL) { np->parent = dad; /* we temporarily use the next field as `last_child'*/ - if (dad->next == NULL) - dad->child = np; - else - dad->next->sibling = np; + list_add_tail(&(np->siblings), &(dad->children)); dad->next = np; } } diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index 7800127..8ec8a22 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -25,10 +25,11 @@ static struct selftest_results { int failed; } selftest_results; -#define NO_OF_NODES 3 -static struct device_node *nodes[NO_OF_NODES]; -static int last_node_index; +#define MAX_NODES 50 + +static int no_of_nodes; static bool selftest_live_tree; +static struct device_node* nodes[MAX_NODES]; #define selftest(result, fmt, ...) { \ if (!(result)) { \ @@ -417,7 +418,6 @@ static void __init of_selftest_changeset(void) n1->parent = parent; n2->parent = parent; n21->parent = n2; - n2->child = n21; ppremove = of_find_property(parent, "prop-remove", NULL); selftest(ppremove, "failed to find removal prop"); @@ -713,44 +713,34 @@ static void update_node_properties(struct device_node *np, child->parent = dup; } -/** - * attach_node_and_children - attaches nodes - * and its children to live tree - * - * @np: Node to attach to live tree - */ -static int attach_node_and_children(struct device_node *np) -{ - struct device_node *next, *root = np, *dup; - /* skip root node */ - np = np->child; - /* storing a copy in temporary node */ - dup = np; +static void populate_node_list(struct device_node *np) +{ + struct list_head *pos; - while (dup) { - if (WARN_ON(last_node_index >= NO_OF_NODES)) - return -EINVAL; - nodes[last_node_index++] = dup; - dup = dup->sibling; + list_for_each(pos, &np->children) { + nodes[no_of_nodes] = list_entry(pos, struct device_node, siblings); + populate_node_list(nodes[no_of_nodes++]); } - dup = NULL; +} - while (np) { - next = np->allnext; - dup = of_find_node_by_path(np->full_name); +static void attach_node_and_children(struct device_node *np) +{ + struct device_node *root = np, *dup, *temp; + int i; + + populate_node_list(np); + + for (i = 0; i < no_of_nodes && nodes[i]; i++) { + temp = nodes[i]; + if (temp->parent == root) + temp->parent = of_allnodes; + dup = of_find_node_by_path(temp->full_name); if (dup) - update_node_properties(np, dup); - else { - np->child = NULL; - if (np->parent == root) - np->parent = of_allnodes; - of_attach_node(np); - } - np = next; + update_node_properties(temp, dup); + else + of_attach_node(temp); } - - return 0; } /** @@ -764,7 +754,8 @@ static int __init selftest_data_add(void) extern uint8_t __dtb_testcases_begin[]; extern uint8_t __dtb_testcases_end[]; const int size = __dtb_testcases_end - __dtb_testcases_begin; - int rc; + struct list_head *pos; + int rc, i = 0; if (!size) { pr_warn("%s: No testcase data to attach; not running tests\n", @@ -796,65 +787,66 @@ static int __init selftest_data_add(void) /* enabling flag for removing nodes */ selftest_live_tree = true; of_allnodes = selftest_data_node; + populate_node_list(selftest_data_node); + + // attach the root node + __of_attach_node_sysfs(selftest_data_node); + + while (i < no_of_nodes && nodes[i]) + __of_attach_node_sysfs(nodes[i++]); - for_each_of_allnodes(np) - __of_attach_node_sysfs(np); of_aliases = of_find_node_by_path("/aliases"); of_chosen = of_find_node_by_path("/chosen"); return 0; } + list_for_each(pos, &selftest_data_node->children) { + np = list_entry(pos, struct device_node, siblings); + np->parent = of_allnodes; + } + + /* attach the sub-tree to live tree */ - return attach_node_and_children(selftest_data_node); -} + attach_node_and_children(selftest_data_node); -/** - * detach_node_and_children - detaches node - * and its children from live tree - * - * @np: Node to detach from live tree - */ -static void detach_node_and_children(struct device_node *np) -{ - while (np->child) - detach_node_and_children(np->child); - of_detach_node(np); + return 0; } -/** - * selftest_data_remove - removes the selftest data - * nodes from the live tree - */ static void selftest_data_remove(void) { - struct device_node *np; struct property *prop; + struct device_node *np, *temp; + int i = no_of_nodes; if (selftest_live_tree) { of_node_put(of_aliases); of_node_put(of_chosen); of_aliases = NULL; of_chosen = NULL; - for_each_child_of_node(of_allnodes, np) - detach_node_and_children(np); + while (i >= 0) { + temp = nodes[i--]; + if (temp) + of_detach_node(temp); + } __of_detach_node_sysfs(of_allnodes); of_allnodes = NULL; return; } - while (last_node_index >= 0) { - if (nodes[last_node_index]) { - np = of_find_node_by_path(nodes[last_node_index]->full_name); - if (strcmp(np->full_name, "/aliases") != 0) { - detach_node_and_children(np); - } else { - for_each_property_of_node(np, prop) { - if (strcmp(prop->name, "testcase-alias") == 0) + while (i >= 0) { + temp = nodes[i--]; + if (temp) { + if (strcmp(temp->full_name, "/aliases") == 0) { + for_each_property_of_node(temp, prop) { + if (strcmp(prop->name, "testcase-alias") == 0) { + np = of_find_node_by_path(temp->full_name); of_remove_property(np, prop); + } } } + else + of_detach_node(temp); } - last_node_index--; } } @@ -865,6 +857,7 @@ static int __init of_selftest(void) /* adding data for selftest */ res = selftest_data_add(); + if (res) return res; @@ -891,7 +884,6 @@ static int __init of_selftest(void) /* removing selftest data from live tree */ selftest_data_remove(); - /* Double check linkage after removing testcase data */ of_selftest_check_tree_linkage(); diff --git a/include/linux/of.h b/include/linux/of.h index 6545e7a..84c708c 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -53,10 +53,10 @@ struct device_node { struct property *properties; struct property *deadprops; /* removed properties */ struct device_node *parent; - 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 children; + struct list_head siblings; struct kobject kobj; unsigned long _flags; void *data; -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html