Re: Problem with new driver model?

From: Patrick Mochel (mochel@osdl.org)
Date: Wed Jun 05 2002 - 15:43:53 EST


On 5 Jun 2002, Paul Fulghum wrote:

> When testing the drivers I maintain on 2.5.20, I hit the
> BUG_ON in include/linux/devices.txt:115.

There is a patch that should fix this in Linus's tree.

If you're using bitkeeper, you can pull it from
linux.bkbits.net/linux-2.5.

If not, it's appended here.

        -pat

diff -Nru a/drivers/base/core.c b/drivers/base/core.c
--- a/drivers/base/core.c Wed Jun 5 13:43:20 2002
+++ b/drivers/base/core.c Wed Jun 5 13:43:20 2002
@@ -5,6 +5,8 @@
  * 2002 Open Source Development Lab
  */
 
+#define DEBUG 0
+
 #include <linux/device.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -118,9 +120,8 @@
         return bus_for_each_dev(drv->bus,drv,do_driver_bind);
 }
 
-static int do_driver_unbind(struct device * dev, void * data)
+static int do_driver_unbind(struct device * dev, struct device_driver * drv)
 {
- struct device_driver * drv = (struct device_driver *)data;
         lock_device(dev);
         if (dev->driver == drv) {
                 dev->driver = NULL;
@@ -134,7 +135,31 @@
 
 void driver_unbind(struct device_driver * drv)
 {
- driver_for_each_dev(drv,drv,do_driver_unbind);
+ struct device * next;
+ struct device * dev = NULL;
+ struct list_head * node;
+ int error = 0;
+
+ read_lock(&drv->lock);
+ node = drv->devices.next;
+ while (node != &drv->devices) {
+ next = list_entry(node,struct device,driver_list);
+ get_device(next);
+ read_unlock(&drv->lock);
+
+ if (dev)
+ put_device(dev);
+ dev = next;
+ if ((error = do_driver_unbind(dev,drv))) {
+ put_device(dev);
+ break;
+ }
+ read_lock(&drv->lock);
+ node = dev->driver_list.next;
+ }
+ read_unlock(&drv->lock);
+ if (dev)
+ put_device(dev);
 }
 
 /**
@@ -178,8 +203,8 @@
         }
         spin_unlock(&device_lock);
 
- DBG("DEV: registering device: ID = '%s', name = %s\n",
- dev->bus_id, dev->name);
+ pr_debug("DEV: registering device: ID = '%s', name = %s\n",
+ dev->bus_id, dev->name);
 
         if ((error = device_make_dir(dev)))
                 goto register_done;
@@ -212,8 +237,8 @@
         list_del_init(&dev->g_list);
         spin_unlock(&device_lock);
 
- DBG("DEV: Unregistering device. ID = '%s', name = '%s'\n",
- dev->bus_id,dev->name);
+ pr_debug("DEV: Unregistering device. ID = '%s', name = '%s'\n",
+ dev->bus_id,dev->name);
 
         /* Notify the platform of the removal, in case they
          * need to do anything...
diff -Nru a/drivers/base/driver.c b/drivers/base/driver.c
--- a/drivers/base/driver.c Wed Jun 5 13:43:20 2002
+++ b/drivers/base/driver.c Wed Jun 5 13:43:20 2002
@@ -3,6 +3,8 @@
  *
  */
 
+#define DEBUG 0
+
 #include <linux/device.h>
 #include <linux/module.h>
 #include <linux/errno.h>
@@ -67,6 +69,7 @@
         get_bus(drv->bus);
         atomic_set(&drv->refcount,2);
         rwlock_init(&drv->lock);
+ INIT_LIST_HEAD(&drv->devices);
         write_lock(&drv->bus->lock);
         list_add(&drv->bus_list,&drv->bus->drivers);
         write_unlock(&drv->bus->lock);
@@ -76,16 +79,8 @@
         return 0;
 }
 
-/**
- * put_driver - decrement driver's refcount and clean up if necessary
- * @drv: driver in question
- */
-void put_driver(struct device_driver * drv)
+static void __remove_driver(struct device_driver * drv)
 {
- if (!atomic_dec_and_lock(&drv->refcount,&device_lock))
- return;
- spin_unlock(&device_lock);
-
         if (drv->bus) {
                 pr_debug("Unregistering driver '%s' from bus '%s'\n",drv->name,drv->bus->name);
 
@@ -101,6 +96,28 @@
                 drv->release(drv);
 }
 
+void remove_driver(struct device_driver * drv)
+{
+ spin_lock(&device_lock);
+ atomic_set(&drv->refcount,0);
+ spin_unlock(&device_lock);
+ __remove_driver(drv);
+}
+
+/**
+ * put_driver - decrement driver's refcount and clean up if necessary
+ * @drv: driver in question
+ */
+void put_driver(struct device_driver * drv)
+{
+ if (!atomic_dec_and_lock(&drv->refcount,&device_lock))
+ return;
+ spin_unlock(&device_lock);
+
+ __remove_driver(drv);
+}
+
 EXPORT_SYMBOL(driver_for_each_dev);
 EXPORT_SYMBOL(driver_register);
 EXPORT_SYMBOL(put_driver);
+EXPORT_SYMBOL(remove_driver);
diff -Nru a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
--- a/drivers/pci/pci-driver.c Wed Jun 5 13:43:20 2002
+++ b/drivers/pci/pci-driver.c Wed Jun 5 13:43:20 2002
@@ -38,23 +38,35 @@
 static int pci_device_probe(struct device * dev)
 {
         int error = 0;
+ struct pci_driver *drv;
+ struct pci_dev *pci_dev;
 
- struct pci_driver * drv = list_entry(dev->driver,struct pci_driver,driver);
- struct pci_dev * pci_dev = list_entry(dev,struct pci_dev,dev);
+ drv = list_entry(dev->driver, struct pci_driver, driver);
+ pci_dev = list_entry(dev, struct pci_dev, dev);
+
+ if (drv->probe) {
+ const struct pci_device_id *id;
 
- if (drv->probe)
- error = drv->probe(pci_dev,drv->id_table);
- return error > 0 ? 0 : -ENODEV;
+ id = pci_match_device(drv->id_table, pci_dev);
+ if (id)
+ error = drv->probe(pci_dev, id);
+ if (error >= 0) {
+ pci_dev->driver = drv;
+ error = 0;
+ }
+ }
+ return error;
 }
 
 static int pci_device_remove(struct device * dev)
 {
         struct pci_dev * pci_dev = list_entry(dev,struct pci_dev,dev);
+ struct pci_driver * drv = pci_dev->driver;
 
- if (dev->driver) {
- struct pci_driver * drv = list_entry(dev->driver,struct pci_driver,driver);
+ if (drv) {
                 if (drv->remove)
                         drv->remove(pci_dev);
+ pci_dev->driver = NULL;
         }
         return 0;
 }
@@ -124,7 +136,7 @@
 void
 pci_unregister_driver(struct pci_driver *drv)
 {
- put_driver(&drv->driver);
+ remove_driver(&drv->driver);
 }
 
 static struct pci_driver pci_compat_driver = {
diff -Nru a/include/linux/device.h b/include/linux/device.h
--- a/include/linux/device.h Wed Jun 5 13:43:20 2002
+++ b/include/linux/device.h Wed Jun 5 13:43:20 2002
@@ -118,6 +118,7 @@
 }
 
 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/



This archive was generated by hypermail 2b29 : Fri Jun 07 2002 - 22:00:26 EST