> On Wed, 5 Jun 2002, Patrick Mochel wrote:
>
> > No, I haven't given up on it, and I admit it's fishy. However, I did it
> > to ensure that no one can access the driver since it is going away as
> > soon as we return to pci_unregister_driver. Though, I'll also admit that
> > making the next potential user hit the BUG() in get_driver() is not the
> > nicest thing to do...
>
> There's two phases:
> o remove_driver():
> Make sure that now one else can find (and inc the refcount) your struct
> anymore. That can and should be done immediately. Drop your reference.
> o At some point later, everybody else will have dropped their references
> and the destructor can be called.
>
> Alternatively:
> o remove_driver():
> Make sure that now one else can find (and inc the refcount) your struct
> anymore. That can and should be done immediately. Drop your reference.
> Wait for everybody else to drop their reference. Then return to the
> caller
> o At some point later, everybody else will have dropped their references
> and the remove_driver() should be woken up and you get to the return.
Alright, I can deal. I expanded on what you had, and moved it into the
core. It's not tested, but lemme know what you think.
-pat
===== drivers/base/driver.c 1.7 vs edited =====
--- 1.7/drivers/base/driver.c Wed Jun 5 15:59:31 2002
+++ edited/drivers/base/driver.c Wed Jun 5 17:29:55 2002
@@ -8,6 +8,7 @@
#include <linux/device.h>
#include <linux/module.h>
#include <linux/errno.h>
+#include <linux/completion.h>
#include "base.h"
@@ -70,6 +71,8 @@
atomic_set(&drv->refcount,2);
rwlock_init(&drv->lock);
INIT_LIST_HEAD(&drv->devices);
+ init_completion(&drv->complete);
+
write_lock(&drv->bus->lock);
list_add(&drv->bus_list,&drv->bus->drivers);
write_unlock(&drv->bus->lock);
@@ -84,18 +87,21 @@
pr_debug("Unregistering driver '%s' from bus '%s'\n",drv->name,drv->bus->name);
driver_detach(drv);
driverfs_remove_dir(&drv->dir);
- if (drv->release)
- drv->release(drv);
put_bus(drv->bus);
}
-void remove_driver(struct device_driver * drv)
+void driver_unregister(struct device_driver * drv)
{
+ /* make sure no one can get to it via the bus any more */
write_lock(&drv->bus->lock);
- atomic_set(&drv->refcount,0);
list_del_init(&drv->bus_list);
write_unlock(&drv->bus->lock);
- __remove_driver(drv);
+
+ /* dec the refcount */
+ put_driver(drv);
+
+ /* if we didn't get rid of the driver, wait for it to be done */
+ wait_for_completion(&drv->complete);
}
/**
@@ -112,9 +118,10 @@
list_del_init(&drv->bus_list);
write_unlock(&drv->bus->lock);
__remove_driver(drv);
+ complete(&drv->complete);
}
EXPORT_SYMBOL(driver_for_each_dev);
EXPORT_SYMBOL(driver_register);
+EXPORT_SYMBOL(driver_unregister);
EXPORT_SYMBOL(put_driver);
-EXPORT_SYMBOL(remove_driver);
===== include/linux/device.h 1.20 vs edited =====
--- 1.20/include/linux/device.h Wed Jun 5 14:43:23 2002
+++ edited/include/linux/device.h Wed Jun 5 17:33:21 2002
@@ -28,6 +28,7 @@
#include <linux/ioport.h>
#include <linux/list.h>
#include <linux/sched.h>
+#include <linux/completion.h>
#include <linux/driverfs_fs.h>
#define DEVICE_NAME_SIZE 80
@@ -91,6 +92,7 @@
rwlock_t lock;
atomic_t refcount;
+ struct completion complete;
list_t bus_list;
list_t devices;
@@ -102,13 +104,12 @@
int (*suspend) (struct device * dev, u32 state, u32 level);
int (*resume) (struct device * dev, u32 level);
-
- void (*release) (struct device_driver * drv);
};
extern int driver_register(struct device_driver * drv);
+extern void driver_unregister(struct device_driver * drv);
static inline struct device_driver * get_driver(struct device_driver * drv)
{
@@ -118,7 +119,6 @@
}
extern void put_driver(struct device_driver * drv);
-extern void remove_driver(struct device_driver * drv);
extern int driver_for_each_dev(struct device_driver * drv, void * data,
int (*callback)(struct device * dev, void * data));
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/