On Tue, Nov 18, 2014 at 7:10 AM, Grant Likely <grant.likely@xxxxxxxxxx> wrote: > On Sun, 16 Nov 2014 20:52:56 -0800 > , Gaurav Minocha <gaurav.minocha.os@xxxxxxxxx> > wrote: >> 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> > > Hi Gaurav, > > So, after you've done this work, I need to you rebase it (and of course > it is non-trivial) onto linux-next. I've already got a patch queued up > which gets rid of the of_allnodes/allnext list which will have conflicts > with this patch. > I am using http://git.kernel.org/pub/scm/linux/kernel/git/glikely/linux.git There is no branch linux-next in it. Also, I believe http://git.secretlab.ca/ is not up yet. Please correct. > I'll make comments below where still relevant. > >> --- >> 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; > > What is this 'found' stuff about? Why is this in the loop? I think with your suggestions below, it will definitely change. Considering the fact that it is a doubly linked list. First, I try to locate the pre node in the list. If found, then only I call of_node_get() on the next node, and if successful I break. I used found in the loop so that I do not have to manually check the next node is head or not. I can follow another approach, by directly getting the next entry and failing it if it is the head node. > >> + } >> of_node_put(prev); > > Most of this code can be eliminated by using list_for_each_entry(). > Actually, the use of a loop here in the original code is a little bit > goofy simply because it never actually loops. It always exits on the > first iteration, but it does conveniently calculate the next entry in > the list. The thing to do here is take inspiration from the > list_for_each_entry() implementation and do the following: > > if (!prev) { > next = list_first_entry_or_null(&node->children, struct device_node, siblings); > } else if (prev->siblings.next != &node->children) > next = list_next_entry(prev, siblings); > } else { > next = NULL; > } > >> + >> 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) { > > Use list_for_each_entry_continue(), but there will be a bit of fiddling > to make sure the first entry in the list is handled correctly. You can > handle this by using list_entry on the &node->children list, but be > careful to never actually dereference that pointer. > >> + 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]; > > I think it's time to rework this structure. Instead of a static list, > the code should figure out how many entries it needs and then kzalloc() > the region. Ok. > >> >> #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