[PATCH RFD] alternative kobject release wait mechanism

From: Tejun Heo
Date: Tue Apr 17 2007 - 14:41:37 EST


Hello, all.

Agreed with the problem but I'm not very enthusiastic for adding
kobj->owner. How about the following? exit() routines will have to
do device_unregister_wait() instead of device_unregister(). On return
from it, it's guaranteed that all references to it are dropped and
->release is finished. The caller is responsible for avoiding
deadlock, of course.

The code is only compile-tested and is more of proof-of-concept than
working code.

DO NOT APPLY.

diff --git a/drivers/base/core.c b/drivers/base/core.c
index 37930d0..72ccf16 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -741,6 +741,17 @@ void put_device(struct device * dev)


/**
+ * put_device_wait - put and wait for all references to go away
+ * @dev: device in question.
+ */
+void put_device_wait(struct device * dev)
+{
+ if (dev)
+ kobject_put_wait(&dev->kobj);
+}
+
+
+/**
* device_del - delete device from system.
* @dev: device.
*
@@ -839,6 +850,20 @@ void device_unregister(struct device * dev)
}


+/**
+ * device_unregister_wait - unregister dev and wait for it to be released
+ * @dev: device going away.
+ *
+ * Delete and put @dev; then, wait for it to be released.
+ */
+void device_unregister_wait(struct device * dev)
+{
+ pr_debug("DEV: Unregistering device. ID = '%s'\n", dev->bus_id);
+ device_del(dev);
+ put_device_wait(dev);
+}
+
+
static struct device * next_device(struct klist_iter * i)
{
struct klist_node * n = klist_next(i);
@@ -917,8 +942,10 @@ EXPORT_SYMBOL_GPL(device_register);

EXPORT_SYMBOL_GPL(device_del);
EXPORT_SYMBOL_GPL(device_unregister);
+EXPORT_SYMBOL_GPL(device_unregister_wait);
EXPORT_SYMBOL_GPL(get_device);
EXPORT_SYMBOL_GPL(put_device);
+EXPORT_SYMBOL_GPL(put_device_wait);

EXPORT_SYMBOL_GPL(device_create_file);
EXPORT_SYMBOL_GPL(device_remove_file);
diff --git a/include/linux/device.h b/include/linux/device.h
index 5cf30e9..1a1f1da 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -490,6 +490,7 @@ void driver_init(void);
*/
extern int __must_check device_register(struct device * dev);
extern void device_unregister(struct device * dev);
+extern void device_unregister_wait(struct device * dev);
extern void device_initialize(struct device * dev);
extern int __must_check device_add(struct device * dev);
extern void device_del(struct device * dev);
@@ -535,6 +536,7 @@ extern int (*platform_notify_remove)(struct device * dev);
*/
extern struct device * get_device(struct device * dev);
extern void put_device(struct device * dev);
+extern void put_device_wait(struct device * dev);


/* drivers/base/power/shutdown.c */
diff --git a/include/linux/kobject.h b/include/linux/kobject.h
index b850e03..4f0bb2d 100644
--- a/include/linux/kobject.h
+++ b/include/linux/kobject.h
@@ -85,9 +85,11 @@ extern int __must_check kobject_move(struct kobject *, struct kobject *);

extern int __must_check kobject_register(struct kobject *);
extern void kobject_unregister(struct kobject *);
+extern void kobject_unregister_wait(struct kobject *);

extern struct kobject * kobject_get(struct kobject *);
extern void kobject_put(struct kobject *);
+extern void kobject_put_wait(struct kobject *);

extern struct kobject *kobject_add_dir(struct kobject *, const char *);

diff --git a/lib/kobject.c b/lib/kobject.c
index 057921c..22e5148 100644
--- a/lib/kobject.c
+++ b/lib/kobject.c
@@ -16,6 +16,15 @@
#include <linux/stat.h>
#include <linux/slab.h>

+struct kobj_wait {
+ struct list_head list;
+ struct kobject *kobj;
+ struct completion cmpl;
+};
+
+static spinlock_t kobj_wait_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(kobj_wait_list);
+
/**
* populate_dir - populate directory with attributes.
* @kobj: object we're working on.
@@ -425,6 +434,23 @@ void kobject_unregister(struct kobject * kobj)
}

/**
+ * kobject_unregister_wait - unregister, put and wait
+ * @kobj: object going away
+ *
+ * Remove @kobj from hierarchy, decrement refcount and wait for
+ * it to die.
+ */
+void kobject_unregister_wait(struct kobject * kobj)
+{
+ if (!kobj)
+ return;
+ pr_debug("kobject %s: unregistering\n",kobject_name(kobj));
+ kobject_uevent(kobj, KOBJ_REMOVE);
+ kobject_del(kobj);
+ kobject_put_wait(kobj);
+}
+
+/**
* kobject_get - increment refcount for object.
* @kobj: object.
*/
@@ -446,8 +472,22 @@ void kobject_cleanup(struct kobject * kobj)
struct kobj_type * t = get_ktype(kobj);
struct kset * s = kobj->kset;
struct kobject * parent = kobj->parent;
+ struct kobj_wait *kwait;
+ unsigned long flags;

pr_debug("kobject %s: cleaning up\n",kobject_name(kobj));
+
+ /* is somebody waiting for @kobj to die? */
+ spin_lock_irqsave(&kobj_wait_lock, flags);
+ list_for_each_entry(kwait, &kobj_wait_list, list) {
+ if (kwait->kobj == kobj) {
+ list_del_init(&kwait->list);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&kobj_wait_lock, flags);
+
+ /* clean up */
if (kobj->k_name != kobj->name)
kfree(kobj->k_name);
kobj->k_name = NULL;
@@ -456,6 +496,10 @@ void kobject_cleanup(struct kobject * kobj)
if (s)
kset_put(s);
kobject_put(parent);
+
+ /* notify waiter */
+ if (kwait)
+ complete(&kwait->cmpl);
}

static void kobject_release(struct kref *kref)
@@ -475,6 +519,42 @@ void kobject_put(struct kobject * kobj)
kref_put(&kobj->kref, kobject_release);
}

+/**
+ * kobject_put_wait - put and wait for all references to go away
+ * @kobj: object
+ *
+ * This function is used by @kobj owner to kill it - it puts a
+ * reference to @kobj and waits till all the references are gone
+ * and ->release is finished. @kobj must have been deleted and
+ * the caller is responsible for guaranteeing that all references
+ * will be dropped in foreseeable future.
+ */
+void kobject_put_wait(struct kobject * kobj)
+{
+ struct kobj_wait kwait;
+ unsigned long flags;
+
+ if (!kobj)
+ return;
+
+ BUG_ON(!list_empty(&kobj->entry));
+
+ init_completion(&kwait.cmpl);
+ kwait.kobj = kobj;
+
+ spin_lock_irqsave(&kobj_wait_lock, flags);
+ list_add(&kwait.list, &kobj_wait_list);
+ spin_unlock_irqrestore(&kobj_wait_lock, flags);
+
+ kobject_put(kobj);
+
+ if (!wait_for_completion_timeout(&kwait.cmpl, 30 * HZ)) {
+ printk(KERN_WARNING "kobject_put_wait: kobject %p is still "
+ "alive after 30s, possible reference count bug\n", kobj);
+ dump_stack();
+ wait_for_completion(&kwait.cmpl);
+ }
+}

static void dir_release(struct kobject *kobj)
{
@@ -691,8 +771,10 @@ void subsys_remove_file(struct subsystem * s, struct subsys_attribute * a)
EXPORT_SYMBOL(kobject_init);
EXPORT_SYMBOL(kobject_register);
EXPORT_SYMBOL(kobject_unregister);
+EXPORT_SYMBOL(kobject_unregister_wait);
EXPORT_SYMBOL(kobject_get);
EXPORT_SYMBOL(kobject_put);
+EXPORT_SYMBOL(kobject_put_wait);
EXPORT_SYMBOL(kobject_add);
EXPORT_SYMBOL(kobject_del);

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/