[PATCH RFD] alternative kobject release wait mechanism

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux