Added config_detach_children, config_activate_children.
authorcsapuntz <csapuntz@openbsd.org>
Sun, 9 Apr 2000 19:23:18 +0000 (19:23 +0000)
committercsapuntz <csapuntz@openbsd.org>
Sun, 9 Apr 2000 19:23:18 +0000 (19:23 +0000)
Added ref counting to devices: device_ref, device_unref and a new method
for devices : ca_zeroref, called when the ref count hits zero.

Note that ca_zeroref may be called from interrupt context.

sys/kern/subr_autoconf.c
sys/sys/device.h

index 12f8234..1dcdcac 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: subr_autoconf.c,v 1.25 1999/08/08 00:37:09 niklas Exp $       */
+/*     $OpenBSD: subr_autoconf.c,v 1.26 2000/04/09 19:23:18 csapuntz Exp $     */
 /*     $NetBSD: subr_autoconf.c,v 1.21 1996/04/04 06:06:18 cgd Exp $   */
 
 /*
@@ -400,6 +400,7 @@ config_attach(parent, match, aux, print)
                cf->cf_fstate = FSTATE_FOUND;
 
        TAILQ_INSERT_TAIL(&alldevs, dev, dv_list);
+       device_ref(dev);
 
        if (parent == ROOT)
                printf("%s (root)", dev->dv_xname);
@@ -508,6 +509,8 @@ config_make_softc(parent, cf)
        if (cd->cd_devs[dev->dv_unit])
                panic("config_make_softc: duplicate %s", dev->dv_xname);
 
+       dev->dv_ref = 1;
+
        return (dev);
 }
 
@@ -607,6 +610,7 @@ config_detach(dev, flags)
         * Unlink from device list.
         */
        TAILQ_REMOVE(&alldevs, dev, dv_list);
+       device_unref(dev);
 
        /*
         * Remove from cfdriver's array, tell the world, and free softc.
@@ -614,8 +618,8 @@ config_detach(dev, flags)
        cd->cd_devs[dev->dv_unit] = NULL;
        if ((flags & DETACH_QUIET) == 0)
                printf("%s detached\n", dev->dv_xname);
-       free(dev, M_DEVBUF);
 
+       device_unref(dev);
        /*
         * If the device now has no units in use, deallocate its softc array.
         */
@@ -722,6 +726,139 @@ config_process_deferred_children(parent)
        }
 }
 
