kern/100796: if_tun requires kqueue hooks (patch included)
David Gilbert
dgilbert at daveg.ca
Tue Jul 25 02:30:21 UTC 2006
>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 at 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:
More information about the freebsd-bugs
mailing list