diff --git a/sys/fs/specfs/spec_vnops.c b/sys/fs/specfs/spec_vnops.c index 4682b5df619..ab9ce7f5a5b 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 bedbd1e48d4..1f8db46dd56 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 eafb9773704..e10d61e20a7 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 bcf7af86940..e43c78a2897 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 2a2b984f4e9..40ee4c346d5 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 2a2b984f4e9..40ee4c346d5 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);