Index: stable/6/sys/kern/kern_conf.c =================================================================== --- stable/6/sys/kern/kern_conf.c (revision 167067) +++ stable/6/sys/kern/kern_conf.c (revision 167068) @@ -1,902 +1,902 @@ /*- * Copyright (c) 1999-2002 Poul-Henning Kamp * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_DEVT, "cdev", "cdev storage"); struct mtx devmtx; static void destroy_devl(struct cdev *dev); static struct cdev *make_dev_credv(struct cdevsw *devsw, int minornr, struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, va_list ap); void dev_lock(void) { mtx_lock(&devmtx); } void dev_unlock(void) { mtx_unlock(&devmtx); } void dev_ref(struct cdev *dev) { mtx_assert(&devmtx, MA_NOTOWNED); mtx_lock(&devmtx); dev->si_refcount++; mtx_unlock(&devmtx); } void dev_refl(struct cdev *dev) { mtx_assert(&devmtx, MA_OWNED); dev->si_refcount++; } void dev_rel(struct cdev *dev) { int flag = 0; mtx_assert(&devmtx, MA_NOTOWNED); dev_lock(); dev->si_refcount--; KASSERT(dev->si_refcount >= 0, ("dev_rel(%s) gave negative count", devtoname(dev))); #if 0 if (dev->si_usecount == 0 && (dev->si_flags & SI_CHEAPCLONE) && (dev->si_flags & SI_NAMED)) ; else #endif if (dev->si_devsw == NULL && dev->si_refcount == 0) { LIST_REMOVE(dev, si_list); flag = 1; } dev_unlock(); if (flag) devfs_free(dev); } struct cdevsw * dev_refthread(struct cdev *dev) { struct cdevsw *csw; mtx_assert(&devmtx, MA_NOTOWNED); dev_lock(); csw = dev->si_devsw; if (csw != NULL) dev->si_threadcount++; dev_unlock(); return (csw); } struct cdevsw * devvn_refthread(struct vnode *vp, struct cdev **devp) { struct cdevsw *csw; mtx_assert(&devmtx, MA_NOTOWNED); csw = NULL; dev_lock(); *devp = vp->v_rdev; if (*devp != NULL) { csw = (*devp)->si_devsw; if (csw != NULL) (*devp)->si_threadcount++; } dev_unlock(); return (csw); } void dev_relthread(struct cdev *dev) { mtx_assert(&devmtx, MA_NOTOWNED); dev_lock(); dev->si_threadcount--; dev_unlock(); } int nullop(void) { return (0); } int eopnotsupp(void) { return (EOPNOTSUPP); } static int enxio(void) { return (ENXIO); } static int enodev(void) { return (ENODEV); } /* Define a dead_cdevsw for use when devices leave unexpectedly. */ #define dead_open (d_open_t *)enxio #define dead_close (d_close_t *)enxio #define dead_read (d_read_t *)enxio #define dead_write (d_write_t *)enxio #define dead_ioctl (d_ioctl_t *)enxio #define dead_poll (d_poll_t *)enodev #define dead_mmap (d_mmap_t *)enodev static void dead_strategy(struct bio *bp) { biofinish(bp, NULL, ENXIO); } #define dead_dump (dumper_t *)enxio #define dead_kqfilter (d_kqfilter_t *)enxio static struct cdevsw dead_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, /* XXX: does dead_strategy need this ? */ .d_open = dead_open, .d_close = dead_close, .d_read = dead_read, .d_write = dead_write, .d_ioctl = dead_ioctl, .d_poll = dead_poll, .d_mmap = dead_mmap, .d_strategy = dead_strategy, .d_name = "dead", .d_dump = dead_dump, .d_kqfilter = dead_kqfilter }; /* Default methods if driver does not specify method */ #define null_open (d_open_t *)nullop #define null_close (d_close_t *)nullop #define no_read (d_read_t *)enodev #define no_write (d_write_t *)enodev #define no_ioctl (d_ioctl_t *)enodev #define no_mmap (d_mmap_t *)enodev #define no_kqfilter (d_kqfilter_t *)enodev static void no_strategy(struct bio *bp) { biofinish(bp, NULL, ENODEV); } static int no_poll(struct cdev *dev __unused, int events, struct thread *td __unused) { /* * Return true for read/write. If the user asked for something * special, return POLLNVAL, so that clients have a way of * determining reliably whether or not the extended * functionality is present without hard-coding knowledge * of specific filesystem implementations. * Stay in sync with vop_nopoll(). */ if (events & ~POLLSTANDARD) return (POLLNVAL); return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)); } #define no_dump (dumper_t *)enodev static int giant_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { int retval; mtx_lock(&Giant); retval = dev->si_devsw->d_gianttrick-> d_open(dev, oflags, devtype, td); mtx_unlock(&Giant); return (retval); } static int giant_fdopen(struct cdev *dev, int oflags, struct thread *td, int fdidx) { int retval; mtx_lock(&Giant); retval = dev->si_devsw->d_gianttrick-> d_fdopen(dev, oflags, td, fdidx); mtx_unlock(&Giant); return (retval); } static int giant_close(struct cdev *dev, int fflag, int devtype, struct thread *td) { int retval; mtx_lock(&Giant); retval = dev->si_devsw->d_gianttrick-> d_close(dev, fflag, devtype, td); mtx_unlock(&Giant); return (retval); } static void giant_strategy(struct bio *bp) { mtx_lock(&Giant); bp->bio_dev->si_devsw->d_gianttrick-> d_strategy(bp); mtx_unlock(&Giant); } static int giant_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { int retval; mtx_lock(&Giant); retval = dev->si_devsw->d_gianttrick-> d_ioctl(dev, cmd, data, fflag, td); mtx_unlock(&Giant); return (retval); } static int giant_read(struct cdev *dev, struct uio *uio, int ioflag) { int retval; mtx_lock(&Giant); retval = dev->si_devsw->d_gianttrick-> d_read(dev, uio, ioflag); mtx_unlock(&Giant); return (retval); } static int giant_write(struct cdev *dev, struct uio *uio, int ioflag) { int retval; mtx_lock(&Giant); retval = dev->si_devsw->d_gianttrick-> d_write(dev, uio, ioflag); mtx_unlock(&Giant); return (retval); } static int giant_poll(struct cdev *dev, int events, struct thread *td) { int retval; mtx_lock(&Giant); retval = dev->si_devsw->d_gianttrick-> d_poll(dev, events, td); mtx_unlock(&Giant); return (retval); } static int giant_kqfilter(struct cdev *dev, struct knote *kn) { int retval; mtx_lock(&Giant); retval = dev->si_devsw->d_gianttrick-> d_kqfilter(dev, kn); mtx_unlock(&Giant); return (retval); } static int giant_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) { int retval; mtx_lock(&Giant); retval = dev->si_devsw->d_gianttrick-> d_mmap(dev, offset, paddr, nprot); mtx_unlock(&Giant); return (retval); } /* * struct cdev * and u_dev_t primitives */ int minor(struct cdev *x) { if (x == NULL) return NODEV; return(x->si_drv0 & MAXMINOR); } int dev2unit(struct cdev *x) { if (x == NULL) return NODEV; return (minor2unit(minor(x))); } u_int minor2unit(u_int _minor) { KASSERT((_minor & ~MAXMINOR) == 0, ("Illegal minor %x", _minor)); return ((_minor & 0xff) | ((_minor >> 8) & 0xffff00)); } int unit2minor(int unit) { KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit)); return ((unit & 0xff) | ((unit << 8) & ~0xffff)); } static struct cdev * newdev(struct cdevsw *csw, int y, struct cdev *si) { struct cdev *si2; dev_t udev; mtx_assert(&devmtx, MA_OWNED); udev = y; LIST_FOREACH(si2, &csw->d_devs, si_list) { if (si2->si_drv0 == udev) { devfs_free(si); return (si2); } } si->si_drv0 = udev; si->si_devsw = csw; LIST_INSERT_HEAD(&csw->d_devs, si, si_list); return (si); } int uminor(dev_t dev) { return (dev & MAXMINOR); } int umajor(dev_t dev) { return ((dev & ~MAXMINOR) >> 8); } static void fini_cdevsw(struct cdevsw *devsw) { struct cdevsw *gt; if (devsw->d_gianttrick != NULL) { gt = devsw->d_gianttrick; memcpy(devsw, gt, sizeof *devsw); free(gt, M_DEVT); devsw->d_gianttrick = NULL; } devsw->d_flags &= ~D_INIT; } static void prep_cdevsw(struct cdevsw *devsw) { struct cdevsw *dsw2; if (devsw->d_flags & D_NEEDGIANT) dsw2 = malloc(sizeof *dsw2, M_DEVT, M_WAITOK); else dsw2 = NULL; dev_lock(); if (devsw->d_version != D_VERSION_01) { printf( "WARNING: Device driver \"%s\" has wrong version %s\n", devsw->d_name == NULL ? "???" : 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_ioctl == NULL) devsw->d_ioctl = ttyioctl; if (devsw->d_read == NULL) devsw->d_read = ttyread; if (devsw->d_write == NULL) devsw->d_write = ttywrite; if (devsw->d_kqfilter == NULL) devsw->d_kqfilter = ttykqfilter; if (devsw->d_poll == NULL) devsw->d_poll = ttypoll; } if (devsw->d_flags & D_NEEDGIANT) { if (devsw->d_gianttrick == NULL) { memcpy(dsw2, devsw, sizeof *dsw2); devsw->d_gianttrick = dsw2; } else free(dsw2, M_DEVT); } #define FIXUP(member, noop, giant) \ do { \ if (devsw->member == NULL) { \ devsw->member = noop; \ } else if (devsw->d_flags & D_NEEDGIANT) \ devsw->member = giant; \ } \ while (0) FIXUP(d_open, null_open, giant_open); FIXUP(d_fdopen, NULL, giant_fdopen); FIXUP(d_close, null_close, giant_close); FIXUP(d_read, no_read, giant_read); FIXUP(d_write, no_write, giant_write); FIXUP(d_ioctl, no_ioctl, giant_ioctl); FIXUP(d_poll, no_poll, giant_poll); FIXUP(d_mmap, no_mmap, giant_mmap); FIXUP(d_strategy, no_strategy, giant_strategy); FIXUP(d_kqfilter, no_kqfilter, giant_kqfilter); if (devsw->d_dump == NULL) devsw->d_dump = no_dump; LIST_INIT(&devsw->d_devs); devsw->d_flags |= D_INIT; dev_unlock(); } static struct cdev * make_dev_credv(struct cdevsw *devsw, int minornr, struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, va_list ap) { struct cdev *dev; int i; KASSERT((minornr & ~MAXMINOR) == 0, ("Invalid minor (0x%x) in make_dev", minornr)); if (!(devsw->d_flags & D_INIT)) prep_cdevsw(devsw); dev = devfs_alloc(); dev_lock(); dev = newdev(devsw, minornr, dev); if (dev->si_flags & SI_CHEAPCLONE && dev->si_flags & SI_NAMED) { /* * This is allowed as it removes races and generally * simplifies cloning devices. * XXX: still ?? */ dev_unlock(); return (dev); } KASSERT(!(dev->si_flags & SI_NAMED), ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)", devsw->d_name, minor(dev), devtoname(dev))); i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap); if (i > (sizeof dev->__si_namebuf - 1)) { printf("WARNING: Device name truncated! (%s)\n", dev->__si_namebuf); } dev->si_flags |= SI_NAMED; if (cr != NULL) dev->si_cred = crhold(cr); else dev->si_cred = NULL; dev->si_uid = uid; dev->si_gid = gid; dev->si_mode = mode; devfs_create(dev); dev_unlock(); return (dev); } struct cdev * make_dev(struct cdevsw *devsw, int minornr, uid_t uid, gid_t gid, int mode, const char *fmt, ...) { struct cdev *dev; va_list ap; va_start(ap, fmt); dev = make_dev_credv(devsw, minornr, NULL, uid, gid, mode, fmt, ap); va_end(ap); return (dev); } struct cdev * make_dev_cred(struct cdevsw *devsw, int minornr, struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, ...) { struct cdev *dev; va_list ap; va_start(ap, fmt); dev = make_dev_credv(devsw, minornr, cr, uid, gid, mode, fmt, ap); va_end(ap); return (dev); } static void dev_dependsl(struct cdev *pdev, struct cdev *cdev) { cdev->si_parent = pdev; cdev->si_flags |= SI_CHILD; LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings); } void dev_depends(struct cdev *pdev, struct cdev *cdev) { dev_lock(); dev_dependsl(pdev, cdev); dev_unlock(); } struct cdev * make_dev_alias(struct cdev *pdev, const char *fmt, ...) { struct cdev *dev; va_list ap; int i; dev = devfs_alloc(); dev_lock(); dev->si_flags |= SI_ALIAS; dev->si_flags |= SI_NAMED; va_start(ap, fmt); i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap); if (i > (sizeof dev->__si_namebuf - 1)) { printf("WARNING: Device name truncated! (%s)\n", dev->__si_namebuf); } va_end(ap); devfs_create(dev); dev_unlock(); dev_depends(pdev, dev); return (dev); } static void destroy_devl(struct cdev *dev) { struct cdevsw *csw; mtx_assert(&devmtx, MA_OWNED); KASSERT(dev->si_flags & SI_NAMED, ("WARNING: Driver mistake: destroy_dev on %d\n", minor(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_devl(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; } csw = dev->si_devsw; dev->si_devsw = NULL; /* already NULL for SI_ALIAS */ while (csw != NULL && csw->d_purge != NULL && dev->si_threadcount) { printf("Purging %lu threads from %s\n", dev->si_threadcount, devtoname(dev)); csw->d_purge(dev); msleep(csw, &devmtx, PRIBIO, "devprg", hz/10); } if (csw != NULL && csw->d_purge != NULL) printf("All threads purged from %s\n", devtoname(dev)); dev->si_drv1 = 0; dev->si_drv2 = 0; bzero(&dev->__si_u, sizeof(dev->__si_u)); if (!(dev->si_flags & SI_ALIAS)) { /* Remove from cdevsw list */ LIST_REMOVE(dev, si_list); /* If cdevsw has no more struct cdev *'s, clean it */ if (LIST_EMPTY(&csw->d_devs)) fini_cdevsw(csw); } dev->si_flags &= ~SI_ALIAS; if (dev->si_refcount > 0) { LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list); } else { devfs_free(dev); } } void destroy_dev(struct cdev *dev) { dev_lock(); destroy_devl(dev); dev_unlock(); } const char * devtoname(struct cdev *dev) { char *p; struct cdevsw *csw; int mynor; if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') { p = dev->si_name; csw = dev_refthread(dev); if (csw != NULL) { sprintf(p, "(%s)", csw->d_name); dev_relthread(dev); } p += strlen(p); mynor = minor(dev); if (mynor < 0 || mynor > 255) sprintf(p, "/%#x", (u_int)mynor); else sprintf(p, "/%d", mynor); } return (dev->si_name); } int dev_stdclone(char *name, char **namep, const char *stem, int *unit) { int u, i; i = strlen(stem); if (bcmp(stem, name, i) != 0) return (0); if (!isdigit(name[i])) return (0); u = 0; if (name[i] == '0' && isdigit(name[i+1])) return (0); while (isdigit(name[i])) { u *= 10; u += name[i++] - '0'; } if (u > 0xffffff) return (0); *unit = u; if (namep) *namep = &name[i]; if (name[i]) return (2); return (1); } /* * Helper functions for cloning device drivers. * * The objective here is to make it unnecessary for the device drivers to * use rman or similar to manage their unit number space. Due to the way * we do "on-demand" devices, using rman or other "private" methods * will be very tricky to lock down properly once we lock down this file. * * Instead we give the drivers these routines which puts the struct cdev *'s * that are to be managed on their own list, and gives the driver the ability * to ask for the first free unit number or a given specified unit number. * * In addition these routines support paired devices (pty, nmdm and similar) * by respecting a number of "flag" bits in the minor number. * */ struct clonedevs { LIST_HEAD(,cdev) head; }; void clone_setup(struct clonedevs **cdp) { *cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO); LIST_INIT(&(*cdp)->head); } int -clone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev **dp, u_int extra) +clone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev **dp, int extra) { struct clonedevs *cd; struct cdev *dev, *ndev, *dl, *de; int unit, low, u; KASSERT(*cdp != NULL, ("clone_setup() not called in driver \"%s\"", csw->d_name)); KASSERT(!(extra & CLONE_UNITMASK), ("Illegal extra bits (0x%x) in clone_create", extra)); KASSERT(*up <= CLONE_UNITMASK, ("Too high unit (0x%x) in clone_create", *up)); if (!(csw->d_flags & D_INIT)) prep_cdevsw(csw); /* * Search the list for a lot of things in one go: * A preexisting match is returned immediately. * The lowest free unit number if we are passed -1, and the place * in the list where we should insert that new element. * The place to insert a specified unit number, if applicable * the end of the list. */ unit = *up; ndev = devfs_alloc(); dev_lock(); low = extra; de = dl = NULL; cd = *cdp; LIST_FOREACH(dev, &cd->head, si_clone) { KASSERT(dev->si_flags & SI_CLONELIST, ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); u = dev2unit(dev); if (u == (unit | extra)) { *dp = dev; devfs_free(ndev); dev_unlock(); return (0); } if (unit == -1 && u == low) { low++; de = dev; continue; } else if (u < (unit | extra)) { de = dev; continue; } else if (u > (unit | extra)) { dl = dev; break; } } if (unit == -1) unit = low & CLONE_UNITMASK; dev = newdev(csw, unit2minor(unit | extra), ndev); if (dev->si_flags & SI_CLONELIST) { printf("dev %p (%s) is on clonelist\n", dev, dev->si_name); printf("unit=%d, low=%d, extra=0x%x\n", unit, low, extra); LIST_FOREACH(dev, &cd->head, si_clone) { printf("\t%p %s\n", dev, dev->si_name); } panic("foo"); } KASSERT(!(dev->si_flags & SI_CLONELIST), ("Dev %p(%s) should not be on clonelist", dev, dev->si_name)); if (dl != NULL) LIST_INSERT_BEFORE(dl, dev, si_clone); else if (de != NULL) LIST_INSERT_AFTER(de, dev, si_clone); else LIST_INSERT_HEAD(&cd->head, dev, si_clone); dev->si_flags |= SI_CLONELIST; *up = unit; dev_unlock(); return (1); } /* * Kill everything still on the list. The driver should already have * disposed of any softc hung of the struct cdev *'s at this time. */ void clone_cleanup(struct clonedevs **cdp) { struct cdev *dev, *tdev; struct clonedevs *cd; cd = *cdp; if (cd == NULL) return; dev_lock(); LIST_FOREACH_SAFE(dev, &cd->head, si_clone, tdev) { KASSERT(dev->si_flags & SI_CLONELIST, ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); KASSERT(dev->si_flags & SI_NAMED, ("Driver has goofed in cloning underways udev %x", dev->si_drv0)); destroy_devl(dev); } dev_unlock(); free(cd, M_DEVBUF); *cdp = NULL; } Index: stable/6/sys/net/if_tap.c =================================================================== --- stable/6/sys/net/if_tap.c (revision 167067) +++ stable/6/sys/net/if_tap.c (revision 167068) @@ -1,994 +1,994 @@ /*- * Copyright (C) 1999-2000 by Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * BASED ON: * ------------------------------------------------------------------------- * * Copyright (c) 1988, Julian Onions * Nottingham University 1987. */ /* * $FreeBSD$ * $Id: if_tap.c,v 0.21 2000/07/23 21:46:02 max Exp $ */ #include "opt_inet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CDEV_NAME "tap" #define TAPDEBUG if (tapdebug) printf #define TAP "tap" #define VMNET "vmnet" #define TAPMAXUNIT 0x7fff #define VMNET_DEV_MASK CLONE_FLAG0 /* module */ static int tapmodevent(module_t, int, void *); /* device */ static void tapclone(void *, struct ucred *, char *, int, struct cdev **); static void tapcreate(struct cdev *); /* network interface */ static void tapifstart(struct ifnet *); static int tapifioctl(struct ifnet *, u_long, caddr_t); static void tapifinit(void *); /* character device */ static d_open_t tapopen; static d_close_t tapclose; static d_read_t tapread; static d_write_t tapwrite; static d_ioctl_t tapioctl; static d_poll_t tappoll; static d_kqfilter_t tapkqfilter; /* kqueue(2) */ static int tapkqread(struct knote *, long); static int tapkqwrite(struct knote *, long); static void tapkqdetach(struct knote *); static struct filterops tap_read_filterops = { .f_isfd = 1, .f_attach = NULL, .f_detach = tapkqdetach, .f_event = tapkqread, }; static struct filterops tap_write_filterops = { .f_isfd = 1, .f_attach = NULL, .f_detach = tapkqdetach, .f_event = tapkqwrite, }; static struct cdevsw tap_cdevsw = { .d_version = D_VERSION, .d_flags = D_PSEUDO | D_NEEDGIANT, .d_open = tapopen, .d_close = tapclose, .d_read = tapread, .d_write = tapwrite, .d_ioctl = tapioctl, .d_poll = tappoll, .d_name = CDEV_NAME, .d_kqfilter = tapkqfilter, }; /* * All global variables in if_tap.c are locked with tapmtx, with the * exception of tapdebug, which is accessed unlocked; tapclones is * static at runtime. */ static struct mtx tapmtx; static int tapdebug = 0; /* debug flag */ static int tapuopen = 0; /* allow user open() */ static SLIST_HEAD(, tap_softc) taphead; /* first device */ static struct clonedevs *tapclones; MALLOC_DECLARE(M_TAP); MALLOC_DEFINE(M_TAP, CDEV_NAME, "Ethernet tunnel interface"); SYSCTL_INT(_debug, OID_AUTO, if_tap_debug, CTLFLAG_RW, &tapdebug, 0, ""); SYSCTL_DECL(_net_link); SYSCTL_NODE(_net_link, OID_AUTO, tap, CTLFLAG_RW, 0, "Ethernet tunnel software network interface"); SYSCTL_INT(_net_link_tap, OID_AUTO, user_open, CTLFLAG_RW, &tapuopen, 0, "Allow user to open /dev/tap (based on node permissions)"); SYSCTL_INT(_net_link_tap, OID_AUTO, debug, CTLFLAG_RW, &tapdebug, 0, ""); DEV_MODULE(if_tap, tapmodevent, NULL); /* * tapmodevent * * module event handler */ static int tapmodevent(module_t mod, int type, void *data) { static eventhandler_tag eh_tag = NULL; struct tap_softc *tp = NULL; struct ifnet *ifp = NULL; int s; switch (type) { case MOD_LOAD: /* intitialize device */ mtx_init(&tapmtx, "tapmtx", NULL, MTX_DEF); SLIST_INIT(&taphead); clone_setup(&tapclones); eh_tag = EVENTHANDLER_REGISTER(dev_clone, tapclone, 0, 1000); if (eh_tag == NULL) { clone_cleanup(&tapclones); mtx_destroy(&tapmtx); return (ENOMEM); } return (0); case MOD_UNLOAD: /* * The EBUSY algorithm here can't quite atomically * guarantee that this is race-free since we have to * release the tap mtx to deregister the clone handler. */ mtx_lock(&tapmtx); SLIST_FOREACH(tp, &taphead, tap_next) { mtx_lock(&tp->tap_mtx); if (tp->tap_flags & TAP_OPEN) { mtx_unlock(&tp->tap_mtx); mtx_unlock(&tapmtx); return (EBUSY); } mtx_unlock(&tp->tap_mtx); } mtx_unlock(&tapmtx); EVENTHANDLER_DEREGISTER(dev_clone, eh_tag); mtx_lock(&tapmtx); while ((tp = SLIST_FIRST(&taphead)) != NULL) { SLIST_REMOVE_HEAD(&taphead, tap_next); mtx_unlock(&tapmtx); ifp = tp->tap_ifp; TAPDEBUG("detaching %s\n", ifp->if_xname); /* Unlocked read. */ KASSERT(!(tp->tap_flags & TAP_OPEN), ("%s flags is out of sync", ifp->if_xname)); knlist_destroy(&tp->tap_rsel.si_note); destroy_dev(tp->tap_dev); s = splimp(); ether_ifdetach(ifp); if_free_type(ifp, IFT_ETHER); splx(s); mtx_destroy(&tp->tap_mtx); free(tp, M_TAP); mtx_lock(&tapmtx); } mtx_unlock(&tapmtx); clone_cleanup(&tapclones); mtx_destroy(&tapmtx); break; default: return (EOPNOTSUPP); } return (0); } /* tapmodevent */ /* * DEVFS handler * * We need to support two kind of devices - tap and vmnet */ static void tapclone(void *arg, struct ucred *cred, char *name, int namelen, struct cdev **dev) { - u_int extra; + int extra; int i, unit; char *device_name = name; if (*dev != NULL) return; device_name = TAP; extra = 0; if (strcmp(name, TAP) == 0) { unit = -1; } else if (strcmp(name, VMNET) == 0) { device_name = VMNET; extra = VMNET_DEV_MASK; unit = -1; } else if (dev_stdclone(name, NULL, device_name, &unit) != 1) { device_name = VMNET; extra = VMNET_DEV_MASK; if (dev_stdclone(name, NULL, device_name, &unit) != 1) return; } /* find any existing device, or allocate new unit number */ i = clone_create(&tapclones, &tap_cdevsw, &unit, dev, extra); if (i) { *dev = make_dev(&tap_cdevsw, unit2minor(unit | extra), UID_ROOT, GID_WHEEL, 0600, "%s%d", device_name, unit); if (*dev != NULL) { dev_ref(*dev); (*dev)->si_flags |= SI_CHEAPCLONE; } } } /* tapclone */ /* * tapcreate * * to create interface */ static void tapcreate(struct cdev *dev) { struct ifnet *ifp = NULL; struct tap_softc *tp = NULL; unsigned short macaddr_hi; int unit, s; char *name = NULL; u_char eaddr[6]; dev->si_flags &= ~SI_CHEAPCLONE; /* allocate driver storage and create device */ MALLOC(tp, struct tap_softc *, sizeof(*tp), M_TAP, M_WAITOK | M_ZERO); mtx_init(&tp->tap_mtx, "tap_mtx", NULL, MTX_DEF); mtx_lock(&tapmtx); SLIST_INSERT_HEAD(&taphead, tp, tap_next); mtx_unlock(&tapmtx); unit = dev2unit(dev); /* select device: tap or vmnet */ if (unit & VMNET_DEV_MASK) { name = VMNET; tp->tap_flags |= TAP_VMNET; } else name = TAP; unit &= TAPMAXUNIT; TAPDEBUG("tapcreate(%s%d). minor = %#x\n", name, unit, minor(dev)); /* generate fake MAC address: 00 bd xx xx xx unit_no */ macaddr_hi = htons(0x00bd); bcopy(&macaddr_hi, eaddr, sizeof(short)); bcopy(&ticks, &eaddr[2], sizeof(long)); eaddr[5] = (u_char)unit; /* fill the rest and attach interface */ ifp = tp->tap_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) panic("%s%d: can not if_alloc()", name, unit); ifp->if_softc = tp; if_initname(ifp, name, unit); ifp->if_init = tapifinit; ifp->if_start = tapifstart; ifp->if_ioctl = tapifioctl; ifp->if_mtu = ETHERMTU; ifp->if_flags = (IFF_BROADCAST|IFF_SIMPLEX|IFF_MULTICAST); ifp->if_snd.ifq_maxlen = ifqmaxlen; dev->si_drv1 = tp; tp->tap_dev = dev; s = splimp(); ether_ifattach(ifp, eaddr); splx(s); mtx_lock(&tp->tap_mtx); tp->tap_flags |= TAP_INITED; mtx_unlock(&tp->tap_mtx); knlist_init(&tp->tap_rsel.si_note, NULL, NULL, NULL, NULL); TAPDEBUG("interface %s is created. minor = %#x\n", ifp->if_xname, minor(dev)); } /* tapcreate */ /* * tapopen * * to open tunnel. must be superuser */ static int tapopen(struct cdev *dev, int flag, int mode, struct thread *td) { struct tap_softc *tp = NULL; struct ifnet *ifp = NULL; int s; if (tapuopen == 0 && suser(td) != 0) return (EPERM); if ((dev2unit(dev) & CLONE_UNITMASK) > TAPMAXUNIT) return (ENXIO); /* * XXXRW: Non-atomic test-and-set of si_drv1. Currently protected * by Giant, but the race actually exists under memory pressure as * well even when running with Giant, as malloc() may sleep. */ tp = dev->si_drv1; if (tp == NULL) { tapcreate(dev); tp = dev->si_drv1; } mtx_lock(&tp->tap_mtx); if (tp->tap_flags & TAP_OPEN) { mtx_unlock(&tp->tap_mtx); return (EBUSY); } bcopy(IFP2ENADDR(tp->tap_ifp), tp->ether_addr, sizeof(tp->ether_addr)); tp->tap_pid = td->td_proc->p_pid; tp->tap_flags |= TAP_OPEN; ifp = tp->tap_ifp; mtx_unlock(&tp->tap_mtx); s = splimp(); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; splx(s); TAPDEBUG("%s is open. minor = %#x\n", ifp->if_xname, minor(dev)); return (0); } /* tapopen */ /* * tapclose * * close the device - mark i/f down & delete routing info */ static int tapclose(struct cdev *dev, int foo, int bar, struct thread *td) { struct ifaddr *ifa; struct tap_softc *tp = dev->si_drv1; struct ifnet *ifp = tp->tap_ifp; int s; /* junk all pending output */ IF_DRAIN(&ifp->if_snd); /* * do not bring the interface down, and do not anything with * interface, if we are in VMnet mode. just close the device. */ mtx_lock(&tp->tap_mtx); if (((tp->tap_flags & TAP_VMNET) == 0) && (ifp->if_flags & IFF_UP)) { mtx_unlock(&tp->tap_mtx); s = splimp(); if_down(ifp); if (ifp->if_drv_flags & IFF_DRV_RUNNING) { TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { rtinit(ifa, (int)RTM_DELETE, 0); } if_purgeaddrs(ifp); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } splx(s); } else mtx_unlock(&tp->tap_mtx); funsetown(&tp->tap_sigio); selwakeuppri(&tp->tap_rsel, PZERO+1); KNOTE_UNLOCKED(&tp->tap_rsel.si_note, 0); mtx_lock(&tp->tap_mtx); tp->tap_flags &= ~TAP_OPEN; tp->tap_pid = 0; mtx_unlock(&tp->tap_mtx); TAPDEBUG("%s is closed. minor = %#x\n", ifp->if_xname, minor(dev)); return (0); } /* tapclose */ /* * tapifinit * * network interface initialization function */ static void tapifinit(void *xtp) { struct tap_softc *tp = (struct tap_softc *)xtp; struct ifnet *ifp = tp->tap_ifp; TAPDEBUG("initializing %s\n", ifp->if_xname); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; /* attempt to start output */ tapifstart(ifp); } /* tapifinit */ /* * tapifioctl * * Process an ioctl request on network interface */ static int tapifioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct tap_softc *tp = (struct tap_softc *)(ifp->if_softc); struct ifstat *ifs = NULL; int s, dummy; switch (cmd) { case SIOCSIFFLAGS: /* XXX -- just like vmnet does */ case SIOCADDMULTI: case SIOCDELMULTI: break; case SIOCGIFSTATUS: s = splimp(); ifs = (struct ifstat *)data; dummy = strlen(ifs->ascii); mtx_lock(&tp->tap_mtx); if (tp->tap_pid != 0 && dummy < sizeof(ifs->ascii)) snprintf(ifs->ascii + dummy, sizeof(ifs->ascii) - dummy, "\tOpened by PID %d\n", tp->tap_pid); mtx_unlock(&tp->tap_mtx); splx(s); break; default: s = splimp(); dummy = ether_ioctl(ifp, cmd, data); splx(s); return (dummy); /* NOT REACHED */ } return (0); } /* tapifioctl */ /* * tapifstart * * queue packets from higher level ready to put out */ static void tapifstart(struct ifnet *ifp) { struct tap_softc *tp = ifp->if_softc; int s; TAPDEBUG("%s starting\n", ifp->if_xname); /* * do not junk pending output if we are in VMnet mode. * XXX: can this do any harm because of queue overflow? */ mtx_lock(&tp->tap_mtx); if (((tp->tap_flags & TAP_VMNET) == 0) && ((tp->tap_flags & TAP_READY) != TAP_READY)) { struct mbuf *m = NULL; mtx_unlock(&tp->tap_mtx); /* Unlocked read. */ TAPDEBUG("%s not ready, tap_flags = 0x%x\n", ifp->if_xname, tp->tap_flags); s = splimp(); do { IF_DEQUEUE(&ifp->if_snd, m); if (m != NULL) m_freem(m); ifp->if_oerrors ++; } while (m != NULL); splx(s); return; } mtx_unlock(&tp->tap_mtx); s = splimp(); ifp->if_drv_flags |= IFF_DRV_OACTIVE; if (ifp->if_snd.ifq_len != 0) { mtx_lock(&tp->tap_mtx); if (tp->tap_flags & TAP_RWAIT) { tp->tap_flags &= ~TAP_RWAIT; wakeup(tp); } if ((tp->tap_flags & TAP_ASYNC) && (tp->tap_sigio != NULL)) { mtx_unlock(&tp->tap_mtx); pgsigio(&tp->tap_sigio, SIGIO, 0); } else mtx_unlock(&tp->tap_mtx); selwakeuppri(&tp->tap_rsel, PZERO+1); KNOTE_UNLOCKED(&tp->tap_rsel.si_note, 0); ifp->if_opackets ++; /* obytes are counted in ether_output */ } ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; splx(s); } /* tapifstart */ /* * tapioctl * * the cdevsw interface is now pretty minimal */ static int tapioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) { struct tap_softc *tp = dev->si_drv1; struct ifnet *ifp = tp->tap_ifp; struct tapinfo *tapp = NULL; int s; int f; int ival; switch (cmd) { case TAPSIFINFO: s = splimp(); tapp = (struct tapinfo *)data; ifp->if_mtu = tapp->mtu; ifp->if_type = tapp->type; ifp->if_baudrate = tapp->baudrate; splx(s); break; case TAPGIFINFO: tapp = (struct tapinfo *)data; tapp->mtu = ifp->if_mtu; tapp->type = ifp->if_type; tapp->baudrate = ifp->if_baudrate; break; case TAPSDEBUG: tapdebug = *(int *)data; break; case TAPGDEBUG: *(int *)data = tapdebug; break; case FIONBIO: break; case FIOASYNC: s = splimp(); mtx_lock(&tp->tap_mtx); if (*(int *)data) tp->tap_flags |= TAP_ASYNC; else tp->tap_flags &= ~TAP_ASYNC; mtx_unlock(&tp->tap_mtx); splx(s); break; case FIONREAD: s = splimp(); if (ifp->if_snd.ifq_head) { struct mbuf *mb = ifp->if_snd.ifq_head; for(*(int *)data = 0;mb != NULL;mb = mb->m_next) *(int *)data += mb->m_len; } else *(int *)data = 0; splx(s); break; case FIOSETOWN: return (fsetown(*(int *)data, &tp->tap_sigio)); case FIOGETOWN: *(int *)data = fgetown(&tp->tap_sigio); return (0); /* this is deprecated, FIOSETOWN should be used instead */ case TIOCSPGRP: return (fsetown(-(*(int *)data), &tp->tap_sigio)); /* this is deprecated, FIOGETOWN should be used instead */ case TIOCGPGRP: *(int *)data = -fgetown(&tp->tap_sigio); return (0); /* VMware/VMnet port ioctl's */ case SIOCGIFFLAGS: /* get ifnet flags */ bcopy(&ifp->if_flags, data, sizeof(ifp->if_flags)); break; case _IO('V', 0): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ case VMIO_SIOCSIFFLAGS: /* VMware/VMnet SIOCSIFFLAGS */ f = *(int *)data; f &= 0x0fff; f &= ~IFF_CANTCHANGE; f |= IFF_UP; s = splimp(); ifp->if_flags = f | (ifp->if_flags & IFF_CANTCHANGE); splx(s); break; case OSIOCGIFADDR: /* get MAC address of the remote side */ case SIOCGIFADDR: mtx_lock(&tp->tap_mtx); bcopy(tp->ether_addr, data, sizeof(tp->ether_addr)); mtx_unlock(&tp->tap_mtx); break; case SIOCSIFADDR: /* set MAC address of the remote side */ mtx_lock(&tp->tap_mtx); bcopy(data, tp->ether_addr, sizeof(tp->ether_addr)); mtx_unlock(&tp->tap_mtx); break; default: return (ENOTTY); } return (0); } /* tapioctl */ /* * tapread * * the cdevsw read interface - reads a packet at a time, or at * least as much of a packet as can be read */ static int tapread(struct cdev *dev, struct uio *uio, int flag) { struct tap_softc *tp = dev->si_drv1; struct ifnet *ifp = tp->tap_ifp; struct mbuf *m = NULL; int error = 0, len, s; TAPDEBUG("%s reading, minor = %#x\n", ifp->if_xname, minor(dev)); mtx_lock(&tp->tap_mtx); if ((tp->tap_flags & TAP_READY) != TAP_READY) { mtx_unlock(&tp->tap_mtx); /* Unlocked read. */ TAPDEBUG("%s not ready. minor = %#x, tap_flags = 0x%x\n", ifp->if_xname, minor(dev), tp->tap_flags); return (EHOSTDOWN); } tp->tap_flags &= ~TAP_RWAIT; mtx_unlock(&tp->tap_mtx); /* sleep until we get a packet */ do { s = splimp(); IF_DEQUEUE(&ifp->if_snd, m); splx(s); if (m == NULL) { if (flag & O_NONBLOCK) return (EWOULDBLOCK); mtx_lock(&tp->tap_mtx); tp->tap_flags |= TAP_RWAIT; mtx_unlock(&tp->tap_mtx); error = tsleep(tp,PCATCH|(PZERO+1),"taprd",0); if (error) return (error); } } while (m == NULL); /* feed packet to bpf */ BPF_MTAP(ifp, m); /* xfer packet to user space */ while ((m != NULL) && (uio->uio_resid > 0) && (error == 0)) { len = min(uio->uio_resid, m->m_len); if (len == 0) break; error = uiomove(mtod(m, void *), len, uio); m = m_free(m); } if (m != NULL) { TAPDEBUG("%s dropping mbuf, minor = %#x\n", ifp->if_xname, minor(dev)); m_freem(m); } return (error); } /* tapread */ /* * tapwrite * * the cdevsw write interface - an atomic write is a packet - or else! */ static int tapwrite(struct cdev *dev, struct uio *uio, int flag) { struct ether_header *eh; struct tap_softc *tp = dev->si_drv1; struct ifnet *ifp = tp->tap_ifp; struct mbuf *m; TAPDEBUG("%s writting, minor = %#x\n", ifp->if_xname, minor(dev)); if (uio->uio_resid == 0) return (0); if ((uio->uio_resid < 0) || (uio->uio_resid > TAPMRU)) { TAPDEBUG("%s invalid packet len = %d, minor = %#x\n", ifp->if_xname, uio->uio_resid, minor(dev)); return (EIO); } if ((m = m_uiotombuf(uio, M_DONTWAIT, 0, ETHER_ALIGN)) == NULL) { ifp->if_ierrors ++; return (ENOBUFS); } m->m_pkthdr.rcvif = ifp; /* * Only pass a unicast frame to ether_input(), if it would actually * have been received by non-virtual hardware. */ if (m->m_len < sizeof(struct ether_header)) { m_freem(m); return (0); } eh = mtod(m, struct ether_header *); if (eh && (ifp->if_flags & IFF_PROMISC) == 0 && !ETHER_IS_MULTICAST(eh->ether_dhost) && bcmp(eh->ether_dhost, IFP2ENADDR(ifp), ETHER_ADDR_LEN) != 0) { m_freem(m); return (0); } /* Pass packet up to parent. */ (*ifp->if_input)(ifp, m); ifp->if_ipackets ++; /* ibytes are counted in parent */ return (0); } /* tapwrite */ /* * tappoll * * the poll interface, this is only useful on reads * really. the write detect always returns true, write never blocks * anyway, it either accepts the packet or drops it */ static int tappoll(struct cdev *dev, int events, struct thread *td) { struct tap_softc *tp = dev->si_drv1; struct ifnet *ifp = tp->tap_ifp; int s, revents = 0; TAPDEBUG("%s polling, minor = %#x\n", ifp->if_xname, minor(dev)); s = splimp(); if (events & (POLLIN | POLLRDNORM)) { if (ifp->if_snd.ifq_len > 0) { TAPDEBUG("%s have data in queue. len = %d, " \ "minor = %#x\n", ifp->if_xname, ifp->if_snd.ifq_len, minor(dev)); revents |= (events & (POLLIN | POLLRDNORM)); } else { TAPDEBUG("%s waiting for data, minor = %#x\n", ifp->if_xname, minor(dev)); selrecord(td, &tp->tap_rsel); } } if (events & (POLLOUT | POLLWRNORM)) revents |= (events & (POLLOUT | POLLWRNORM)); splx(s); return (revents); } /* tappoll */ /* * tap_kqfilter * * support for kevent() system call */ static int tapkqfilter(struct cdev *dev, struct knote *kn) { int s; struct tap_softc *tp = dev->si_drv1; struct ifnet *ifp = tp->tap_ifp; s = splimp(); switch (kn->kn_filter) { case EVFILT_READ: TAPDEBUG("%s kqfilter: EVFILT_READ, minor = %#x\n", ifp->if_xname, minor(dev)); kn->kn_fop = &tap_read_filterops; break; case EVFILT_WRITE: TAPDEBUG("%s kqfilter: EVFILT_WRITE, minor = %#x\n", ifp->if_xname, minor(dev)); kn->kn_fop = &tap_write_filterops; break; default: TAPDEBUG("%s kqfilter: invalid filter, minor = %#x\n", ifp->if_xname, minor(dev)); splx(s); return (EINVAL); /* NOT REACHED */ } splx(s); kn->kn_hook = (caddr_t) dev; knlist_add(&tp->tap_rsel.si_note, kn, 0); return (0); } /* tapkqfilter */ /* * tap_kqread * * Return true if there is data in the interface queue */ static int tapkqread(struct knote *kn, long hint) { int ret, s; struct cdev *dev = (struct cdev *)(kn->kn_hook); struct tap_softc *tp = dev->si_drv1; struct ifnet *ifp = tp->tap_ifp; s = splimp(); if ((kn->kn_data = ifp->if_snd.ifq_len) > 0) { TAPDEBUG("%s have data in queue. len = %d, minor = %#x\n", ifp->if_xname, ifp->if_snd.ifq_len, minor(dev)); ret = 1; } else { TAPDEBUG("%s waiting for data, minor = %#x\n", ifp->if_xname, minor(dev)); ret = 0; } splx(s); return (ret); } /* tapkqread */ /* * tap_kqwrite * * Always can write. Return the MTU in kn->data */ static int tapkqwrite(struct knote *kn, long hint) { int s; struct tap_softc *tp = ((struct cdev *) kn->kn_hook)->si_drv1; struct ifnet *ifp = tp->tap_ifp; s = splimp(); kn->kn_data = ifp->if_mtu; splx(s); return (1); } /* tapkqwrite */ static void tapkqdetach(struct knote *kn) { struct tap_softc *tp = ((struct cdev *) kn->kn_hook)->si_drv1; knlist_remove(&tp->tap_rsel.si_note, kn, 0); } /* tapkqdetach */ Index: stable/6/sys/sys/conf.h =================================================================== --- stable/6/sys/sys/conf.h (revision 167067) +++ stable/6/sys/sys/conf.h (revision 167068) @@ -1,311 +1,311 @@ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * Copyright (c) 2000 * Poul-Henning Kamp. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)conf.h 8.5 (Berkeley) 1/9/95 * $FreeBSD$ */ #ifndef _SYS_CONF_H_ #define _SYS_CONF_H_ #ifdef _KERNEL #include #else #include #endif struct tty; struct snapdata; struct devfs_dirent; struct cdevsw; struct cdev { struct cdev_priv *si_priv; u_int si_flags; #define SI_ALIAS 0x0002 /* carrier of alias name */ #define SI_NAMED 0x0004 /* make_dev{_alias} has been called */ #define SI_CHEAPCLONE 0x0008 /* can be removed_dev'ed when vnode reclaims */ #define SI_CHILD 0x0010 /* child of another struct cdev **/ #define SI_DEVOPEN 0x0020 /* opened by device */ #define SI_CONSOPEN 0x0040 /* opened by console */ #define SI_DUMPDEV 0x0080 /* is kernel dumpdev */ #define SI_CANDELETE 0x0100 /* can do BIO_DELETE */ #define SI_CLONELIST 0x0200 /* on a clone list */ struct timespec si_atime; struct timespec si_ctime; struct timespec si_mtime; uid_t si_uid; gid_t si_gid; mode_t si_mode; struct ucred *si_cred; /* cached clone-time credential */ u_int si_drv0; int si_refcount; LIST_ENTRY(cdev) si_list; LIST_ENTRY(cdev) si_clone; LIST_HEAD(, cdev) si_children; LIST_ENTRY(cdev) si_siblings; struct cdev *si_parent; char *si_name; void *si_drv1, *si_drv2; struct cdevsw *si_devsw; int si_iosize_max; /* maximum I/O size (for physio &al) */ u_long si_usecount; u_long si_threadcount; union { struct tty *__sit_tty; struct snapdata *__sid_snapdata; } __si_u; char __si_namebuf[SPECNAMELEN + 1]; }; #define si_tty __si_u.__sit_tty #define si_snapdata __si_u.__sid_snapdata #ifdef _KERNEL /* * Definitions of device driver entry switches */ struct bio; struct buf; struct thread; struct uio; struct knote; struct clonedevs; struct vnode; /* * Note: d_thread_t is provided as a transition aid for those drivers * that treat struct proc/struct thread as an opaque data type and * exist in substantially the same form in both 4.x and 5.x. Writers * of drivers that dips into the d_thread_t structure should use * struct thread or struct proc as appropriate for the version of the * OS they are using. It is provided in lieu of each device driver * inventing its own way of doing this. While it does violate style(9) * in a number of ways, this violation is deemed to be less * important than the benefits that a uniform API between releases * gives. * * Users of struct thread/struct proc that aren't device drivers should * not use d_thread_t. */ typedef struct thread d_thread_t; typedef int d_open_t(struct cdev *dev, int oflags, int devtype, struct thread *td); typedef int d_fdopen_t(struct cdev *dev, int oflags, struct thread *td, int fdidx); typedef int d_close_t(struct cdev *dev, int fflag, int devtype, struct thread *td); typedef void d_strategy_t(struct bio *bp); typedef int d_ioctl_t(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td); typedef int d_read_t(struct cdev *dev, struct uio *uio, int ioflag); typedef int d_write_t(struct cdev *dev, struct uio *uio, int ioflag); typedef int d_poll_t(struct cdev *dev, int events, struct thread *td); typedef int d_kqfilter_t(struct cdev *dev, struct knote *kn); typedef int d_mmap_t(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot); typedef void d_purge_t(struct cdev *dev); typedef int d_spare2_t(struct cdev *dev); typedef int dumper_t( void *priv, /* Private to the driver. */ void *virtual, /* Virtual (mapped) address. */ vm_offset_t physical, /* Physical address of virtual. */ off_t offset, /* Byte-offset to write at. */ size_t length); /* Number of bytes to dump. */ #endif /* _KERNEL */ /* * Types for d_flags. */ #define D_TAPE 0x0001 #define D_DISK 0x0002 #define D_TTY 0x0004 #define D_MEM 0x0008 #ifdef _KERNEL #define D_TYPEMASK 0xffff /* * Flags for d_flags which the drivers can set. */ #define D_MEMDISK 0x00010000 /* memory type disk */ #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 */ #define D_NEEDGIANT 0x00400000 /* driver want Giant */ /* * Version numbers. */ #define D_VERSION_00 0x20011966 #define D_VERSION_01 0x17032005 /* Add d_uid,gid,mode & kind */ #define D_VERSION D_VERSION_01 /* * Flags used for internal housekeeping */ #define D_INIT 0x80000000 /* cdevsw initialized */ /* * Character device switch table */ struct cdevsw { int d_version; 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; d_purge_t *d_purge; d_spare2_t *d_spare2; uid_t d_uid; gid_t d_gid; mode_t d_mode; const char *d_kind; /* These fields should not be messed with by drivers */ LIST_ENTRY(cdevsw) d_list; LIST_HEAD(, cdev) d_devs; int d_spare3; struct cdevsw *d_gianttrick; }; #define NUMCDEVSW 256 #define MAXMINOR 0xffff00ffU struct module; struct devsw_module_data { int (*chainevh)(struct module *, int, void *); /* next handler */ void *chainarg; /* arg for next event handler */ /* Do not initialize fields hereafter */ }; #define DEV_MODULE(name, evh, arg) \ static moduledata_t name##_mod = { \ #name, \ evh, \ arg \ }; \ DECLARE_MODULE(name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE) void clone_setup(struct clonedevs **cdp); void clone_cleanup(struct clonedevs **); #define CLONE_UNITMASK 0xfffff #define CLONE_FLAG0 (CLONE_UNITMASK + 1) -int clone_create(struct clonedevs **, struct cdevsw *, int *unit, struct cdev **dev, u_int extra); +int clone_create(struct clonedevs **, struct cdevsw *, int *unit, struct cdev **dev, int extra); int count_dev(struct cdev *_dev); void destroy_dev(struct cdev *_dev); struct cdevsw *dev_refthread(struct cdev *_dev); struct cdevsw *devvn_refthread(struct vnode *vp, struct cdev **devp); void dev_relthread(struct cdev *_dev); void dev_depends(struct cdev *_pdev, struct cdev *_cdev); void dev_ref(struct cdev *dev); void dev_refl(struct cdev *dev); void dev_rel(struct cdev *dev); void dev_strategy(struct cdev *dev, struct buf *bp); struct cdev *make_dev(struct cdevsw *_devsw, int _minor, uid_t _uid, gid_t _gid, int _perms, const char *_fmt, ...) __printflike(6, 7); struct cdev *make_dev_cred(struct cdevsw *_devsw, int _minor, struct ucred *_cr, uid_t _uid, gid_t _gid, int _perms, const char *_fmt, ...) __printflike(7, 8); struct cdev *make_dev_alias(struct cdev *_pdev, const char *_fmt, ...) __printflike(2, 3); int dev2unit(struct cdev *_dev); void dev_lock(void); void dev_unlock(void); int unit2minor(int _unit); u_int minor2unit(u_int _minor); void setconf(void); #define UID_ROOT 0 #define UID_BIN 3 #define UID_UUCP 66 #define GID_WHEEL 0 #define GID_KMEM 2 #define GID_OPERATOR 5 #define GID_BIN 7 #define GID_GAMES 13 #define GID_DIALER 68 typedef void (*dev_clone_fn)(void *arg, struct ucred *cred, char *name, int namelen, struct cdev **result); int dev_stdclone(char *_name, char **_namep, const char *_stem, int *_unit); EVENTHANDLER_DECLARE(dev_clone, dev_clone_fn); /* Stuff relating to kernel-dump */ struct dumperinfo { dumper_t *dumper; /* Dumping function. */ void *priv; /* Private parts. */ u_int blocksize; /* Size of block in bytes. */ off_t mediaoffset; /* Initial offset in bytes. */ off_t mediasize; /* Space available in bytes. */ }; int set_dumper(struct dumperinfo *); void dumpsys(struct dumperinfo *); extern int dumping; /* system is dumping */ /* D_TTY related functions */ d_close_t ttyclose; d_ioctl_t ttyioctl; d_kqfilter_t ttykqfilter; d_open_t ttyopen; d_poll_t ttypoll; d_read_t ttyread; d_write_t ttywrite; #endif /* _KERNEL */ #endif /* !_SYS_CONF_H_ */