+int
+config_detach_children(parent, flags)
+       struct device *parent;
+       int flags;
+{
+       struct device *dev, *next_dev;
+       int  rv = 0;
+
+       /* The config_detach routine may sleep, meaning devices
+          may be added to the queue. However, all devices will
+          be added to the tail of the queue, the queue won't
+          be re-organized, and the subtree of parent here should be locked
+          for purposes of adding/removing children.
+       */
+       for (dev  = TAILQ_FIRST(&alldevs);
+            dev != NULL; dev = next_dev) {
+               next_dev = TAILQ_NEXT(dev, dv_list);
+               if (dev->dv_parent == parent &&
+                   (rv = config_detach(dev, flags)))
+                       return (rv);
+       }
+
+       return  (rv);
+}
+
+int
+config_activate_children(parent, act)
+       struct device *parent;
+       enum devact act;
+{
+       struct device *dev, *next_dev;
+       int  rv = 0;
+
+       /* The config_deactivate routine may sleep, meaning devices
+          may be added to the queue. However, all devices will
+          be added to the tail of the queue, the queue won't
+          be re-organized, and the subtree of parent here should be locked
+          for purposes of adding/removing children.
+       */
+       for (dev = TAILQ_FIRST(&alldevs);
+            dev != NULL; dev = next_dev) {
+               next_dev = TAILQ_NEXT(dev, dv_list);
+               if (dev->dv_parent == parent) {
+                       switch (act) {
+                       case DVACT_ACTIVATE:
+                               rv = config_activate(dev);
+                               break;
+                       case DVACT_DEACTIVATE:
+                               rv = config_deactivate(dev);
+                               break;
+                       default:
+#ifdef DIAGNOSTIC
+                               printf ("config_activate_children: shouldn't get here");
+#endif
+                               rv = EOPNOTSUPP;
+                               break;
+
+                       }
+                                               
+                       if (rv)
+                               break;
+               }
+       }
+
+       return  (rv);
+}
+
+/* 
+ * Lookup a device in the cfdriver device array.  Does not return a
+ * device if it is not active.
+ *
+ * Increments ref count on the device by one, reflecting the
+ * new reference created on the stack.
+ *
+ * Context: process only 
+ */
+struct device *
+device_lookup(cd, unit)
+       struct cfdriver *cd;
+       int unit;
+{
+       struct device *dv = NULL;
+
+       if (unit >= 0 && unit <= cd->cd_ndevs)
+               dv = (struct device *)(cd->cd_devs[unit]);
+       
+       if (!dv)
+               return (NULL);
+
+       if (!(dv->dv_flags & DVF_ACTIVE))
+               dv = NULL;
+
+       if (dv != NULL)
+               device_ref(dv);
+
+       return (dv);
+}
+
+
+/*
+ * Increments the ref count on the device structure. The device
+ * structure is freed when the ref count hits 0.
+ *
+ * Context: process or interrupt
+ */
+void
+device_ref(dv)
+       struct device *dv;
+{
+       dv->dv_ref++;
+}
+
+/*
+ * Decrement the ref count on the device structure.
+ *
+ * free's the structure when the ref count hits zero and calls the zeroref
+ * function.
+ *
+ * Context: process or interrupt
+ */
+void
+device_unref(dv)
+       struct device *dv;
+{
+       dv->dv_ref--;
+       if (dv->dv_ref == 0) {
+               if (dv->dv_cfdata->cf_attach->ca_zeroref)
+                       (*dv->dv_cfdata->cf_attach->ca_zeroref)(dv);
+               
+               free(dv, M_DEVBUF);
+       }
+}
+
 /*
  * Attach an event.  These must come from initially-zero space (see
  * commented-out assignments below), but that occurs naturally for
index 5db3220..4b319b8 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: device.h,v 1.17 1999/08/08 00:37:09 niklas Exp $      */
+/*     $OpenBSD: device.h,v 1.18 2000/04/09 19:23:18 csapuntz Exp $    */
 /*     $NetBSD: device.h,v 1.15 1996/04/09 20:55:24 cgd Exp $  */
 
 /*
@@ -71,6 +71,8 @@ enum devact {
        DVACT_DEACTIVATE,       /* deactivate the device */
 };
 
+#include <sys/lock.h>
+
 struct device {
        enum    devclass dv_class;      /* this device's classification */
        TAILQ_ENTRY(device) dv_list;    /* entry on list of all devices */
@@ -79,6 +81,7 @@ struct device {
        char    dv_xname[16];           /* external name (name + unit) */
        struct  device *dv_parent;      /* pointer to parent device */
        int     dv_flags;               /* misc. flags; see below */
+       int     dv_ref;                 /* ref count */
 };
 
 /* dv_flags */
@@ -142,6 +145,7 @@ struct cfattach {
        void    (*ca_attach) __P((struct device *, struct device *, void *));
        int     (*ca_detach) __P((struct device *, int));
        int     (*ca_activate) __P((struct device *, enum devact));
+       void    (*ca_zeroref) __P((struct device *));
 };
 
 /* Flags given to config_detach(), and the ca_detach function. */
@@ -197,13 +201,19 @@ struct device *config_rootfound __P((char *, void *));
 void config_scan __P((cfscan_t, struct device *));
 struct device *config_attach __P((struct device *, void *, void *, cfprint_t));
 int config_detach __P((struct device *, int));
+int config_detach_children __P((struct device *, int));
 int config_activate __P((struct device *));
 int config_deactivate __P((struct device *));
+int config_activate_children __P((struct device *, enum devact));
 struct device *config_make_softc __P((struct device *parent,
     struct cfdata *cf));
 void config_defer __P((struct device *, void (*)(struct device *)));
 void evcnt_attach __P((struct device *, const char *, struct evcnt *));
 
+struct device *device_lookup __P((struct cfdriver *, int unit));
+void device_ref __P((struct device *));
+void device_unref __P((struct device *));
+
 /* compatibility definitions */
 #define config_found(d, a, p)  config_found_sm((d), (a), (p), NULL)
 #if 0