aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/fs/specfs/spec_vnops.c39
-rw-r--r--sys/kern/kern_conf.c183
-rw-r--r--sys/kern/vfs_bio.c7
-rw-r--r--sys/kern/vfs_subr.c6
-rw-r--r--sys/sys/conf.h51
-rw-r--r--sys/sys/linedisc.h51
6 files changed, 259 insertions, 78 deletions
diff --git a/sys/fs/specfs/spec_vnops.c b/sys/fs/specfs/spec_vnops.c
index 4682b5df6190..ab9ce7f5a5b4 100644
--- a/sys/fs/specfs/spec_vnops.c
+++ b/sys/fs/specfs/spec_vnops.c
@@ -140,7 +140,6 @@ spec_open(ap)
dev_t dev = vp->v_rdev;
int error;
struct cdevsw *dsw;
- const char *cp;
if (vp->v_type == VBLK)
return (ENXIO);
@@ -194,6 +193,8 @@ spec_open(ap)
vp->v_vflag |= VV_ISTTY;
VOP_UNLOCK(vp, 0, td);
+ dev_ref(dev);
+ cdevsw_ref(dsw);
if(!(dsw->d_flags & D_NEEDGIANT)) {
DROP_GIANT();
if (dsw->d_fdopen != NULL)
@@ -205,6 +206,9 @@ spec_open(ap)
error = dsw->d_fdopen(dev, ap->a_mode, td, ap->a_fdidx);
else
error = dsw->d_open(dev, ap->a_mode, S_IFCHR, td);
+ cdevsw_rel(dsw);
+ if (error != 0)
+ dev_rel(dev);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
if (error)
@@ -225,14 +229,6 @@ spec_open(ap)
if (!dev->si_bsize_phys)
dev->si_bsize_phys = DEV_BSIZE;
}
- if ((dsw->d_flags & D_DISK) == 0) {
- cp = devtoname(dev);
- if (*cp == '#' && (dsw->d_flags & D_NAGGED) == 0) {
- printf("WARNING: driver %s should register devices with make_dev() (dev_t = \"%s\")\n",
- dsw->d_name, cp);
- dsw->d_flags |= D_NAGGED;
- }
- }
return (error);
}
@@ -267,12 +263,16 @@ spec_read(ap)
dsw = devsw(dev);
VOP_UNLOCK(vp, 0, td);
+ KASSERT(dev->si_refcount > 0,
+ ("specread() on un-referenced dev_t (%s)", devtoname(dev)));
+ cdevsw_ref(dsw);
if (!(dsw->d_flags & D_NEEDGIANT)) {
DROP_GIANT();
error = dsw->d_read(dev, uio, ap->a_ioflag);
PICKUP_GIANT();
} else
error = dsw->d_read(dev, uio, ap->a_ioflag);
+ cdevsw_rel(dsw);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
if (uio->uio_resid != resid || (error == 0 && resid != 0))
vfs_timestamp(&dev->si_atime);
@@ -307,12 +307,16 @@ spec_write(ap)
resid = uio->uio_resid;
VOP_UNLOCK(vp, 0, td);
+ KASSERT(dev->si_refcount > 0,
+ ("spec_write() on un-referenced dev_t (%s)", devtoname(dev)));
+ cdevsw_ref(dsw);
if (!(dsw->d_flags & D_NEEDGIANT)) {
DROP_GIANT();
error = dsw->d_write(dev, uio, ap->a_ioflag);
PICKUP_GIANT();
} else
error = dsw->d_write(dev, uio, ap->a_ioflag);
+ cdevsw_rel(dsw);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
if (uio->uio_resid != resid || (error == 0 && resid != 0)) {
vfs_timestamp(&dev->si_ctime);
@@ -342,6 +346,9 @@ spec_ioctl(ap)
dev = ap->a_vp->v_rdev;
dsw = devsw(dev);
+ KASSERT(dev->si_refcount > 0,
+ ("spec_ioctl() on un-referenced dev_t (%s)", devtoname(dev)));
+ cdevsw_ref(dsw);
if (!(dsw->d_flags & D_NEEDGIANT)) {
DROP_GIANT();
error = dsw->d_ioctl(dev, ap->a_command,
@@ -350,6 +357,7 @@ spec_ioctl(ap)
} else
error = dsw->d_ioctl(dev, ap->a_command,
ap->a_data, ap->a_fflag, ap->a_td);
+ cdevsw_rel(dsw);
if (error == ENOIOCTL)
error = ENOTTY;
return (error);
@@ -371,12 +379,16 @@ spec_poll(ap)
dev = ap->a_vp->v_rdev;
dsw = devsw(dev);
+ KASSERT(dev->si_refcount > 0,
+ ("spec_poll() on un-referenced dev_t (%s)", devtoname(dev)));
+ cdevsw_ref(dsw);
if (!(dsw->d_flags & D_NEEDGIANT)) {
DROP_GIANT();
error = dsw->d_poll(dev, ap->a_events, ap->a_td);
PICKUP_GIANT();
} else
error = dsw->d_poll(dev, ap->a_events, ap->a_td);
+ cdevsw_rel(dsw);
return(error);
}
@@ -394,12 +406,16 @@ spec_kqfilter(ap)
dev = ap->a_vp->v_rdev;
dsw = devsw(dev);
+ KASSERT(dev->si_refcount > 0,
+ ("spec_kqfilter() on un-referenced dev_t (%s)", devtoname(dev)));
+ cdevsw_ref(dsw);
if (!(dsw->d_flags & D_NEEDGIANT)) {
DROP_GIANT();
error = dsw->d_kqfilter(dev, ap->a_kn);
PICKUP_GIANT();
} else
error = dsw->d_kqfilter(dev, ap->a_kn);
+ cdevsw_rel(dsw);
return (error);
}
@@ -631,12 +647,17 @@ spec_close(ap)
return (0);
}
VI_UNLOCK(vp);
+ KASSERT(dev->si_refcount > 0,
+ ("spec_close() on un-referenced dev_t (%s)", devtoname(dev)));
+ cdevsw_ref(dsw);
if (!(dsw->d_flags & D_NEEDGIANT)) {
DROP_GIANT();
error = dsw->d_close(dev, ap->a_fflag, S_IFCHR, td);
PICKUP_GIANT();
} else
error = dsw->d_close(dev, ap->a_fflag, S_IFCHR, td);
+ cdevsw_rel(dsw);
+ dev_rel(dev);
return (error);
}
diff --git a/sys/kern/kern_conf.c b/sys/kern/kern_conf.c
index bedbd1e48d41..1f8db46dd56d 100644
--- a/sys/kern/kern_conf.c
+++ b/sys/kern/kern_conf.c
@@ -68,6 +68,63 @@ static LIST_HEAD(, cdev) dev_free;
static int free_devt;
SYSCTL_INT(_debug, OID_AUTO, free_devt, CTLFLAG_RW, &free_devt, 0, "");
+static struct mtx devmtx;
+static void freedev(dev_t dev);
+
+static void
+devlock(void)
+{
+ if (!mtx_initialized(&devmtx))
+ mtx_init(&devmtx, "dev_t", NULL, MTX_DEF);
+ mtx_lock(&devmtx);
+}
+
+static void
+devunlock(void)
+{
+ mtx_unlock(&devmtx);
+}
+
+void
+dev_ref(dev_t dev)
+{
+ devlock();
+ dev->si_refcount++;
+ devunlock();
+}
+
+void
+dev_rel(dev_t dev)
+{
+ devlock();
+ dev->si_refcount--;
+ KASSERT(dev->si_refcount >= 0,
+ ("dev_rel(%s) gave negative count", devtoname(dev)));
+ if (dev->si_devsw == NULL && dev->si_refcount == 0) {
+ LIST_REMOVE(dev, si_list);
+ freedev(dev);
+ }
+ devunlock();
+}
+
+void
+cdevsw_ref(struct cdevsw *csw)
+{
+ devlock();
+ csw->d_refcount++;
+ devunlock();
+}
+
+void
+cdevsw_rel(struct cdevsw *csw)
+{
+ devlock();
+ csw->d_refcount--;
+ KASSERT(csw->d_refcount >= 0,
+ ("cdevsw_vrel(%s) gave negative count", csw->d_name));
+ devunlock();
+}
+
static dev_t makedev(int x, int y);
int
@@ -178,7 +235,7 @@ no_poll(dev_t dev __unused, int events, struct thread *td __unused)
struct cdevsw *
devsw(dev_t dev)
{
- if (dev->si_devsw)
+ if (dev->si_devsw != NULL)
return (dev->si_devsw);
return (&dead_cdevsw);
}
@@ -230,7 +287,6 @@ allocdev(void)
if (LIST_FIRST(&dev_free)) {
si = LIST_FIRST(&dev_free);
- LIST_REMOVE(si, si_hash);
} else if (stashed >= DEVT_STASH) {
MALLOC(si, struct cdev *, sizeof(*si), M_DEVT,
M_USE_RESERVE | M_ZERO | M_WAITOK);
@@ -267,17 +323,10 @@ makedev(int x, int y)
return (si);
}
-void
+static void
freedev(dev_t dev)
{
- if (!free_devt)
- return;
- if (SLIST_FIRST(&dev->si_hlist))
- return;
- if (dev->si_devsw || dev->si_drv1 || dev->si_drv2)
- return;
- LIST_REMOVE(dev, si_hash);
if (dev->si_flags & SI_STASHED) {
bzero(dev, sizeof(*dev));
dev->si_flags |= SI_STASHED;
@@ -340,12 +389,41 @@ find_major(struct cdevsw *devsw)
KASSERT(i > 0, ("Out of major numbers (%s)", devsw->d_name));
devsw->d_maj = i;
reserved_majors[i] = i;
+ devsw->d_flags |= D_ALLOCMAJ;
+}
+
+static void
+fini_cdevsw(struct cdevsw *devsw)
+{
+ if (devsw->d_flags & D_ALLOCMAJ) {
+ reserved_majors[devsw->d_maj] = 0;
+ devsw->d_maj = MAJOR_AUTO;
+ devsw->d_flags &= ~D_ALLOCMAJ;
+ }
}
static void
prep_cdevsw(struct cdevsw *devsw)
{
+ devlock();
+
+ if (devsw->d_version != D_VERSION_00) {
+ printf(
+ "WARNING: Device driver \"%s\" has wrong version %s\n",
+ devsw->d_name, "and is disabled. Recompile KLD module.");
+ devsw->d_open = dead_open;
+ devsw->d_close = dead_close;
+ devsw->d_read = dead_read;
+ devsw->d_write = dead_write;
+ devsw->d_ioctl = dead_ioctl;
+ devsw->d_poll = dead_poll;
+ devsw->d_mmap = dead_mmap;
+ devsw->d_strategy = dead_strategy;
+ devsw->d_dump = dead_dump;
+ devsw->d_kqfilter = dead_kqfilter;
+ }
+
if (devsw->d_flags & D_TTY) {
if (devsw->d_read == NULL) devsw->d_read = ttyread;
if (devsw->d_write == NULL) devsw->d_write = ttywrite;
@@ -363,6 +441,11 @@ prep_cdevsw(struct cdevsw *devsw)
if (devsw->d_strategy == NULL) devsw->d_strategy = no_strategy;
if (devsw->d_dump == NULL) devsw->d_dump = no_dump;
if (devsw->d_kqfilter == NULL) devsw->d_kqfilter = no_kqfilter;
+
+ LIST_INIT(&devsw->d_devs);
+
+ devsw->d_flags |= D_INIT;
+
if (devsw->d_maj == MAJOR_AUTO) {
find_major(devsw);
} else {
@@ -377,34 +460,37 @@ prep_cdevsw(struct cdevsw *devsw)
reserved_majors[devsw->d_maj] = devsw->d_maj;
}
}
+ devunlock();
}
dev_t
-make_dev(struct cdevsw *devsw, int minor, uid_t uid, gid_t gid, int perms,
- const char *fmt, ...)
+make_dev(struct cdevsw *devsw, int minornr, uid_t uid, gid_t gid, int perms, const char *fmt, ...)
{
dev_t dev;
va_list ap;
int i;
- KASSERT((minor & ~0xffff00ff) == 0,
- ("Invalid minor (0x%x) in make_dev", minor));
- prep_cdevsw(devsw);
- dev = makedev(devsw->d_maj, minor);
+ KASSERT((minornr & ~0xffff00ff) == 0,
+ ("Invalid minor (0x%x) in make_dev", minornr));
+
+ if (!(devsw->d_flags & D_INIT))
+ prep_cdevsw(devsw);
+ dev = makedev(devsw->d_maj, minornr);
if (dev->si_flags & SI_CHEAPCLONE &&
dev->si_flags & SI_NAMED &&
dev->si_devsw == devsw) {
/*
* This is allowed as it removes races and generally
* simplifies cloning devices.
+ * XXX: still ??
*/
return (dev);
}
- if (dev->si_flags & SI_NAMED) {
- printf( "WARNING: Driver mistake: repeat make_dev(\"%s\")\n",
- dev->si_name);
- panic("don't do that");
- }
+ devlock();
+ KASSERT(!(dev->si_flags & SI_NAMED),
+ ("make_dev() by driver %s on pre-existing device (maj=%d, min=%d, name=%s)",
+ devsw->d_name, major(dev), minor(dev), devtoname(dev)));
+
va_start(ap, fmt);
i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
if (i > (sizeof dev->__si_namebuf - 1)) {
@@ -418,7 +504,9 @@ make_dev(struct cdevsw *devsw, int minor, uid_t uid, gid_t gid, int perms,
dev->si_mode = perms;
dev->si_flags |= SI_NAMED;
+ LIST_INSERT_HEAD(&devsw->d_devs, dev, si_list);
devfs_create(dev);
+ devunlock();
return (dev);
}
@@ -439,9 +527,11 @@ void
dev_depends(dev_t pdev, dev_t cdev)
{
+ devlock();
cdev->si_parent = pdev;
cdev->si_flags |= SI_CHILD;
LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings);
+ devunlock();
}
dev_t
@@ -452,9 +542,9 @@ make_dev_alias(dev_t pdev, const char *fmt, ...)
int i;
dev = allocdev();
+ devlock();
dev->si_flags |= SI_ALIAS;
dev->si_flags |= SI_NAMED;
- dev_depends(pdev, dev);
va_start(ap, fmt);
i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
if (i > (sizeof dev->__si_namebuf - 1)) {
@@ -464,13 +554,14 @@ make_dev_alias(dev_t pdev, const char *fmt, ...)
va_end(ap);
devfs_create(dev);
+ devunlock();
+ dev_depends(pdev, dev);
return (dev);
}
-void
-destroy_dev(dev_t dev)
+static void
+idestroy_dev(dev_t dev)
{
-
if (!(dev->si_flags & SI_NAMED)) {
printf( "WARNING: Driver mistake: destroy_dev on %d/%d\n",
major(dev), minor(dev));
@@ -478,24 +569,55 @@ destroy_dev(dev_t dev)
}
devfs_destroy(dev);
+
+ /* Remove name marking */
dev->si_flags &= ~SI_NAMED;
+ /* If we are a child, remove us from the parents list */
if (dev->si_flags & SI_CHILD) {
LIST_REMOVE(dev, si_siblings);
dev->si_flags &= ~SI_CHILD;
}
+
+ /* Kill our children */
while (!LIST_EMPTY(&dev->si_children))
- destroy_dev(LIST_FIRST(&dev->si_children));
+ idestroy_dev(LIST_FIRST(&dev->si_children));
+
+ /* Remove from clone list */
if (dev->si_flags & SI_CLONELIST) {
LIST_REMOVE(dev, si_clone);
dev->si_flags &= ~SI_CLONELIST;
}
+
+ if (!(dev->si_flags & SI_ALIAS)) {
+ /* Remove from cdevsw list */
+ LIST_REMOVE(dev, si_list);
+
+ /* If cdevsw has no dev_t's, clean it */
+ if (LIST_EMPTY(&dev->si_devsw->d_devs))
+ fini_cdevsw(dev->si_devsw);
+
+ LIST_REMOVE(dev, si_hash);
+ }
dev->si_drv1 = 0;
dev->si_drv2 = 0;
- dev->si_devsw = 0;
+ dev->si_devsw = NULL;
bzero(&dev->__si_u, sizeof(dev->__si_u));
dev->si_flags &= ~SI_ALIAS;
- freedev(dev);
+ if (dev->si_refcount > 0) {
+ LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list);
+ } else {
+ freedev(dev);
+ }
+}
+
+void
+destroy_dev(dev_t dev)
+{
+
+ devlock();
+ idestroy_dev(dev);
+ devunlock();
}
const char *
@@ -672,12 +794,11 @@ sysctl_devname(SYSCTL_HANDLER_ARGS)
return (error);
if (ud == NOUDEV)
return(EINVAL);
- dev = makedev(umajor(ud), uminor(ud));
- if (dev->si_name[0] == '\0')
+ dev = udev2dev(ud);
+ if (dev == NODEV)
error = ENOENT;
else
error = SYSCTL_OUT(req, dev->si_name, strlen(dev->si_name) + 1);
- freedev(dev);
return (error);
}
diff --git a/sys/kern/vfs_bio.c b/sys/kern/vfs_bio.c
index eafb9773704c..e10d61e20a79 100644
--- a/sys/kern/vfs_bio.c
+++ b/sys/kern/vfs_bio.c
@@ -3069,12 +3069,19 @@ bufdonebio(struct bio *bp)
void
dev_strategy(struct buf *bp)
{
+ struct cdevsw *csw;
if ((!bp->b_iocmd) || (bp->b_iocmd & (bp->b_iocmd - 1)))
panic("b_iocmd botch");
bp->b_io.bio_done = bufdonebio;
bp->b_io.bio_caller2 = bp;
+ csw = devsw(bp->b_io.bio_dev);
+ KASSERT(bp->b_io.bio_dev->si_refcount > 0,
+ ("dev_strategy on un-referenced dev_t (%s)",
+ devtoname(bp->b_io.bio_dev)));
+ cdevsw_ref(csw);
(*devsw(bp->b_io.bio_dev)->d_strategy)(&bp->b_io);
+ cdevsw_rel(csw);
}
/*
diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c
index bcf7af869400..e43c78a28971 100644
--- a/sys/kern/vfs_subr.c
+++ b/sys/kern/vfs_subr.c
@@ -1853,6 +1853,7 @@ addalias(nvp, dev)
{
KASSERT(nvp->v_type == VCHR, ("addalias on non-special vnode"));
+ dev_ref(dev);
nvp->v_rdev = dev;
VI_LOCK(nvp);
mtx_lock(&spechash_mtx);
@@ -2469,7 +2470,7 @@ vop_revoke(ap)
mtx_lock(&spechash_mtx);
vq = SLIST_FIRST(&dev->si_hlist);
mtx_unlock(&spechash_mtx);
- if (!vq)
+ if (vq == NULL)
break;
vgone(vq);
}
@@ -2587,11 +2588,12 @@ vgonel(vp, td)
* if it is on one.
*/
VI_LOCK(vp);
- if (vp->v_type == VCHR && vp->v_rdev != NULL && vp->v_rdev != NODEV) {
+ if (vp->v_type == VCHR && vp->v_rdev != NODEV) {
mtx_lock(&spechash_mtx);
SLIST_REMOVE(&vp->v_rdev->si_hlist, vp, vnode, v_specnext);
vp->v_rdev->si_usecount -= vp->v_usecount;
mtx_unlock(&spechash_mtx);
+ dev_rel(vp->v_rdev);
vp->v_rdev = NULL;
}
diff --git a/sys/sys/conf.h b/sys/sys/conf.h
index 2a2b984f4e94..40ee4c346d5c 100644
--- a/sys/sys/conf.h
+++ b/sys/sys/conf.h
@@ -69,6 +69,8 @@ struct cdev {
struct timespec si_ctime;
struct timespec si_mtime;
udev_t si_udev;
+ int si_refcount;
+ LIST_ENTRY(cdev) si_list;
LIST_ENTRY(cdev) si_clone;
LIST_ENTRY(cdev) si_hash;
SLIST_HEAD(, vnode) si_hlist;
@@ -195,10 +197,9 @@ typedef int dumper_t(
#define D_TYPEMASK 0xffff
/*
- * Flags for d_flags.
+ * Flags for d_flags which the drivers can set.
*/
#define D_MEMDISK 0x00010000 /* memory type disk */
-#define D_NAGGED 0x00020000 /* nagged about missing make_dev() */
#define D_TRACKCLOSE 0x00080000 /* track all closes */
#define D_MMAP_ANON 0x00100000 /* special treatment in vm_mmap.c */
#define D_PSEUDO 0x00200000 /* make_dev() can return NULL */
@@ -211,24 +212,35 @@ typedef int dumper_t(
#define D_VERSION D_VERSION_00
/*
+ * Flags used for internal housekeeping
+ */
+#define D_INIT 0x80000000 /* cdevsw initialized */
+#define D_ALLOCMAJ 0x40000000 /* major# is allocated */
+
+/*
* Character device switch table
*/
struct cdevsw {
- int d_version;
- int d_maj;
- u_int d_flags;
- const char *d_name;
- d_open_t *d_open;
- d_fdopen_t *d_fdopen;
- d_close_t *d_close;
- d_read_t *d_read;
- d_write_t *d_write;
- d_ioctl_t *d_ioctl;
- d_poll_t *d_poll;
- d_mmap_t *d_mmap;
- d_strategy_t *d_strategy;
- dumper_t *d_dump;
- d_kqfilter_t *d_kqfilter;
+ int d_version;
+ int d_maj;
+ u_int d_flags;
+ const char *d_name;
+ d_open_t *d_open;
+ d_fdopen_t *d_fdopen;
+ d_close_t *d_close;
+ d_read_t *d_read;
+ d_write_t *d_write;
+ d_ioctl_t *d_ioctl;
+ d_poll_t *d_poll;
+ d_mmap_t *d_mmap;
+ d_strategy_t *d_strategy;
+ dumper_t *d_dump;
+ d_kqfilter_t *d_kqfilter;
+
+ /* These fields should not be messed with by drivers */
+ LIST_ENTRY(cdevsw) d_list;
+ LIST_HEAD(, cdev) d_devs;
+ int d_refcount;
};
/*
@@ -293,11 +305,14 @@ int clone_create(struct clonedevs **, struct cdevsw *, int *unit, dev_t *dev, u_
int count_dev(dev_t _dev);
void destroy_dev(dev_t _dev);
struct cdevsw *devsw(dev_t _dev);
+void cdevsw_ref(struct cdevsw *);
+void cdevsw_rel(struct cdevsw *);
const char *devtoname(dev_t _dev);
int dev_named(dev_t _pdev, const char *_name);
void dev_depends(dev_t _pdev, dev_t _cdev);
+void dev_ref(dev_t dev);
+void dev_rel(dev_t dev);
void dev_strategy(struct buf *bp);
-void freedev(dev_t _dev);
dev_t makebdev(int _maj, int _min);
dev_t make_dev(struct cdevsw *_devsw, int _minor, uid_t _uid, gid_t _gid,
int _perms, const char *_fmt, ...) __printflike(6, 7);
diff --git a/sys/sys/linedisc.h b/sys/sys/linedisc.h
index 2a2b984f4e94..40ee4c346d5c 100644
--- a/sys/sys/linedisc.h
+++ b/sys/sys/linedisc.h
@@ -69,6 +69,8 @@ struct cdev {
struct timespec si_ctime;
struct timespec si_mtime;
udev_t si_udev;
+ int si_refcount;
+ LIST_ENTRY(cdev) si_list;
LIST_ENTRY(cdev) si_clone;
LIST_ENTRY(cdev) si_hash;
SLIST_HEAD(, vnode) si_hlist;
@@ -195,10 +197,9 @@ typedef int dumper_t(
#define D_TYPEMASK 0xffff
/*
- * Flags for d_flags.
+ * Flags for d_flags which the drivers can set.
*/
#define D_MEMDISK 0x00010000 /* memory type disk */
-#define D_NAGGED 0x00020000 /* nagged about missing make_dev() */
#define D_TRACKCLOSE 0x00080000 /* track all closes */
#define D_MMAP_ANON 0x00100000 /* special treatment in vm_mmap.c */
#define D_PSEUDO 0x00200000 /* make_dev() can return NULL */
@@ -211,24 +212,35 @@ typedef int dumper_t(
#define D_VERSION D_VERSION_00
/*
+ * Flags used for internal housekeeping
+ */
+#define D_INIT 0x80000000 /* cdevsw initialized */
+#define D_ALLOCMAJ 0x40000000 /* major# is allocated */
+
+/*
* Character device switch table
*/
struct cdevsw {
- int d_version;
- int d_maj;
- u_int d_flags;
- const char *d_name;
- d_open_t *d_open;
- d_fdopen_t *d_fdopen;
- d_close_t *d_close;
- d_read_t *d_read;
- d_write_t *d_write;
- d_ioctl_t *d_ioctl;
- d_poll_t *d_poll;
- d_mmap_t *d_mmap;
- d_strategy_t *d_strategy;
- dumper_t *d_dump;
- d_kqfilter_t *d_kqfilter;
+ int d_version;
+ int d_maj;
+ u_int d_flags;
+ const char *d_name;
+ d_open_t *d_open;
+ d_fdopen_t *d_fdopen;
+ d_close_t *d_close;
+ d_read_t *d_read;
+ d_write_t *d_write;
+ d_ioctl_t *d_ioctl;
+ d_poll_t *d_poll;
+ d_mmap_t *d_mmap;
+ d_strategy_t *d_strategy;
+ dumper_t *d_dump;
+ d_kqfilter_t *d_kqfilter;
+
+ /* These fields should not be messed with by drivers */
+ LIST_ENTRY(cdevsw) d_list;
+ LIST_HEAD(, cdev) d_devs;
+ int d_refcount;
};
/*
@@ -293,11 +305,14 @@ int clone_create(struct clonedevs **, struct cdevsw *, int *unit, dev_t *dev, u_
int count_dev(dev_t _dev);
void destroy_dev(dev_t _dev);
struct cdevsw *devsw(dev_t _dev);
+void cdevsw_ref(struct cdevsw *);
+void cdevsw_rel(struct cdevsw *);
const char *devtoname(dev_t _dev);
int dev_named(dev_t _pdev, const char *_name);
void dev_depends(dev_t _pdev, dev_t _cdev);
+void dev_ref(dev_t dev);
+void dev_rel(dev_t dev);
void dev_strategy(struct buf *bp);
-void freedev(dev_t _dev);
dev_t makebdev(int _maj, int _min);
dev_t make_dev(struct cdevsw *_devsw, int _minor, uid_t _uid, gid_t _gid,
int _perms, const char *_fmt, ...) __printflike(6, 7);