Commit 5c8bfbc4 authored by Paul Mackerras's avatar Paul Mackerras Committed by Linus Torvalds

[PATCH] ppc64: use kref for device_node refcounting

This patch is from Nathan Lynch <nathanl@austin.ibm.com>.

This changes struct device_node and associated code to use the kref api for
object refcounting and freeing.  I've given it some testing on pSeries with
cpu add/remove and verified that the release function works.  The change is
somewhat cosmetic but it does make the code easier to understand...  at least
I think so =)

The only real change is that the refcount on all device_nodes is initialized
at 1, and the device node is freed when the refcount reaches 0 (of_remove_node
has the extra "put" to ensure that this happens).  This lets us get rid of the
OF_STALE flag and macros in prom.h.
Signed-off-by: default avatarNathan Lynch <nathanl@austin.ibm.com>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 61f34d57
......@@ -717,6 +717,7 @@ static unsigned long __init unflatten_dt_node(unsigned long mem,
dad->next->sibling = np;
dad->next = np;
}
kref_init(&np->kref);
}
while(1) {
u32 sz, noff;
......@@ -1475,24 +1476,31 @@ EXPORT_SYMBOL(of_get_next_child);
* @node: Node to inc refcount, NULL is supported to
* simplify writing of callers
*
* Returns the node itself or NULL if gone.
* Returns node.
*/
struct device_node *of_node_get(struct device_node *node)
{
if (node && !OF_IS_STALE(node)) {
atomic_inc(&node->_users);
return node;
}
return NULL;
if (node)
kref_get(&node->kref);
return node;
}
EXPORT_SYMBOL(of_node_get);
static inline struct device_node * kref_to_device_node(struct kref *kref)
{
return container_of(kref, struct device_node, kref);
}
/**
* of_node_cleanup - release a dynamically allocated node
* @arg: Node to be released
* of_node_release - release a dynamically allocated node
* @kref: kref element of the node to be released
*
* In of_node_put() this function is passed to kref_put()
* as the destructor.
*/
static void of_node_cleanup(struct device_node *node)
static void of_node_release(struct kref *kref)
{
struct device_node *node = kref_to_device_node(kref);
struct property *prop = node->properties;
if (!OF_IS_DYNAMIC(node))
......@@ -1518,19 +1526,8 @@ static void of_node_cleanup(struct device_node *node)
*/
void of_node_put(struct device_node *node)
{
if (!node)
return;
WARN_ON(0 == atomic_read(&node->_users));
if (OF_IS_STALE(node)) {
if (atomic_dec_and_test(&node->_users)) {
of_node_cleanup(node);
return;
}
}
else
atomic_dec(&node->_users);
if (node)
kref_put(&node->kref, of_node_release);
}
EXPORT_SYMBOL(of_node_put);
......@@ -1773,7 +1770,7 @@ int of_add_node(const char *path, struct property *proplist)
np->properties = proplist;
OF_MARK_DYNAMIC(np);
of_node_get(np);
kref_init(&np->kref);
np->parent = derive_parent(path);
if (!np->parent) {
kfree(np);
......@@ -1809,8 +1806,9 @@ static void of_cleanup_node(struct device_node *np)
}
/*
* Remove an OF device node from the system.
* Caller should have already "gotten" np.
* "Unplug" a node from the device tree. The caller must hold
* a reference to the node. The memory associated with the node
* is not freed until its refcount goes to zero.
*/
int of_remove_node(struct device_node *np)
{
......@@ -1828,7 +1826,6 @@ int of_remove_node(struct device_node *np)
of_cleanup_node(np);
write_lock(&devtree_lock);
OF_MARK_STALE(np);
remove_node_proc_entries(np);
if (allnodes == np)
allnodes = np->allnext;
......@@ -1853,6 +1850,7 @@ int of_remove_node(struct device_node *np)
}
write_unlock(&devtree_lock);
of_node_put(parent);
of_node_put(np); /* Must decrement the refcount */
return 0;
}
......
......@@ -149,18 +149,15 @@ struct device_node {
struct proc_dir_entry *pde; /* this node's proc directory */
struct proc_dir_entry *name_link; /* name symlink */
struct proc_dir_entry *addr_link; /* addr symlink */
atomic_t _users; /* reference count */
struct kref kref;
unsigned long _flags;
};
extern struct device_node *of_chosen;
/* flag descriptions */
#define OF_STALE 0 /* node is slated for deletion */
#define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */
#define OF_IS_STALE(x) test_bit(OF_STALE, &x->_flags)
#define OF_MARK_STALE(x) set_bit(OF_STALE, &x->_flags)
#define OF_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags)
#define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment