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 [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to