Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 75b57ecf authored by Grant Likely's avatar Grant Likely
Browse files

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: default avatarGrant Likely <grant.likely@secretlab.ca>
Tested-by: default avatarSascha Hauer <s.hauer@pengutronix.de>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: David S. Miller <davem@davemloft.net>
Cc: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Cc: Pantelis Antoniou <panto@antoniou-consulting.com>
parent dab2310d
Loading
Loading
Loading
Loading
+28 −0
Original line number Diff line number Diff line
What:		/sys/firmware/devicetree/*
Date:		November 2013
Contact:	Grant Likely <grant.likely@linaro.org>
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.
+0 −2
Original line number Diff line number Diff line
@@ -11,7 +11,6 @@
 */

#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/notifier.h>
#include <linux/spinlock.h>
#include <linux/cpu.h>
@@ -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;
}
+0 −2
Original line number Diff line number Diff line
@@ -12,7 +12,6 @@
 */

#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/notifier.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
@@ -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)) {
+1 −1
Original line number Diff line number Diff line
@@ -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));
+166 −8
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include <linux/of.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/proc_fs.h>

#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) */
Loading