Date: Mon, 24 Jul 2006 22:20:23 -0400 (EDT) From: David Gilbert <dgilbert@daveg.ca> To: FreeBSD-gnats-submit@FreeBSD.org Subject: kern/100796: if_tun requires kqueue hooks (patch included) Message-ID: <20060725022023.A54FB4AC45@canoe.dclg.ca> Resent-Message-ID: <200607250230.k6P2UF4R001622@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 100796 >Category: kern >Synopsis: if_tun requires kqueue hooks (patch included) >Confidential: no >Severity: serious >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Tue Jul 25 02:30:14 GMT 2006 >Closed-Date: >Last-Modified: >Originator: David Gilbert >Release: FreeBSD 6.1-STABLE i386 >Organization: DaveG.ca >Environment: System: FreeBSD canoe.dclg.ca 6.1-STABLE FreeBSD 6.1-STABLE #8: Mon Jul 24 17:57:45 EDT 2006 dgilbert@canoe.dclg.ca:/usr/src/sys/i386/compile/CANOE i386 These patches are against the 6.1-STABLE branch, but I believe that the patch should apply against the 7.0 branch (there don't appear to be a lot of changes). >Description: It appears that nobody put kqueue code into if_tun. Strangely, someone implemented kqueue for if_tap... and the implementation doesn't appear difficult. This patch is made with heavy reference to the code added to if_tap.c between 1.58 and 1.59. I'm not at all positive that I understood all the nuances, but the patch works (or appears to work). Someone might, however, want to give it a once over. >How-To-Repeat: Write some code that tries to use kqueue() with if_tun. >Fix: --- if_tun.c.orig Mon Jul 24 18:07:33 2006 +++ if_tun.c Mon Jul 24 20:34:37 2006 @@ -123,6 +123,26 @@ static d_write_t tunwrite; static d_ioctl_t tunioctl; static d_poll_t tunpoll; +static d_kqfilter_t tunkqfilter; + +/* kqueue(2) */ +static int tunkqread(struct knote *, long); +static int tunkqwrite(struct knote *, long); +static void tunkqdetach(struct knote *); + +static struct filterops tun_read_filterops = { + .f_isfd = 1, + .f_attach = NULL, + .f_detach = tunkqdetach, + .f_event = tunkqread, +}; + +static struct filterops tun_write_filterops = { + .f_isfd = 1, + .f_attach = NULL, + .f_detach = tunkqdetach, + .f_event = tunkqwrite, +}; static struct cdevsw tun_cdevsw = { .d_version = D_VERSION, @@ -134,6 +154,7 @@ .d_ioctl = tunioctl, .d_poll = tunpoll, .d_name = TUNNAME, + .d_kqfilter = tunkqfilter, }; static void @@ -188,7 +209,7 @@ { static eventhandler_tag tag; struct tun_softc *tp; - + switch (type) { case MOD_LOAD: mtx_init(&tunmtx, "tunmtx", NULL, MTX_DEF); @@ -202,7 +223,10 @@ mtx_lock(&tunmtx); while ((tp = TAILQ_FIRST(&tunhead)) != NULL) { + struct ifnet *ifp = TUN2IFP(tp); TAILQ_REMOVE(&tunhead, tp, tun_list); + TUNDEBUG(ifp,"Detaching %s\n", ifp->if_xname); + knlist_destroy(&tp->tun_rsel.si_note); mtx_unlock(&tunmtx); tun_destroy(tp); mtx_lock(&tunmtx); @@ -231,6 +255,8 @@ struct tun_softc *tp = ifp->if_softc; struct mbuf *m; + TUNDEBUG(ifp,"%s starting\n", ifp->if_xname); + if (ALTQ_IS_ENABLED(&ifp->if_snd)) { IFQ_LOCK(&ifp->if_snd); IFQ_POLL_NOLOCK(&ifp->if_snd, m); @@ -252,6 +278,7 @@ } else mtx_unlock(&tp->tun_mtx); selwakeuppri(&tp->tun_rsel, PZERO + 1); + KNOTE_UNLOCKED(&tp->tun_rsel.si_note, 0); } /* XXX: should return an error code so it can fail. */ @@ -289,6 +316,10 @@ if_attach(ifp); bpfattach(ifp, DLT_NULL, sizeof(u_int32_t)); dev->si_drv1 = sc; + + knlist_init(&sc->tun_rsel.si_note, NULL, NULL, NULL, NULL); + + TUNDEBUG(ifp, "interface %s is created, minor = %#x\n", ifp->if_xname, minor(dev)); } static int @@ -376,6 +407,7 @@ funsetown(&tp->tun_sigio); selwakeuppri(&tp->tun_rsel, PZERO + 1); + KNOTE_UNLOCKED(&tp->tun_rsel.si_note, 0); TUNDEBUG (ifp, "closed\n"); return (0); } @@ -862,4 +894,97 @@ splx(s); return (revents); +} + +/* tunkqfilter + * + * support for the kevent() system call + */ + +static int +tunkqfilter(struct cdev *dev, struct knote *kn) +{ + int s; + struct tun_softc *tp = dev->si_drv1; + struct ifnet *ifp = TUN2IFP(tp); + + s = splimp(); + switch(kn->kn_filter) + { + case EVFILT_READ: + TUNDEBUG(ifp, "%s kqfilter: EVFILT_READ, minor = %#x\n", + ifp->if_xname, minor(dev)); + kn->kn_fop = &tun_read_filterops; + break; + + case EVFILT_WRITE: + TUNDEBUG(ifp, "%s kqfilter: EVFILT_WRITE, minor = %#x\n", + ifp->if_xname, minor(dev)); + kn->kn_fop = &tun_write_filterops; + break; + + default: + TUNDEBUG(ifp, "%s kqfilter: invalid filter, minor = %#x\n", + ifp->if_xname, minor(dev)); + splx(s); + return(EINVAL); + break; + } + splx(s); + + kn->kn_hook = (caddr_t) dev; + knlist_add(&tp->tun_rsel.si_note, kn, 0); + + return(0); +} + +/* return true of there is data in the interface queue */ + +static int +tunkqread(struct knote *kn, long hint) +{ + int ret, s; + struct cdev *dev = (struct cdev *)(kn->kn_hook); + struct tun_softc *tp = dev->si_drv1; + struct ifnet *ifp = TUN2IFP(tp); + + s = splimp(); + if((kn->kn_data = ifp->if_snd.ifq_len) > 0) + { + TUNDEBUG(ifp, "%s have data in the queue. Len = %d, minor = %#x\n", + ifp->if_xname, ifp->if_snd.ifq_len, minor(dev)); + ret = 1; + } + else + { + TUNDEBUG(ifp, "%s waiting for data, minor = %#x\n",ifp->if_xname, minor(dev)); + ret = 0; + } + splx(s); + + return(ret); +} + +/* Always can write, always return MTU in kn->data */ + +static int +tunkqwrite(struct knote *kn, long hint) +{ + int s; + struct tun_softc *tp = ((struct cdev *) kn->kn_hook)->si_drv1; + struct ifnet *ifp = TUN2IFP(tp); + + s = splimp(); + kn->kn_data = ifp->if_mtu; + splx(s); + + return(1); +} + +static void +tunkqdetach(struct knote *kn) +{ + struct tun_softc *tp = ((struct cdev *) kn->kn_hook)->si_drv1; + + knlist_remove(&tp->tun_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?20060725022023.A54FB4AC45>