Skip to content

Commit 75b57ec

Browse files
committed
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 <[email protected]> Tested-by: Sascha Hauer <[email protected]> Cc: Rob Herring <[email protected]> Cc: Benjamin Herrenschmidt <[email protected]> Cc: David S. Miller <[email protected]> Cc: Nathan Fontenot <[email protected]> Cc: Pantelis Antoniou <[email protected]>
1 parent dab2310 commit 75b57ec

File tree

9 files changed

+209
-18
lines changed

9 files changed

+209
-18
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
What: /sys/firmware/devicetree/*
2+
Date: November 2013
3+
Contact: Grant Likely <[email protected]>
4+
Description:
5+
When using OpenFirmware or a Flattened Device Tree to enumerate
6+
hardware, the device tree structure will be exposed in this
7+
directory.
8+
9+
It is possible for multiple device-tree directories to exist.
10+
Some device drivers use a separate detached device tree which
11+
have no attachment to the system tree and will appear in a
12+
different subdirectory under /sys/firmware/devicetree.
13+
14+
Userspace must not use the /sys/firmware/devicetree/base
15+
path directly, but instead should follow /proc/device-tree
16+
symlink. It is possible that the absolute path will change
17+
in the future, but the symlink is the stable ABI.
18+
19+
The /proc/device-tree symlink replaces the devicetree /proc
20+
filesystem support, and has largely the same semantics and
21+
should be compatible with existing userspace.
22+
23+
The contents of /sys/firmware/devicetree/ is a
24+
hierarchy of directories, one per device tree node. The
25+
directory name is the resolved path component name (node
26+
name plus address). Properties are represented as files
27+
in the directory. The contents of each file is the exact
28+
binary data from the device tree.

arch/powerpc/platforms/pseries/dlpar.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
*/
1212

1313
#include <linux/kernel.h>
14-
#include <linux/kref.h>
1514
#include <linux/notifier.h>
1615
#include <linux/spinlock.h>
1716
#include <linux/cpu.h>
@@ -87,7 +86,6 @@ static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa,
8786
}
8887

8988
of_node_set_flag(dn, OF_DYNAMIC);
90-
kref_init(&dn->kref);
9189

9290
return dn;
9391
}

arch/powerpc/platforms/pseries/reconfig.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
*/
1313

