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, ...@@ -717,6 +717,7 @@ static unsigned long __init unflatten_dt_node(unsigned long mem,
dad->next->sibling = np; dad->next->sibling = np;
dad->next = np; dad->next = np;
} }
kref_init(&np->kref);
} }
while(1) { while(1) {
u32 sz, noff; u32 sz, noff;
...@@ -1475,24 +1476,31 @@ EXPORT_SYMBOL(of_get_next_child); ...@@ -1475,24 +1476,31 @@ EXPORT_SYMBOL(of_get_next_child);
* @node: Node to inc refcount, NULL is supported to * @node: Node to inc refcount, NULL is supported to
* simplify writing of callers * simplify writing of callers
* *
* Returns the node itself or NULL if gone. * Returns node.
*/ */
struct device_node *of_node_get(struct device_node *node) struct device_node *of_node_get(struct device_node *node)
{ {
if (node && !OF_IS_STALE(node)) { if (node)
atomic_inc(&node->_users); kref_get(&node->kref);
return node; return node;
}
return NULL;
} }
EXPORT_SYMBOL(of_node_get); 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 * of_node_release - release a dynamically allocated node
* @arg: Node to be released * @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; struct property *prop = node->properties;
if (!OF_IS_DYNAMIC(node)) if (!OF_IS_DYNAMIC(node))
...@@ -1518,19 +1526,8 @@ static void of_node_cleanup(struct device_node *node) ...@@ -1518,19 +1526,8 @@ static void of_node_cleanup(struct device_node *node)
*/ */
void of_node_put(struct device_node *node) void of_node_put(struct device_node *node)
{ {
if (!node) if (node)
return; kref_put(&node->kref, of_node_release);
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);
} }
EXPORT_SYMBOL(of_node_put); EXPORT_SYMBOL(of_node_put);
...@@ -1773,7 +1770,7 @@ int of_add_node(const char *path, struct property *proplist) ...@@ -1773,7 +1770,7 @@ int of_add_node(const char *path, struct property *proplist)
np->properties = proplist; np->properties = proplist;
OF_MARK_DYNAMIC(np); OF_MARK_DYNAMIC(np);
of_node_get(np); kref_init(&np->kref);
np->parent = derive_parent(path); np->parent = derive_parent(path);
if (!np->parent) { if (!np->parent) {
kfree(np); kfree(np);
...@@ -1809,8 +1806,9 @@ static void of_cleanup_node(struct device_node *np) ...@@ -1809,8 +1806,9 @@ static void of_cleanup_node(struct device_node *np)
} }
/* /*
* Remove an OF device node from the system. * "Unplug" a node from the device tree. The caller must hold
* Caller should have already "gotten" np. * 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) int of_remove_node(struct device_node *np)
{ {
...@@ -1828,7 +1826,6 @@ int of_remove_node(struct device_node *np) ...@@ -1828,7 +1826,6 @@ int of_remove_node(struct device_node *np)
of_cleanup_node(np); of_cleanup_node(np);
write_lock(&devtree_lock); write_lock(&devtree_lock);
OF_MARK_STALE(np);
remove_node_proc_entries(np); remove_node_proc_entries(np);
if (allnodes == np) if (allnodes == np)
allnodes = np->allnext; allnodes = np->allnext;
...@@ -1853,6 +1850,7 @@ int of_remove_node(struct device_node *np) ...@@ -1853,6 +1850,7 @@ int of_remove_node(struct device_node *np)
} }
write_unlock(&devtree_lock); write_unlock(&devtree_lock);
of_node_put(parent); of_node_put(parent);
of_node_put(np); /* Must decrement the refcount */
return 0; return 0;
} }
......
...@@ -149,18 +149,15 @@ struct device_node { ...@@ -149,18 +149,15 @@ struct device_node {
struct proc_dir_entry *pde; /* this node's proc directory */ struct proc_dir_entry *pde; /* this node's proc directory */
struct proc_dir_entry *name_link; /* name symlink */ struct proc_dir_entry *name_link; /* name symlink */
struct proc_dir_entry *addr_link; /* addr symlink */ struct proc_dir_entry *addr_link; /* addr symlink */
atomic_t _users; /* reference count */ struct kref kref;
unsigned long _flags; unsigned long _flags;
}; };
extern struct device_node *of_chosen; extern struct device_node *of_chosen;
/* flag descriptions */ /* flag descriptions */
#define OF_STALE 0 /* node is slated for deletion */
#define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */ #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_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags)
#define OF_MARK_DYNAMIC(x) set_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