From 07e3414a332b23aba4b16d6b0302cf906684f033 Mon Sep 17 00:00:00 2001 From: csapuntz Date: Sun, 9 Apr 2000 19:23:18 +0000 Subject: [PATCH] Added config_detach_children, config_activate_children. 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 | 141 ++++++++++++++++++++++++++++++++++++++- sys/sys/device.h | 12 +++- 2 files changed, 150 insertions(+), 3 deletions(-) diff --git a/sys/kern/subr_autoconf.c b/sys/kern/subr_autoconf.c index 12f8234d317..1dcdcacf061 100644 --- a/sys/kern/subr_autoconf.c +++ b/sys/kern/subr_autoconf.c @@ -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 diff --git a/sys/sys/device.h b/sys/sys/device.h index 5db3220c683..4b319b84125 100644 --- a/sys/sys/device.h +++ b/sys/sys/device.h @@ -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 + 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 -- 2.20.1