1414
#include <linux/kernel.h>
15-
#include <linux/kref.h>
1615
#include <linux/notifier.h>
1716
#include <linux/proc_fs.h>
1817
#include <linux/slab.h>
@@ -70,7 +69,6 @@ static int pSeries_reconfig_add_node(const char *path, struct property *proplist
7069

7170
np->properties = proplist;
7271
of_node_set_flag(np, OF_DYNAMIC);
73-
kref_init(&np->kref);
7472

7573
np->parent = derive_parent(path);
7674
if (IS_ERR(np->parent)) {

arch/powerpc/sysdev/msi_bitmap.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ void __init test_of_node(void)
202202

203203
/* There should really be a struct device_node allocator */
204204
memset(&of_node, 0, sizeof(of_node));
205-
kref_init(&of_node.kref);
205+
kref_init(&of_node.kobj.kref);
206206
of_node.full_name = node_name;
207207

208208
check(0 == msi_bitmap_alloc(&bmp, size, &of_node));

drivers/of/base.c

Lines changed: 166 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <linux/of.h>
2424
#include <linux/spinlock.h>
2525
#include <linux/slab.h>
26+
#include <linux/string.h>
2627
#include <linux/proc_fs.h>
2728

2829
#include "of_private.h"
@@ -35,6 +36,12 @@ struct device_node *of_chosen;
3536
struct device_node *of_aliases;
3637
static struct device_node *of_stdout;
3738

39+
static struct kset *of_kset;
40+
41+
/*
42+
* Used to protect the of_aliases; but also overloaded to hold off addition of
43+
* nodes to sysfs
44+
*/
3845
DEFINE_MUTEX(of_aliases_mutex);
3946

4047
/* use when traversing tree through the allnext, child, sibling,
@@ -92,14 +99,14 @@ int __weak of_node_to_nid(struct device_node *np)
9299
struct device_node *of_node_get(struct device_node *node)
93100
{
94101
if (node)
95-
kref_get(&node->kref);
102+
kobject_get(&node->kobj);
96103
return node;
97104
}
98105
EXPORT_SYMBOL(of_node_get);
99106

100-
static inline struct device_node *kref_to_device_node(struct kref *kref)
107+
static inline struct device_node *kobj_to_device_node(struct kobject *kobj)
101108
{
102-
return container_of(kref, struct device_node, kref);
109+
return container_of(kobj, struct device_node, kobj);
103110
}
104111

105112
/**
@@ -109,16 +116,15 @@ static inline struct device_node *kref_to_device_node(struct kref *kref)
109116
* In of_node_put() this function is passed to kref_put()
110117
* as the destructor.
111118
*/
112-
static void of_node_release(struct kref *kref)
119+
static void of_node_release(struct kobject *kobj)
113120
{
114-
struct device_node *node = kref_to_device_node(kref);
121+
struct device_node *node = kobj_to_device_node(kobj);
115122
struct property *prop = node->properties;
116123

117124
/* We should never be releasing nodes that haven't been detached. */
118125
if (!of_node_check_flag(node, OF_DETACHED)) {
119126
pr_err("ERROR: Bad of_node_put() on %s\n", node->full_name);
120127
dump_stack();
121-
kref_init(&node->kref);
122128
return;
123129
}
124130

@@ -151,11 +157,140 @@ static void of_node_release(struct kref *kref)
151157
void of_node_put(struct device_node *node)
152158
{
153159
if (node)
154-
kref_put(&node->kref, of_node_release);
160+
kobject_put(&node->kobj);
155161
}
156162
EXPORT_SYMBOL(of_node_put);
163+
#else
164+
static void of_node_release(struct kobject *kobj)
165+
{
166+
/* Without CONFIG_OF_DYNAMIC, no nodes gets freed */
167+
}
157168
#endif /* CONFIG_OF_DYNAMIC */
158169

170+
struct kobj_type of_node_ktype = {
171+
.release = of_node_release,
172+
};
173+
174+
static ssize_t of_node_property_read(struct file *filp, struct kobject *kobj,
175+
struct bin_attribute *bin_attr, char *buf,
176+
loff_t offset, size_t count)
177+
{
178+
struct property *pp = container_of(bin_attr, struct property, attr);
179+
return memory_read_from_buffer(buf, count, &offset, pp->value, pp->length);
180+
}
181+
182+
static const char *safe_name(struct kobject *kobj, const char *orig_name)
183+
{
184+
const char *name = orig_name;
185+
struct kernfs_node *kn;
186+
int i = 0;
187+
188+
/* don't be a hero. After 16 tries give up */
189+
while (i < 16 && (kn = sysfs_get_dirent(kobj->sd, name))) {
190+
sysfs_put(kn);
191+
if (name != orig_name)
192+
kfree(name);
193+
name = kasprintf(GFP_KERNEL, "%s#%i", orig_name, ++i);
194+
}
195+
196+
if (name != orig_name)
197+
pr_warn("device-tree: Duplicate name in %s, renamed to \"%s\"\n",
198+
kobject_name(kobj), name);
199+
return name;
200+
}
201+
202+
static int __of_add_property_sysfs(struct device_node *np, struct property *pp)
203+
{
204+
int rc;
205+
206+
/* Important: Don't leak passwords */
207+
bool secure = strncmp(pp->name, "security-", 9) == 0;
208+
209+
sysfs_bin_attr_init(&pp->attr);
210+
pp->attr.attr.name = safe_name(&np->kobj, pp->name);
211+
pp->attr.attr.mode = secure ? S_IRUSR : S_IRUGO;
212+
pp->attr.size = secure ? 0 : pp->length;
213+
pp->attr.read = of_node_property_read;
214+
215+
rc = sysfs_create_bin_file(&np->kobj, &pp->attr);
216+
WARN(rc, "error adding attribute %s to node %s\n", pp->name, np->full_name);
217+
return rc;
218+
}
219+
220+
static int __of_node_add(struct device_node *np)
221+
{
222+
const char *name;
223+
struct property *pp;
224+
int rc;
225+
226+
np->kobj.kset = of_kset;
227+
if (!np->parent) {
228+
/* Nodes without parents are new top level trees */
229+
rc = kobject_add(&np->kobj, NULL, safe_name(&of_kset->kobj, "base"));
230+
} else {
231+
name = safe_name(&np->parent->kobj, kbasename(np->full_name));
232+
if (!name || !name[0])
233+
return -EINVAL;
234+
235+
rc = kobject_add(&np->kobj, &np->parent->kobj, "%s", name);
236+
}
237+
if (rc)
238+
return rc;
239+
240+
for_each_property_of_node(np, pp)
241+
__of_add_property_sysfs(np, pp);
242+
243+
return 0;
244+
}
245+
246+
int of_node_add(struct device_node *np)
247+
{
248+
int rc = 0;
249+
kobject_init(&np->kobj, &of_node_ktype);
250+
mutex_lock(&of_aliases_mutex);
251+
if (of_kset)
252+
rc = __of_node_add(np);
253+
mutex_unlock(&of_aliases_mutex);
254+
return rc;
255+
}
256+
257+
#if defined(CONFIG_OF_DYNAMIC)
258+
static void of_node_remove(struct device_node *np)
259+
{
260+
struct property *pp;
261+
262+
for_each_property_of_node(np, pp)
263+
sysfs_remove_bin_file(&np->kobj, &pp->attr);
264+
265+
kobject_del(&np->kobj);
266+
}
267+
#endif
268+
269+
static int __init of_init(void)
270+
{
271+
struct device_node *np;
272+
273+
/* Create the kset, and register existing nodes */
274+
mutex_lock(&of_aliases_mutex);
275+
of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj);
276+
if (!of_kset) {
277+
mutex_unlock(&of_aliases_mutex);
278+
return -ENOMEM;
279+
}
280+
for_each_of_allnodes(np)
281+
__of_node_add(np);
282+
mutex_unlock(&of_aliases_mutex);
283+
284+
#if !defined(CONFIG_PROC_DEVICETREE)
285+
/* Symlink to the new tree when PROC_DEVICETREE is disabled */
286+
if (of_allnodes)
287+
proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base");
288+
#endif /* CONFIG_PROC_DEVICETREE */
289+
290+
return 0;
291+
}
292+
core_initcall(of_init);
293+
159294
static struct property *__of_find_property(const struct device_node *np,
160295
const char *name, int *lenp)
161296
{
@@ -1546,6 +1681,14 @@ int of_add_property(struct device_node *np, struct property *prop)
15461681
raw_spin_lock_irqsave(&devtree_lock, flags);
15471682
rc = __of_add_property(np, prop);
15481683
raw_spin_unlock_irqrestore(&devtree_lock, flags);
1684+
if (rc)
1685+
return rc;
1686+
1687+
/* at early boot, bail hear and defer setup to of_init() */
1688+
if (!of_kset)
1689+
return 0;
1690+
1691+
__of_add_property_sysfs(np, prop);
15491692

15501693
#ifdef CONFIG_PROC_DEVICETREE
15511694
/* 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)
15931736
if (!found)
15941737
return -ENODEV;
15951738

1739+
/* at early boot, bail hear and defer setup to of_init() */
1740+
if (!of_kset)
1741+
return 0;
1742+
1743+
sysfs_remove_bin_file(&np->kobj, &prop->attr);
1744+
15961745
#ifdef CONFIG_PROC_DEVICETREE
15971746
/* try to remove the proc node as well */
15981747
if (np->pde)
@@ -1643,13 +1792,20 @@ int of_update_property(struct device_node *np, struct property *newprop)
16431792
next = &(*next)->next;
16441793
}
16451794
raw_spin_unlock_irqrestore(&devtree_lock, flags);
1795+
if (rc)
1796+
return rc;
1797+
1798+
/* Update the sysfs attribute */
1799+
if (oldprop)
1800+
sysfs_remove_bin_file(&np->kobj, &oldprop->attr);
1801+
__of_add_property_sysfs(np, newprop);
16461802

16471803
if (!found)
16481804
return -ENODEV;
16491805

16501806
#ifdef CONFIG_PROC_DEVICETREE
16511807
/* try to add to proc as well if it was initialized */
1652-
if (!rc && np->pde)
1808+
if (np->pde)
16531809
proc_device_tree_update_prop(np->pde, newprop, oldprop);
16541810
#endif /* CONFIG_PROC_DEVICETREE */
16551811

@@ -1723,6 +1879,7 @@ int of_attach_node(struct device_node *np)
17231879
of_node_clear_flag(np, OF_DETACHED);
17241880
raw_spin_unlock_irqrestore(&devtree_lock, flags);
17251881

1882+
of_node_add(np);
17261883
of_add_proc_dt_entry(np);
17271884
return 0;
17281885
}
@@ -1795,6 +1952,7 @@ int of_detach_node(struct device_node *np)
17951952
raw_spin_unlock_irqrestore(&devtree_lock, flags);
17961953

17971954
of_remove_proc_dt_entry(np);
1955+
of_node_remove(np);
17981956
return rc;
17991957
}
18001958
#endif /* defined(CONFIG_OF_DYNAMIC) */

