DEVS - Clean up some reference counting issues
authorMatthew Dillon <dillon@apollo.backplane.com>
Wed, 17 Jun 2009 19:29:13 +0000 (12:29 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Wed, 17 Jun 2009 19:29:13 +0000 (12:29 -0700)
* Add dev_ops_remove_override() as the counterpart to dev_ops_add_override().

* Do not complain if ops->head.refs is non-zero when deleting ops,
  as the ops structure might be used with other unit numbers.

* Fix a bad assertion that was leading to incorrect panics when selectively
  removing dev_ops.

sys/kern/kern_conf.c
sys/kern/kern_device.c
sys/kern/subr_disk.c
sys/sys/device.h

index cc86d66..f73e827 100644 (file)
@@ -422,10 +422,10 @@ destroy_all_devs(struct dev_ops *ops, u_int mask, u_int match)
                ndev = LIST_FIRST(&dev_hash[i]);
                while ((dev = ndev) != NULL) {
                    ndev = LIST_NEXT(dev, si_hash);
-                   KKASSERT(dev->si_flags & SI_ADHOC);
                    if (dev->si_ops == ops && 
                        ((u_int)dev->si_uminor & mask) == match
                    ) {
+                       KKASSERT(dev->si_flags & SI_ADHOC);
                        reference_dev(dev);
                        destroy_dev(dev);
                    }
index c0ba2ba..38ce9bf 100644 (file)
@@ -521,6 +521,19 @@ dev_ops_add_override(cdev_t backing_dev, struct dev_ops *template,
        return(ops);
 }
 
+void
+dev_ops_remove_override(struct dev_ops *ops, u_int mask, u_int match)
+{
+       dev_ops_remove(ops, mask, match);
+       if (ops->head.refs) {
+               kprintf("dev_ops_remove_override: %s still has %d refs!\n",
+                       ops->head.name, ops->head.refs);
+       } else {
+               bzero(ops, sizeof(*ops));
+               kfree(ops, M_DEVBUF);
+       }
+}
+
 /*
  * Remove all matching dev_ops entries from the dev_ops_array[] major
  * array so no new user opens can be performed, and destroy all devices
@@ -576,6 +589,11 @@ dev_ops_remove(struct dev_ops *ops, u_int mask, u_int match)
                kfree(rbmaj, M_DEVBUF);
        }
 
+#if 0
+       /*
+        * The same ops might be used with multiple devices, so don't
+        * complain if the ref count is non-zero.
+        */
        if (ops->head.refs != 0) {
                kprintf("%s(%d)[%08x/%08x]: Warning: dev_ops_remove() called "
                        "while %d device refs still exist!\n", 
@@ -585,6 +603,7 @@ dev_ops_remove(struct dev_ops *ops, u_int mask, u_int match)
                if (bootverbose)
                        kprintf("%s: ops removed\n", ops->head.name);
        }
+#endif
        return 0;
 }
 
index 1e7ebac..ca8629a 100644 (file)
@@ -200,14 +200,16 @@ disk_setdiskinfo(struct disk *disk, struct disk_info *info)
 void
 disk_destroy(struct disk *disk)
 {
+       u_int match;
+
        if (disk->d_dev_ops) {
-           dev_ops_remove(disk->d_dev_ops, dkunitmask(), 
-                           dkmakeunit(dkunit(disk->d_cdev)));
+           match = dkmakeunit(dkunit(disk->d_cdev));
+           dev_ops_remove_override(disk->d_dev_ops, dkunitmask(), match);
            LIST_REMOVE(disk, d_list);
        }
        if (disk->d_raw_ops) {
-           destroy_all_devs(disk->d_raw_ops, dkunitmask(), 
-                           dkmakeunit(dkunit(disk->d_rawdev)));
+           match = dkmakeunit(dkunit(disk->d_rawdev));
+           destroy_all_devs(disk->d_raw_ops, dkunitmask(), match);
        }
        bzero(disk, sizeof(*disk));
 }
index 3b55b74..c5eb2eb 100644 (file)
@@ -336,6 +336,7 @@ int dev_ops_add(struct dev_ops *, u_int mask, u_int match);
 int dev_ops_remove(struct dev_ops *, u_int mask, u_int match);
 void dev_ops_release(struct dev_ops *);
 struct dev_ops *dev_ops_add_override(cdev_t, struct dev_ops *, u_int, u_int);
+void dev_ops_remove_override(struct dev_ops *ops, u_int mask, u_int match);
 
 struct dev_ops *dev_ops_intercept(cdev_t, struct dev_ops *);
 void dev_ops_restore(cdev_t, struct dev_ops *);