From cd690b60dea90adc4ad499c6be171cd2b369d378 Mon Sep 17 00:00:00 2001 From: Poul-Henning Kamp Date: Sat, 21 Feb 2004 21:57:26 +0000 Subject: [PATCH] Device megapatch 6/6: This is what we came here for: Hang dev_t's from their cdevsw, refcount cdevsw and dev_t and generally keep track of things a lot better than we used to: Hold a cdevsw reference around all entrances into the device driver, this will be necessary to safely determine when we can unload driver code. Hold a dev_t reference while the device is open. KASSERT that we do not enter the driver on a non-referenced dev_t. Remove old D_NAG code, anonymous dev_t's are not a problem now. When destroy_dev() is called on a referenced dev_t, move it to dead_cdevsw's list. When the refcount drops, free it. Check that cdevsw->d_version is correct. If not, set all methods to the dead_*() methods to prevent entrance into driver. Print warning on console to this effect. The device driver may still explode if it is also incompatible with newbus, but in that case we probably didn't get this far in the first place. --- sys/fs/specfs/spec_vnops.c | 39 ++++++-- sys/kern/kern_conf.c | 183 ++++++++++++++++++++++++++++++------- sys/kern/vfs_bio.c | 7 ++ sys/kern/vfs_subr.c | 6 +- sys/sys/conf.h | 51 +++++++---- sys/sys/linedisc.h | 51 +++++++---- 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 */ @@ -210,25 +211,36 @@ typedef int dumper_t( #define D_VERSION_00 0x20011966 #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 */ @@ -210,25 +211,36 @@ typedef int dumper_t( #define D_VERSION_00 0x20011966 #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);