drivers/of/fdt.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,6 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
232232
dad->next->sibling = np;
233233
dad->next = np;
234234
}
235-
kref_init(&np->kref);
236235
}
237236
/* process properties */
238237
while (1) {
@@ -327,6 +326,8 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
327326
np->name = "<NULL>";
328327
if (!np->type)
329328
np->type = "<NULL>";
329+
330+
of_node_add(np);
330331
}
331332
while (tag == OF_DT_BEGIN_NODE || tag == OF_DT_NOP) {
332333
if (tag == OF_DT_NOP)

drivers/of/pdt.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,6 @@ static struct device_node * __init of_pdt_create_node(phandle node,
179179
of_pdt_incr_unique_id(dp);
180180
dp->parent = parent;
181181

182-
kref_init(&dp->kref);
183-
184182
dp->name = of_pdt_get_one_property(node, "name");
185183
dp->type = of_pdt_get_one_property(node, "device_type");
186184
dp->phandle = node;
@@ -215,6 +213,7 @@ static struct device_node * __init of_pdt_build_tree(struct device_node *parent,
215213
*nextp = &dp->allnext;
216214

217215
dp->full_name = of_pdt_build_full_name(dp);
216+
of_node_add(dp);
218217

219218
dp->child = of_pdt_build_tree(dp,
220219
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)
245244
of_allnodes->path_component_name = "";
246245
#endif
247246
of_allnodes->full_name = "/";
247+
of_node_add(of_allnodes);
248248

249249
nextp = &of_allnodes->allnext;
250250
of_allnodes->child = of_pdt_build_tree(of_allnodes,

drivers/of/testcase-data/tests-phandle.dtsi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11

22
/ {
33
testcase-data {
4+
security-password = "password";
5+
duplicate-name = "duplicate";
6+
duplicate-name { };
47
phandle-tests {
58
provider0: provider0 {
69
#phandle-cells = <0>;

0 commit comments

Comments
 (0)