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.
This commit is contained in:
Poul-Henning Kamp 2004-02-21 21:57:26 +00:00
parent 816d62bbb9
commit cd690b60de
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=126082
6 changed files with 259 additions and 78 deletions

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}
/*

View File

@ -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;
}

View File

@ -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);

View File

@ -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);