Browse Source

[POWERPC] Add a warning to help trackdown device_node refcounting bugs

When the refcount for a device node goes to 0, we call the
destructor - of_node_release(). This should only happen if we've
already detached the node from the device tree.

So add a flag OF_DETACHED which tracks detached-ness, and if we
find ourselves in of_node_release() without it set, issue a
warning and don't free the device_node. To avoid warning
continuously reinitialise the kref to a sane value.

Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Michael Ellerman 18 years ago
parent
commit
6a281856c0
2 changed files with 12 additions and 0 deletions
  1. 11 0
      arch/powerpc/kernel/prom.c
  2. 1 0
      include/asm-powerpc/prom.h

+ 11 - 0
arch/powerpc/kernel/prom.c

@@ -1375,8 +1375,17 @@ static void of_node_release(struct kref *kref)
 	struct device_node *node = kref_to_device_node(kref);
 	struct device_node *node = kref_to_device_node(kref);
 	struct property *prop = node->properties;
 	struct property *prop = node->properties;
 
 
+	/* We should never be releasing nodes that haven't been detached. */
+	if (!of_node_check_flag(node, OF_DETACHED)) {
+		printk("WARNING: Bad of_node_put() on %s\n", node->full_name);
+		dump_stack();
+		kref_init(&node->kref);
+		return;
+	}
+
 	if (!of_node_check_flag(node, OF_DYNAMIC))
 	if (!of_node_check_flag(node, OF_DYNAMIC))
 		return;
 		return;
+
 	while (prop) {
 	while (prop) {
 		struct property *next = prop->next;
 		struct property *next = prop->next;
 		kfree(prop->name);
 		kfree(prop->name);
@@ -1457,6 +1466,8 @@ void of_detach_node(const struct device_node *np)
 		prevsib->sibling = np->sibling;
 		prevsib->sibling = np->sibling;
 	}
 	}
 
 
+	of_node_set_flag(np, OF_DETACHED);
+
 out_unlock:
 out_unlock:
 	write_unlock(&devtree_lock);
 	write_unlock(&devtree_lock);
 }
 }

+ 1 - 0
include/asm-powerpc/prom.h

@@ -99,6 +99,7 @@ extern struct device_node *of_chosen;
 
 
 /* flag descriptions */
 /* flag descriptions */
 #define OF_DYNAMIC	1 /* node and properties were allocated via kmalloc */
 #define OF_DYNAMIC	1 /* node and properties were allocated via kmalloc */
+#define OF_DETACHED	2 /* node has been detached from the device tree */
 
 
 static inline int of_node_check_flag(struct device_node *n, unsigned long flag)
 static inline int of_node_check_flag(struct device_node *n, unsigned long flag)
 {
 {