Date: Mon, 27 Feb 2006 11:31:05 GMT From: Vilmos Nebehaj <vili@huwico.hu> To: freebsd-gnats-submit@FreeBSD.org Subject: kern/93897: if_tap doesn't handle kqueue(2) Message-ID: <200602271131.k1RBV53Q003556@www.freebsd.org> Resent-Message-ID: <200602271140.k1RBe45d053345@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 93897 >Category: kern >Synopsis: if_tap doesn't handle kqueue(2) >Confidential: no >Severity: non-critical >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Mon Feb 27 11:40:04 GMT 2006 >Closed-Date: >Last-Modified: >Originator: Vilmos Nebehaj >Release: FreeBSD 6.0 >Organization: >Environment: oszoo# uname -a FreeBSD oszoo.oszoo 6.0-RELEASE FreeBSD 6.0-RELEASE #0: Thu Nov 3 09:36:13 UTC 2005 root@x64.samsco.home:/usr/obj/usr/src/sys/GENERIC i386 >Description: Kqueue(2) doesn't work on if_tap devices, so applications have to use select or poll. NetBSD and OpenBSD have had the necessary kqueue bits for quite a while, so it would be desirable for FreeBSD to do the same. >How-To-Repeat: Use kqueue/kevent on a file descriptor obtained from opening /dev/tapX. >Fix: I've prepared a patch. It cleanly applies against -CURRENT and RELENG_6_0 too. It can be fetched from http://innoidea.com/~vili/if_tap.diff Index: if_tap.c =================================================================== RCS file: /home/ncvs/src/sys/net/if_tap.c,v retrieving revision 1.58 diff -u -r1.58 if_tap.c --- if_tap.c 11 Nov 2005 16:04:48 -0000 1.58 +++ if_tap.c 27 Feb 2006 10:39:38 -0000 @@ -99,6 +99,17 @@ static d_ioctl_t tapioctl; static d_poll_t tappoll; +/* kqueue(2) */ +static int tap_kqfilter(struct cdev *, struct knote *); +static int tap_kqread(struct knote *, long); +static int tap_kqwrite(struct knote *, long); +static void tap_kqdetach(struct knote *); + +static struct filterops tap_read_filterops = { 1, NULL, tap_kqdetach, + tap_kqread }; +static struct filterops tap_write_filterops = { 1, NULL, tap_kqdetach, + tap_kqwrite }; + static struct cdevsw tap_cdevsw = { .d_version = D_VERSION, .d_flags = D_PSEUDO | D_NEEDGIANT, @@ -109,6 +120,7 @@ .d_ioctl = tapioctl, .d_poll = tappoll, .d_name = CDEV_NAME, + .d_kqfilter = tap_kqfilter, }; /* @@ -397,6 +409,8 @@ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; splx(s); + knlist_init(&tp->tap_rsel.si_note, NULL, NULL, NULL, NULL); + TAPDEBUG("%s is open. minor = %#x\n", ifp->if_xname, minor(dev)); return (0); @@ -446,12 +460,15 @@ funsetown(&tp->tap_sigio); selwakeuppri(&tp->tap_rsel, PZERO+1); + KNOTE(&tp->tap_rsel.si_note, 0, 0); mtx_lock(&tp->tap_mtx); tp->tap_flags &= ~TAP_OPEN; tp->tap_pid = 0; mtx_unlock(&tp->tap_mtx); + knlist_destroy(&tp->tap_rsel.si_note); + TAPDEBUG("%s is closed. minor = %#x\n", ifp->if_xname, minor(dev)); @@ -586,6 +603,7 @@ mtx_unlock(&tp->tap_mtx); selwakeuppri(&tp->tap_rsel, PZERO+1); + KNOTE(&tp->tap_rsel.si_note, 0, 0); ifp->if_opackets ++; /* obytes are counted in ether_output */ } @@ -878,3 +896,78 @@ splx(s); return (revents); } /* tappoll */ + +static int +tap_kqfilter(struct cdev *dev, struct knote *kn) +{ + struct tap_softc *tp = dev->si_drv1; + struct ifnet *ifp = tp->tap_ifp; + + 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)); + return EINVAL; + break; + } + + kn->kn_hook = (caddr_t)dev; + knlist_add(&tp->tap_rsel.si_note, kn, 0); + + return 0; +} + +/* Return true if there is data in the interface queue. */ +static int +tap_kqread(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; +} + +/* Always can write. Return the MTU in kn->data. */ +static int +tap_kqwrite(struct knote *kn, long hint) +{ + struct tap_softc *tp = ((struct cdev *)kn->kn_hook)->si_drv1; + struct ifnet *ifp = tp->tap_ifp; + + kn->kn_data = ifp->if_mtu; + + return 1; +} + +static void +tap_kqdetach(struct knote *kn) +{ + struct tap_softc *tp = ((struct cdev *)kn->kn_hook)->si_drv1; + + knlist_remove(&tp->tap_rsel.si_note, kn, 0); +} >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200602271131.k1RBV53Q003556>