From e3963fd60a83967573f6330c9db134bd581da746 Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Fri, 8 Nov 2013 17:03:57 +0200 Subject: of: Clear detach flag on attach When attaching a node always clear the detach flag. Without this change the sequence detach, attach fails. Signed-off-by: Pantelis Antoniou Signed-off-by: Grant Likely --- drivers/of/base.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/of/base.c') diff --git a/drivers/of/base.c b/drivers/of/base.c index ff85450d5683..87038d80b473 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1692,6 +1692,7 @@ int of_attach_node(struct device_node *np) 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); of_add_proc_dt_entry(np); -- cgit v1.2.3 From 62664f67775fad840cf6f68d6b5f428817bef6c5 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 22 Jan 2014 13:57:39 +0800 Subject: of: add __of_add_property() without lock operations There two places will use the same code for adding one new property to the DT node. Adding __of_add_property() and prepare for fixing of_update_property()'s bug. Signed-off-by: Xiubo Li Signed-off-by: Grant Likely --- drivers/of/base.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) (limited to 'drivers/of/base.c') diff --git a/drivers/of/base.c b/drivers/of/base.c index 87038d80b473..6ad3dc976b18 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1492,12 +1492,32 @@ static int of_property_notify(int action, struct device_node *np, } #endif +/** + * __of_add_property - Add a property to a node without lock operations + */ +static int __of_add_property(struct device_node *np, struct property *prop) +{ + struct property **next; + + prop->next = NULL; + next = &np->properties; + while (*next) { + if (strcmp(prop->name, (*next)->name) == 0) + /* duplicate ! don't insert it */ + return -EEXIST; + + next = &(*next)->next; + } + *next = prop; + + return 0; +} + /** * of_add_property - Add a property to a node */ int of_add_property(struct device_node *np, struct property *prop) { - struct property **next; unsigned long flags; int rc; @@ -1505,27 +1525,17 @@ int of_add_property(struct device_node *np, struct property *prop) if (rc) return rc; - prop->next = NULL; raw_spin_lock_irqsave(&devtree_lock, flags); - next = &np->properties; - while (*next) { - if (strcmp(prop->name, (*next)->name) == 0) { - /* duplicate ! don't insert it */ - raw_spin_unlock_irqrestore(&devtree_lock, flags); - return -1; - } - next = &(*next)->next; - } - *next = prop; + rc = __of_add_property(np, prop); raw_spin_unlock_irqrestore(&devtree_lock, flags); #ifdef CONFIG_PROC_DEVICETREE /* try to add to proc as well if it was initialized */ - if (np->pde) + if (!rc && np->pde) proc_device_tree_add_prop(np->pde, prop); #endif /* CONFIG_PROC_DEVICETREE */ - return 0; + return rc; } /** -- cgit v1.2.3 From 02ed594e7113644c06ae3a89bc9215d839510efc Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 22 Jan 2014 13:57:40 +0800 Subject: of: fix of_update_property() The of_update_property() is intented to update a property in a node and if the property does not exist, will add it. The second search of the property is possibly won't be found, that maybe removed by other thread just before the second search begain. Using the __of_find_property() and __of_add_property() instead and move them into lock operations. Signed-off-by: Xiubo Li Cc: Pantelis Antoniou Signed-off-by: Grant Likely --- drivers/of/base.c | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) (limited to 'drivers/of/base.c') diff --git a/drivers/of/base.c b/drivers/of/base.c index 6ad3dc976b18..239e3da9e728 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1597,7 +1597,7 @@ int of_update_property(struct device_node *np, struct property *newprop) { struct property **next, *oldprop; unsigned long flags; - int rc, found = 0; + int rc = 0; rc = of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop); if (rc) @@ -1606,36 +1606,28 @@ int of_update_property(struct device_node *np, struct property *newprop) if (!newprop->name) return -EINVAL; - oldprop = of_find_property(np, newprop->name, NULL); - if (!oldprop) - return of_add_property(np, newprop); - raw_spin_lock_irqsave(&devtree_lock, flags); - next = &np->properties; - while (*next) { - if (*next == oldprop) { - /* found the node */ - newprop->next = oldprop->next; - *next = newprop; - oldprop->next = np->deadprops; - np->deadprops = oldprop; - found = 1; - break; - } - next = &(*next)->next; + oldprop = __of_find_property(np, newprop->name, NULL); + if (!oldprop) { + /* add the node */ + rc = __of_add_property(np, newprop); + } else { + /* replace the node */ + next = &oldprop; + newprop->next = oldprop->next; + *next = newprop; + oldprop->next = np->deadprops; + np->deadprops = oldprop; } raw_spin_unlock_irqrestore(&devtree_lock, flags); - if (!found) - return -ENODEV; - #ifdef CONFIG_PROC_DEVICETREE /* try to add to proc as well if it was initialized */ - if (np->pde) + if (!rc && np->pde) proc_device_tree_update_prop(np->pde, newprop, oldprop); #endif /* CONFIG_PROC_DEVICETREE */ - return 0; + return rc; } #if defined(CONFIG_OF_DYNAMIC) -- cgit v1.2.3 From a3dbeb5b45af5b6113385db89fce2a8279278e8b Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Tue, 4 Mar 2014 16:07:17 +0800 Subject: Revert "of: fix of_update_property()" This reverts commit 02ed594e7113644c06ae3a89bc9215d839510efc. The change is completely broken. It attempt to get the previous item in a linked list by grabbing the address of a stack variable. Outright wrong. Signed-off-by: Grant Likely --- drivers/of/base.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) (limited to 'drivers/of/base.c') diff --git a/drivers/of/base.c b/drivers/of/base.c index 239e3da9e728..a63e77694d0f 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1597,7 +1597,7 @@ int of_update_property(struct device_node *np, struct property *newprop) { struct property **next, *oldprop; unsigned long flags; - int rc = 0; + int rc, found = 0; rc = of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop); if (rc) @@ -1606,28 +1606,36 @@ int of_update_property(struct device_node *np, struct property *newprop) if (!newprop->name) return -EINVAL; + oldprop = of_find_property(np, newprop->name, NULL); + if (!oldprop) + return of_add_property(np, newprop); + raw_spin_lock_irqsave(&devtree_lock, flags); - oldprop = __of_find_property(np, newprop->name, NULL); - if (!oldprop) { - /* add the node */ - rc = __of_add_property(np, newprop); - } else { - /* replace the node */ - next = &oldprop; - newprop->next = oldprop->next; - *next = newprop; - oldprop->next = np->deadprops; - np->deadprops = oldprop; + next = &np->properties; + while (*next) { + if (*next == oldprop) { + /* found the node */ + newprop->next = oldprop->next; + *next = newprop; + oldprop->next = np->deadprops; + np->deadprops = oldprop; + found = 1; + break; + } + next = &(*next)->next; } raw_spin_unlock_irqrestore(&devtree_lock, flags); + if (!found) + return -ENODEV; + #ifdef CONFIG_PROC_DEVICETREE /* try to add to proc as well if it was initialized */ if (!rc && np->pde) proc_device_tree_update_prop(np->pde, newprop, oldprop); #endif /* CONFIG_PROC_DEVICETREE */ - return rc; + return 0; } #if defined(CONFIG_OF_DYNAMIC) -- cgit v1.2.3 From 75b57ecf9d1d1e17d099ab13b8f48e6e038676be Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 20 Feb 2014 18:02:11 +0000 Subject: of: Make device nodes kobjects so they show up in sysfs Device tree nodes are already treated as objects, and we already want to expose them to userspace which is done using the /proc filesystem today. Right now the kernel has to do a lot of work to keep the /proc view in sync with the in-kernel representation. If device_nodes are switched to be kobjects then the device tree code can be a whole lot simpler. It also turns out that switching to using /sysfs from /proc results in smaller code and data size, and the userspace ABI won't change if /proc/device-tree symlinks to /sys/firmware/devicetree/base. v7: Add missing sysfs_bin_attr_init() v6: Add __of_add_property() early init fixes from Pantelis v5: Rename firmware/ofw to firmware/devicetree Fix updating property values in sysfs v4: Fixed build error on Powerpc Fixed handling of dynamic nodes on powerpc v3: Fixed handling of duplicate attribute and child node names v2: switch to using sysfs bin_attributes which solve the problem of reporting incorrect property size. Signed-off-by: Grant Likely Tested-by: Sascha Hauer Cc: Rob Herring Cc: Benjamin Herrenschmidt Cc: David S. Miller Cc: Nathan Fontenot Cc: Pantelis Antoniou --- Documentation/ABI/testing/sysfs-firmware-ofw | 28 +++++ arch/powerpc/platforms/pseries/dlpar.c | 2 - arch/powerpc/platforms/pseries/reconfig.c | 2 - arch/powerpc/sysdev/msi_bitmap.c | 2 +- drivers/of/base.c | 174 +++++++++++++++++++++++++-- drivers/of/fdt.c | 3 +- drivers/of/pdt.c | 4 +- drivers/of/testcase-data/tests-phandle.dtsi | 3 + include/linux/of.h | 9 +- 9 files changed, 209 insertions(+), 18 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-firmware-ofw (limited to 'drivers/of/base.c') diff --git a/Documentation/ABI/testing/sysfs-firmware-ofw b/Documentation/ABI/testing/sysfs-firmware-ofw new file mode 100644 index 000000000000..f562b188e71d --- /dev/null +++ b/Documentation/ABI/testing/sysfs-firmware-ofw @@ -0,0 +1,28 @@ +What: /sys/firmware/devicetree/* +Date: November 2013 +Contact: Grant Likely +Description: + When using OpenFirmware or a Flattened Device Tree to enumerate + hardware, the device tree structure will be exposed in this + directory. + + It is possible for multiple device-tree directories to exist. + Some device drivers use a separate detached device tree which + have no attachment to the system tree and will appear in a + different subdirectory under /sys/firmware/devicetree. + + Userspace must not use the /sys/firmware/devicetree/base + path directly, but instead should follow /proc/device-tree + symlink. It is possible that the absolute path will change + in the future, but the symlink is the stable ABI. + + The /proc/device-tree symlink replaces the devicetree /proc + filesystem support, and has largely the same semantics and + should be compatible with existing userspace. + + The contents of /sys/firmware/devicetree/ is a + hierarchy of directories, one per device tree node. The + directory name is the resolved path component name (node + name plus address). Properties are represented as files + in the directory. The contents of each file is the exact + binary data from the device tree. diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c index a8fe5aa3d34f..022b38e6a80b 100644 --- a/arch/powerpc/platforms/pseries/dlpar.c +++ b/arch/powerpc/platforms/pseries/dlpar.c @@ -11,7 +11,6 @@ */ #include -#include #include #include #include @@ -87,7 +86,6 @@ static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa, } of_node_set_flag(dn, OF_DYNAMIC); - kref_init(&dn->kref); return dn; } diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index f93cdf55628c..0435bb65d0aa 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c @@ -12,7 +12,6 @@ */ #include -#include #include #include #include @@ -70,7 +69,6 @@ static int pSeries_reconfig_add_node(const char *path, struct property *proplist np->properties = proplist; of_node_set_flag(np, OF_DYNAMIC); - kref_init(&np->kref); np->parent = derive_parent(path); if (IS_ERR(np->parent)) { diff --git a/arch/powerpc/sysdev/msi_bitmap.c b/arch/powerpc/sysdev/msi_bitmap.c index 0968b66b4cf9..8ba60424be95 100644 --- a/arch/powerpc/sysdev/msi_bitmap.c +++ b/arch/powerpc/sysdev/msi_bitmap.c @@ -202,7 +202,7 @@ void __init test_of_node(void) /* There should really be a struct device_node allocator */ memset(&of_node, 0, sizeof(of_node)); - kref_init(&of_node.kref); + kref_init(&of_node.kobj.kref); of_node.full_name = node_name; check(0 == msi_bitmap_alloc(&bmp, size, &of_node)); diff --git a/drivers/of/base.c b/drivers/of/base.c index 48594f334151..3b70a468c8ab 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "of_private.h" @@ -35,6 +36,12 @@ struct device_node *of_chosen; struct device_node *of_aliases; static struct device_node *of_stdout; +static struct kset *of_kset; + +/* + * Used to protect the of_aliases; but also overloaded to hold off addition of + * nodes to sysfs + */ DEFINE_MUTEX(of_aliases_mutex); /* use when traversing tree through the allnext, child, sibling, @@ -92,14 +99,14 @@ int __weak of_node_to_nid(struct device_node *np) struct device_node *of_node_get(struct device_node *node) { if (node) - kref_get(&node->kref); + kobject_get(&node->kobj); return node; } EXPORT_SYMBOL(of_node_get); -static inline struct device_node *kref_to_device_node(struct kref *kref) +static inline struct device_node *kobj_to_device_node(struct kobject *kobj) { - return container_of(kref, struct device_node, kref); + return container_of(kobj, struct device_node, kobj); } /** @@ -109,16 +116,15 @@ static inline struct device_node *kref_to_device_node(struct kref *kref) * In of_node_put() this function is passed to kref_put() * as the destructor. */ -static void of_node_release(struct kref *kref) +static void of_node_release(struct kobject *kobj) { - struct device_node *node = kref_to_device_node(kref); + struct device_node *node = kobj_to_device_node(kobj); struct property *prop = node->properties; /* We should never be releasing nodes that haven't been detached. */ if (!of_node_check_flag(node, OF_DETACHED)) { pr_err("ERROR: Bad of_node_put() on %s\n", node->full_name); dump_stack(); - kref_init(&node->kref); return; } @@ -151,11 +157,140 @@ static void of_node_release(struct kref *kref) void of_node_put(struct device_node *node) { if (node) - kref_put(&node->kref, of_node_release); + kobject_put(&node->kobj); } EXPORT_SYMBOL(of_node_put); +#else +static void of_node_release(struct kobject *kobj) +{ + /* Without CONFIG_OF_DYNAMIC, no nodes gets freed */ +} #endif /* CONFIG_OF_DYNAMIC */ +struct kobj_type of_node_ktype = { + .release = of_node_release, +}; + +static ssize_t of_node_property_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t offset, size_t count) +{ + struct property *pp = container_of(bin_attr, struct property, attr); + return memory_read_from_buffer(buf, count, &offset, pp->value, pp->length); +} + +static const char *safe_name(struct kobject *kobj, const char *orig_name) +{ + const char *name = orig_name; + struct kernfs_node *kn; + int i = 0; + + /* don't be a hero. After 16 tries give up */ + while (i < 16 && (kn = sysfs_get_dirent(kobj->sd, name))) { + sysfs_put(kn); + if (name != orig_name) + kfree(name); + name = kasprintf(GFP_KERNEL, "%s#%i", orig_name, ++i); + } + + if (name != orig_name) + pr_warn("device-tree: Duplicate name in %s, renamed to \"%s\"\n", + kobject_name(kobj), name); + return name; +} + +static int __of_add_property_sysfs(struct device_node *np, struct property *pp) +{ + int rc; + + /* Important: Don't leak passwords */ + bool secure = strncmp(pp->name, "security-", 9) == 0; + + sysfs_bin_attr_init(&pp->attr); + pp->attr.attr.name = safe_name(&np->kobj, pp->name); + pp->attr.attr.mode = secure ? S_IRUSR : S_IRUGO; + pp->attr.size = secure ? 0 : pp->length; + pp->attr.read = of_node_property_read; + + rc = sysfs_create_bin_file(&np->kobj, &pp->attr); + WARN(rc, "error adding attribute %s to node %s\n", pp->name, np->full_name); + return rc; +} + +static int __of_node_add(struct device_node *np) +{ + const char *name; + struct property *pp; + int rc; + + np->kobj.kset = of_kset; + if (!np->parent) { + /* Nodes without parents are new top level trees */ + rc = kobject_add(&np->kobj, NULL, safe_name(&of_kset->kobj, "base")); + } else { + name = safe_name(&np->parent->kobj, kbasename(np->full_name)); + if (!name || !name[0]) + return -EINVAL; + + rc = kobject_add(&np->kobj, &np->parent->kobj, "%s", name); + } + if (rc) + return rc; + + for_each_property_of_node(np, pp) + __of_add_property_sysfs(np, pp); + + return 0; +} + +int of_node_add(struct device_node *np) +{ + int rc = 0; + kobject_init(&np->kobj, &of_node_ktype); + mutex_lock(&of_aliases_mutex); + if (of_kset) + rc = __of_node_add(np); + mutex_unlock(&of_aliases_mutex); + return rc; +} + +#if defined(CONFIG_OF_DYNAMIC) +static void of_node_remove(struct device_node *np) +{ + struct property *pp; + + for_each_property_of_node(np, pp) + sysfs_remove_bin_file(&np->kobj, &pp->attr); + + kobject_del(&np->kobj); +} +#endif + +static int __init of_init(void) +{ + struct device_node *np; + + /* Create the kset, and register existing nodes */ + mutex_lock(&of_aliases_mutex); + of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj); + if (!of_kset) { + mutex_unlock(&of_aliases_mutex); + return -ENOMEM; + } + for_each_of_allnodes(np) + __of_node_add(np); + mutex_unlock(&of_aliases_mutex); + +#if !defined(CONFIG_PROC_DEVICETREE) + /* Symlink to the new tree when PROC_DEVICETREE is disabled */ + if (of_allnodes) + proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base"); +#endif /* CONFIG_PROC_DEVICETREE */ + + return 0; +} +core_initcall(of_init); + static struct property *__of_find_property(const struct device_node *np, const char *name, int *lenp) { @@ -1546,6 +1681,14 @@ int of_add_property(struct device_node *np, struct property *prop) raw_spin_lock_irqsave(&devtree_lock, flags); rc = __of_add_property(np, prop); raw_spin_unlock_irqrestore(&devtree_lock, flags); + if (rc) + return rc; + + /* at early boot, bail hear and defer setup to of_init() */ + if (!of_kset) + return 0; + + __of_add_property_sysfs(np, prop); #ifdef CONFIG_PROC_DEVICETREE /* try to add to proc as well if it was initialized */ @@ -1593,6 +1736,12 @@ int of_remove_property(struct device_node *np, struct property *prop) if (!found) return -ENODEV; + /* at early boot, bail hear and defer setup to of_init() */ + if (!of_kset) + return 0; + + sysfs_remove_bin_file(&np->kobj, &prop->attr); + #ifdef CONFIG_PROC_DEVICETREE /* try to remove the proc node as well */ if (np->pde) @@ -1643,13 +1792,20 @@ int of_update_property(struct device_node *np, struct property *newprop) next = &(*next)->next; } raw_spin_unlock_irqrestore(&devtree_lock, flags); + if (rc) + return rc; + + /* Update the sysfs attribute */ + if (oldprop) + sysfs_remove_bin_file(&np->kobj, &oldprop->attr); + __of_add_property_sysfs(np, newprop); if (!found) return -ENODEV; #ifdef CONFIG_PROC_DEVICETREE /* try to add to proc as well if it was initialized */ - if (!rc && np->pde) + if (np->pde) proc_device_tree_update_prop(np->pde, newprop, oldprop); #endif /* CONFIG_PROC_DEVICETREE */ @@ -1723,6 +1879,7 @@ int of_attach_node(struct device_node *np) of_node_clear_flag(np, OF_DETACHED); raw_spin_unlock_irqrestore(&devtree_lock, flags); + of_node_add(np); of_add_proc_dt_entry(np); return 0; } @@ -1795,6 +1952,7 @@ int of_detach_node(struct device_node *np) raw_spin_unlock_irqrestore(&devtree_lock, flags); of_remove_proc_dt_entry(np); + of_node_remove(np); return rc; } #endif /* defined(CONFIG_OF_DYNAMIC) */ diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 758b4f8b30b7..96ad1ab7f9d6 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -232,7 +232,6 @@ static void * unflatten_dt_node(struct boot_param_header *blob, dad->next->sibling = np; dad->next = np; } - kref_init(&np->kref); } /* process properties */ while (1) { @@ -327,6 +326,8 @@ static void * unflatten_dt_node(struct boot_param_header *blob, np->name = ""; if (!np->type) np->type = ""; + + of_node_add(np); } while (tag == OF_DT_BEGIN_NODE || tag == OF_DT_NOP) { if (tag == OF_DT_NOP) diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c index 7b666736c168..e64fa3d3da5f 100644 --- a/drivers/of/pdt.c +++ b/drivers/of/pdt.c @@ -179,8 +179,6 @@ static struct device_node * __init of_pdt_create_node(phandle node, of_pdt_incr_unique_id(dp); dp->parent = parent; - kref_init(&dp->kref); - dp->name = of_pdt_get_one_property(node, "name"); dp->type = of_pdt_get_one_property(node, "device_type"); dp->phandle = node; @@ -215,6 +213,7 @@ static struct device_node * __init of_pdt_build_tree(struct device_node *parent, *nextp = &dp->allnext; dp->full_name = of_pdt_build_full_name(dp); + of_node_add(dp); dp->child = of_pdt_build_tree(dp, of_pdt_prom_ops->getchild(node), nextp); @@ -245,6 +244,7 @@ void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops) of_allnodes->path_component_name = ""; #endif of_allnodes->full_name = "/"; + of_node_add(of_allnodes); nextp = &of_allnodes->allnext; of_allnodes->child = of_pdt_build_tree(of_allnodes, diff --git a/drivers/of/testcase-data/tests-phandle.dtsi b/drivers/of/testcase-data/tests-phandle.dtsi index 0007d3cd7dc2..788a4c24b8f5 100644 --- a/drivers/of/testcase-data/tests-phandle.dtsi +++ b/drivers/of/testcase-data/tests-phandle.dtsi @@ -1,6 +1,9 @@ / { testcase-data { + security-password = "password"; + duplicate-name = "duplicate"; + duplicate-name { }; phandle-tests { provider0: provider0 { #phandle-cells = <0>; diff --git a/include/linux/of.h b/include/linux/of.h index b3d0f6d86e3b..bd45be5bd565 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -37,6 +37,7 @@ struct property { struct property *next; unsigned long _flags; unsigned int unique_id; + struct bin_attribute attr; }; #if defined(CONFIG_SPARC) @@ -57,7 +58,7 @@ struct device_node { struct device_node *next; /* next device of same type */ struct device_node *allnext; /* next in list of all nodes */ struct proc_dir_entry *pde; /* this node's proc directory */ - struct kref kref; + struct kobject kobj; unsigned long _flags; void *data; #if defined(CONFIG_SPARC) @@ -74,6 +75,8 @@ struct of_phandle_args { uint32_t args[MAX_PHANDLE_ARGS]; }; +extern int of_node_add(struct device_node *node); + #ifdef CONFIG_OF_DYNAMIC extern struct device_node *of_node_get(struct device_node *node); extern void of_node_put(struct device_node *node); @@ -187,6 +190,8 @@ static inline const char *of_node_full_name(const struct device_node *np) return np ? np->full_name : ""; } +#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, -- cgit v1.2.3 From 8357041a69b368991d1b04d9f1d297f8d71e1314 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Tue, 6 Nov 2012 21:03:27 +0000 Subject: of: remove /proc/device-tree The same data is now available in sysfs, so we can remove the code that exports it in /proc and replace it with a symlink to the sysfs version. Tested on versatile qemu model and mpc5200 eval board. More testing would be appreciated. v5: Fixed up conflicts with mainline changes Signed-off-by: Grant Likely Cc: Rob Herring Cc: Benjamin Herrenschmidt Cc: David S. Miller Cc: Nathan Fontenot Cc: Pantelis Antoniou --- drivers/of/Kconfig | 8 -- drivers/of/base.c | 52 +---------- fs/proc/Makefile | 1 - fs/proc/internal.h | 7 -- fs/proc/proc_devtree.c | 241 ------------------------------------------------- fs/proc/root.c | 3 - include/linux/of.h | 11 --- 7 files changed, 1 insertion(+), 322 deletions(-) delete mode 100644 fs/proc/proc_devtree.c (limited to 'drivers/of/base.c') diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index ffdcb11f75fb..a46ac1b00032 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -7,14 +7,6 @@ config OF menu "Device Tree and Open Firmware support" depends on OF -config PROC_DEVICETREE - bool "Support for device tree in /proc" - depends on PROC_FS && !SPARC - help - This option adds a device-tree directory under /proc which contains - an image of the device tree that the kernel copies from Open - Firmware or other boot firmware. If unsure, say Y here. - config OF_SELFTEST bool "Device Tree Runtime self tests" depends on OF_IRQ diff --git a/drivers/of/base.c b/drivers/of/base.c index 3b70a468c8ab..ed3e70b84957 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -281,11 +281,9 @@ static int __init of_init(void) __of_node_add(np); mutex_unlock(&of_aliases_mutex); -#if !defined(CONFIG_PROC_DEVICETREE) - /* Symlink to the new tree when PROC_DEVICETREE is disabled */ + /* Symlink in /proc as required by userspace ABI */ if (of_allnodes) proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base"); -#endif /* CONFIG_PROC_DEVICETREE */ return 0; } @@ -1690,12 +1688,6 @@ int of_add_property(struct device_node *np, struct property *prop) __of_add_property_sysfs(np, prop); -#ifdef CONFIG_PROC_DEVICETREE - /* try to add to proc as well if it was initialized */ - if (!rc && np->pde) - proc_device_tree_add_prop(np->pde, prop); -#endif /* CONFIG_PROC_DEVICETREE */ - return rc; } @@ -1742,12 +1734,6 @@ int of_remove_property(struct device_node *np, struct property *prop) sysfs_remove_bin_file(&np->kobj, &prop->attr); -#ifdef CONFIG_PROC_DEVICETREE - /* try to remove the proc node as well */ - if (np->pde) - proc_device_tree_remove_prop(np->pde, prop); -#endif /* CONFIG_PROC_DEVICETREE */ - return 0; } @@ -1803,12 +1789,6 @@ int of_update_property(struct device_node *np, struct property *newprop) if (!found) return -ENODEV; -#ifdef CONFIG_PROC_DEVICETREE - /* try to add to proc as well if it was initialized */ - if (np->pde) - proc_device_tree_update_prop(np->pde, newprop, oldprop); -#endif /* CONFIG_PROC_DEVICETREE */ - return 0; } @@ -1843,22 +1823,6 @@ int of_reconfig_notify(unsigned long action, void *p) return notifier_to_errno(rc); } -#ifdef CONFIG_PROC_DEVICETREE -static void of_add_proc_dt_entry(struct device_node *dn) -{ - struct proc_dir_entry *ent; - - ent = proc_mkdir(strrchr(dn->full_name, '/') + 1, dn->parent->pde); - if (ent) - proc_device_tree_add_node(dn, ent); -} -#else -static void of_add_proc_dt_entry(struct device_node *dn) -{ - return; -} -#endif - /** * of_attach_node - Plug a device node into the tree and global list. */ @@ -1880,22 +1844,9 @@ int of_attach_node(struct device_node *np) raw_spin_unlock_irqrestore(&devtree_lock, flags); of_node_add(np); - of_add_proc_dt_entry(np); return 0; } -#ifdef CONFIG_PROC_DEVICETREE -static void of_remove_proc_dt_entry(struct device_node *dn) -{ - proc_remove(dn->pde); -} -#else -static void of_remove_proc_dt_entry(struct device_node *dn) -{ - return; -} -#endif - /** * of_detach_node - "Unplug" a node from the device tree. * @@ -1951,7 +1902,6 @@ int of_detach_node(struct device_node *np) of_node_set_flag(np, OF_DETACHED); raw_spin_unlock_irqrestore(&devtree_lock, flags); - of_remove_proc_dt_entry(np); of_node_remove(np); return rc; } diff --git a/fs/proc/Makefile b/fs/proc/Makefile index ab30716584f5..239493ec718e 100644 --- a/fs/proc/Makefile +++ b/fs/proc/Makefile @@ -27,6 +27,5 @@ proc-$(CONFIG_PROC_SYSCTL) += proc_sysctl.o proc-$(CONFIG_NET) += proc_net.o proc-$(CONFIG_PROC_KCORE) += kcore.o proc-$(CONFIG_PROC_VMCORE) += vmcore.o -proc-$(CONFIG_PROC_DEVICETREE) += proc_devtree.o proc-$(CONFIG_PRINTK) += kmsg.o proc-$(CONFIG_PROC_PAGE_MONITOR) += page.o diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 651d09a11dde..3ab6d14e71c5 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -210,13 +210,6 @@ extern struct inode *proc_get_inode(struct super_block *, struct proc_dir_entry extern int proc_fill_super(struct super_block *); extern void proc_entry_rundown(struct proc_dir_entry *); -/* - * proc_devtree.c - */ -#ifdef CONFIG_PROC_DEVICETREE -extern void proc_device_tree_init(void); -#endif - /* * proc_namespaces.c */ diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c deleted file mode 100644 index c82dd5147845..000000000000 --- a/fs/proc/proc_devtree.c +++ /dev/null @@ -1,241 +0,0 @@ -/* - * proc_devtree.c - handles /proc/device-tree - * - * Copyright 1997 Paul Mackerras - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "internal.h" - -static inline void set_node_proc_entry(struct device_node *np, - struct proc_dir_entry *de) -{ - np->pde = de; -} - -static struct proc_dir_entry *proc_device_tree; - -/* - * Supply data on a read from /proc/device-tree/node/property. - */ -static int property_proc_show(struct seq_file *m, void *v) -{ - struct property *pp = m->private; - - seq_write(m, pp->value, pp->length); - return 0; -} - -static int property_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, property_proc_show, __PDE_DATA(inode)); -} - -static const struct file_operations property_proc_fops = { - .owner = THIS_MODULE, - .open = property_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/* - * For a node with a name like "gc@10", we make symlinks called "gc" - * and "@10" to it. - */ - -/* - * Add a property to a node - */ -static struct proc_dir_entry * -__proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp, - const char *name) -{ - struct proc_dir_entry *ent; - - /* - * Unfortunately proc_register puts each new entry - * at the beginning of the list. So we rearrange them. - */ - ent = proc_create_data(name, - strncmp(name, "security-", 9) ? S_IRUGO : S_IRUSR, - de, &property_proc_fops, pp); - if (ent == NULL) - return NULL; - - if (!strncmp(name, "security-", 9)) - proc_set_size(ent, 0); /* don't leak number of password chars */ - else - proc_set_size(ent, pp->length); - - return ent; -} - - -void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop) -{ - __proc_device_tree_add_prop(pde, prop, prop->name); -} - -void proc_device_tree_remove_prop(struct proc_dir_entry *pde, - struct property *prop) -{ - remove_proc_entry(prop->name, pde); -} - -void proc_device_tree_update_prop(struct proc_dir_entry *pde, - struct property *newprop, - struct property *oldprop) -{ - struct proc_dir_entry *ent; - - if (!oldprop) { - proc_device_tree_add_prop(pde, newprop); - return; - } - - for (ent = pde->subdir; ent != NULL; ent = ent->next) - if (ent->data == oldprop) - break; - if (ent == NULL) { - pr_warn("device-tree: property \"%s\" does not exist\n", - oldprop->name); - } else { - ent->data = newprop; - ent->size = newprop->length; - } -} - -/* - * Various dodgy firmware might give us nodes and/or properties with - * conflicting names. That's generally ok, except for exporting via /proc, - * so munge names here to ensure they're unique. - */ - -static int duplicate_name(struct proc_dir_entry *de, const char *name) -{ - struct proc_dir_entry *ent; - int found = 0; - - spin_lock(&proc_subdir_lock); - - for (ent = de->subdir; ent != NULL; ent = ent->next) { - if (strcmp(ent->name, name) == 0) { - found = 1; - break; - } - } - - spin_unlock(&proc_subdir_lock); - - return found; -} - -static const char *fixup_name(struct device_node *np, struct proc_dir_entry *de, - const char *name) -{ - char *fixed_name; - int fixup_len = strlen(name) + 2 + 1; /* name + #x + \0 */ - int i = 1, size; - -realloc: - fixed_name = kmalloc(fixup_len, GFP_KERNEL); - if (fixed_name == NULL) { - pr_err("device-tree: Out of memory trying to fixup " - "name \"%s\"\n", name); - return name; - } - -retry: - size = snprintf(fixed_name, fixup_len, "%s#%d", name, i); - size++; /* account for NULL */ - - if (size > fixup_len) { - /* We ran out of space, free and reallocate. */ - kfree(fixed_name); - fixup_len = size; - goto realloc; - } - - if (duplicate_name(de, fixed_name)) { - /* Multiple duplicates. Retry with a different offset. */ - i++; - goto retry; - } - - pr_warn("device-tree: Duplicate name in %s, renamed to \"%s\"\n", - np->full_name, fixed_name); - - return fixed_name; -} - -/* - * Process a node, adding entries for its children and its properties. - */ -void proc_device_tree_add_node(struct device_node *np, - struct proc_dir_entry *de) -{ - struct property *pp; - struct proc_dir_entry *ent; - struct device_node *child; - const char *p; - - set_node_proc_entry(np, de); - for (child = NULL; (child = of_get_next_child(np, child));) { - /* Use everything after the last slash, or the full name */ - p = kbasename(child->full_name); - - if (duplicate_name(de, p)) - p = fixup_name(np, de, p); - - ent = proc_mkdir(p, de); - if (ent == NULL) - break; - proc_device_tree_add_node(child, ent); - } - of_node_put(child); - - for (pp = np->properties; pp != NULL; pp = pp->next) { - p = pp->name; - - if (strchr(p, '/')) - continue; - - if (duplicate_name(de, p)) - p = fixup_name(np, de, p); - - ent = __proc_device_tree_add_prop(de, pp, p); - if (ent == NULL) - break; - } -} - -/* - * Called on initialization to set up the /proc/device-tree subtree - */ -void __init proc_device_tree_init(void) -{ - struct device_node *root; - - proc_device_tree = proc_mkdir("device-tree", NULL); - if (proc_device_tree == NULL) - return; - root = of_find_node_by_path("/"); - if (root == NULL) { - remove_proc_entry("device-tree", NULL); - pr_debug("/proc/device-tree: can't find root\n"); - return; - } - proc_device_tree_add_node(root, proc_device_tree); - of_node_put(root); -} diff --git a/fs/proc/root.c b/fs/proc/root.c index 87dbcbef7fe4..7bbeb5257af1 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -183,9 +183,6 @@ void __init proc_root_init(void) proc_mkdir("openprom", NULL); #endif proc_tty_init(); -#ifdef CONFIG_PROC_DEVICETREE - proc_device_tree_init(); -#endif proc_mkdir("bus", NULL); proc_sys_init(); } diff --git a/include/linux/of.h b/include/linux/of.h index bd45be5bd565..257994a420f3 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -57,7 +57,6 @@ struct device_node { struct device_node *sibling; struct device_node *next; /* next device of same type */ struct device_node *allnext; /* next in list of all nodes */ - struct proc_dir_entry *pde; /* this node's proc directory */ struct kobject kobj; unsigned long _flags; void *data; @@ -658,14 +657,4 @@ static inline int of_get_available_child_count(const struct device_node *np) return num; } -#if defined(CONFIG_PROC_FS) && defined(CONFIG_PROC_DEVICETREE) -extern void proc_device_tree_add_node(struct device_node *, struct proc_dir_entry *); -extern void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop); -extern void proc_device_tree_remove_prop(struct proc_dir_entry *pde, - struct property *prop); -extern void proc_device_tree_update_prop(struct proc_dir_entry *pde, - struct property *newprop, - struct property *oldprop); -#endif - #endif /* _LINUX_OF_H */ -- cgit v1.2.3 From 0829f6d1f69e4f2fae4062987ae6531a9af1a2e3 Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Fri, 13 Dec 2013 20:08:59 +0200 Subject: of: device_node kobject lifecycle fixes After the move to having device nodes be proper kobjects the lifecycle of the node needs to be controlled better. At first convert of_add_node() in the unflattened functions to of_init_node() which initializes the kobject so that of_node_get/put work correctly even before of_init is called. Afterwards introduce of_node_is_initialized & of_node_is_attached that query the underlying kobject about the state (attached means kobj is visible in sysfs) Using that make sure the lifecycle of the tree is correct at all times. Signed-off-by: Pantelis Antoniou [grant.likely: moved of_node_init() calls, fixed up locking, and dropped __of_populate() hunks] Signed-off-by: Grant Likely --- drivers/of/base.c | 35 ++++++++++++++++++++++++++--------- drivers/of/fdt.c | 3 +-- drivers/of/pdt.c | 3 +-- include/linux/of.h | 19 +++++++++++++++++++ 4 files changed, 47 insertions(+), 13 deletions(-) (limited to 'drivers/of/base.c') diff --git a/drivers/of/base.c b/drivers/of/base.c index ed3e70b84957..418a4ff9d97c 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -246,10 +246,19 @@ static int __of_node_add(struct device_node *np) int of_node_add(struct device_node *np) { int rc = 0; - kobject_init(&np->kobj, &of_node_ktype); + + BUG_ON(!of_node_is_initialized(np)); + + /* + * Grab the mutex here so that in a race condition between of_init() and + * of_node_add(), node addition will still be consistent. + */ mutex_lock(&of_aliases_mutex); if (of_kset) rc = __of_node_add(np); + else + /* This scenario may be perfectly valid, but report it anyway */ + pr_info("of_node_add(%s) before of_init()\n", np->full_name); mutex_unlock(&of_aliases_mutex); return rc; } @@ -259,10 +268,17 @@ static void of_node_remove(struct device_node *np) { struct property *pp; - for_each_property_of_node(np, pp) - sysfs_remove_bin_file(&np->kobj, &pp->attr); + BUG_ON(!of_node_is_initialized(np)); + + /* only remove properties if on sysfs */ + if (of_node_is_attached(np)) { + for_each_property_of_node(np, pp) + sysfs_remove_bin_file(&np->kobj, &pp->attr); + kobject_del(&np->kobj); + } - kobject_del(&np->kobj); + /* finally remove the kobj_init ref */ + of_node_put(np); } #endif @@ -1631,6 +1647,10 @@ static int of_property_notify(int action, struct device_node *np, { struct of_prop_reconfig pr; + /* only call notifiers if the node is attached */ + if (!of_node_is_attached(np)) + return 0; + pr.dn = np; pr.prop = prop; return of_reconfig_notify(action, &pr); @@ -1682,11 +1702,8 @@ int of_add_property(struct device_node *np, struct property *prop) if (rc) return rc; - /* at early boot, bail hear and defer setup to of_init() */ - if (!of_kset) - return 0; - - __of_add_property_sysfs(np, prop); + if (of_node_is_attached(np)) + __of_add_property_sysfs(np, prop); return rc; } diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 96ad1ab7f9d6..70ccc36513e7 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -202,6 +202,7 @@ static void * unflatten_dt_node(struct boot_param_header *blob, __alignof__(struct device_node)); if (allnextpp) { char *fn; + of_node_init(np); np->full_name = fn = ((char *)np) + sizeof(*np); if (new_format) { /* rebuild full path for new format */ @@ -326,8 +327,6 @@ static void * unflatten_dt_node(struct boot_param_header *blob, np->name = ""; if (!np->type) np->type = ""; - - of_node_add(np); } while (tag == OF_DT_BEGIN_NODE || tag == OF_DT_NOP) { if (tag == OF_DT_NOP) diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c index e64fa3d3da5f..36b4035881b0 100644 --- a/drivers/of/pdt.c +++ b/drivers/of/pdt.c @@ -176,6 +176,7 @@ static struct device_node * __init of_pdt_create_node(phandle node, return NULL; dp = prom_early_alloc(sizeof(*dp)); + of_node_init(dp); of_pdt_incr_unique_id(dp); dp->parent = parent; @@ -213,7 +214,6 @@ static struct device_node * __init of_pdt_build_tree(struct device_node *parent, *nextp = &dp->allnext; dp->full_name = of_pdt_build_full_name(dp); - of_node_add(dp); dp->child = of_pdt_build_tree(dp, of_pdt_prom_ops->getchild(node), nextp); @@ -244,7 +244,6 @@ void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops) of_allnodes->path_component_name = ""; #endif of_allnodes->full_name = "/"; - of_node_add(of_allnodes); nextp = &of_allnodes->allnext; of_allnodes->child = of_pdt_build_tree(of_allnodes, diff --git a/include/linux/of.h b/include/linux/of.h index 257994a420f3..a8b9dad90c64 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -76,6 +76,25 @@ struct of_phandle_args { extern int of_node_add(struct device_node *node); +/* initialize a node */ +extern struct kobj_type of_node_ktype; +static inline void of_node_init(struct device_node *node) +{ + kobject_init(&node->kobj, &of_node_ktype); +} + +/* true when node is initialized */ +static inline int of_node_is_initialized(struct device_node *node) +{ + return node && node->kobj.state_initialized; +} + +/* true when node is attached (i.e. present on sysfs) */ +static inline int of_node_is_attached(struct device_node *node) +{ + return node && node->kobj.state_in_sysfs; +} + #ifdef CONFIG_OF_DYNAMIC extern struct device_node *of_node_get(struct device_node *node); extern void of_node_put(struct device_node *node); -- cgit v1.2.3 From 676e1b2fcd9dbb47a59baac13d089621d22c68b8 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 27 Mar 2014 17:11:23 -0700 Subject: of: Add support for ePAPR "stdout-path" property ePAPR 1.1 defines the "stdout-path" property for specifying the console device, but Linux currently only handles the older "linux,stdout-path" property. This patch adds parsing for the new property name. Signed-off-by: Grant Likely --- drivers/of/base.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/of/base.c') diff --git a/drivers/of/base.c b/drivers/of/base.c index 418a4ff9d97c..be2861d69b02 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1955,9 +1955,9 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) of_chosen = of_find_node_by_path("/chosen@0"); if (of_chosen) { - const char *name; - - name = of_get_property(of_chosen, "linux,stdout-path", NULL); + const char *name = of_get_property(of_chosen, "stdout-path", NULL); + if (!name) + name = of_get_property(of_chosen, "linux,stdout-path", NULL); if (name) of_stdout = of_find_node_by_path(name); } -- cgit v1.2.3