kern/93976: if_tun doesn't handle kqueue(2)
Vilmos Nebehaj
vili at huwico.hu
Wed Mar 1 03:20:05 PST 2006
>Number: 93976
>Category: kern
>Synopsis: if_tun 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: Wed Mar 01 11:20:03 GMT 2006
>Closed-Date:
>Last-Modified:
>Originator: Vilmos Nebehaj
>Release: FreeBSD 6.0
>Organization:
>Environment:
FreeBSD oszoo.oszoo 6.0-RELEASE-p4 FreeBSD 6.0-RELEASE-p4 #0: Mon Feb 27 16:55:18 CET 2006
root at oszoo.oszoo:/usr/obj/usr/src/sys/GENERIC i386
>Description:
The same as with if_tap in http://www.freebsd.org/cgi/query-pr.cgi?pr=kern/93897 . Kevent returns an error if someone tries to use it on a tun device (/dev/tunX), because kqueue bits are missing from if_tun.c.I've prepared a patch to address this issue: http://innoidea.com/~vili/if_tun.diff , it cleanly
applies against -CURRENT and RELENG_6_0 too.
Index: if_tun.c
===================================================================
RCS file: /home/ncvs/src/sys/net/if_tun.c,v
retrieving revision 1.152.2.2
diff -u -r1.152.2.2 if_tun.c
--- if_tun.c 25 Aug 2005 05:01:20 -0000 1.152.2.2
+++ if_tun.c 1 Mar 2006 10:47:48 -0000
@@ -117,6 +117,17 @@
struct rtentry *rt);
static void tunstart(struct ifnet *);
+/* kqueue(2) */
+static int tun_kqfilter(struct cdev *, struct knote *);
+static int tun_kqread(struct knote *, long);
+static int tun_kqwrite(struct knote *, long);
+static void tun_kqdetach(struct knote *);
+
+static struct filterops tun_read_filterops = { 1, NULL, tun_kqdetach,
+ tun_kqread };
+static struct filterops tun_write_filterops = { 1, NULL, tun_kqdetach,
+ tun_kqwrite };
+
static d_open_t tunopen;
static d_close_t tunclose;
static d_read_t tunread;
@@ -134,6 +145,7 @@
.d_ioctl = tunioctl,
.d_poll = tunpoll,
.d_name = TUNNAME,
+ .d_kqfilter = tun_kqfilter,
};
static void
@@ -252,6 +264,7 @@
} else
mtx_unlock(&tp->tun_mtx);
selwakeuppri(&tp->tun_rsel, PZERO + 1);
+ KNOTE(&tp->tun_rsel.si_note, 0, 0);
}
/* XXX: should return an error code so it can fail. */
@@ -323,6 +336,9 @@
tp->tun_flags |= TUN_OPEN;
mtx_unlock(&tp->tun_mtx);
ifp = TUN2IFP(tp);
+
+ knlist_init(&tp->tun_rsel.si_note, NULL, NULL, NULL, NULL);
+
TUNDEBUG(ifp, "open\n");
return (0);
@@ -376,6 +392,10 @@
funsetown(&tp->tun_sigio);
selwakeuppri(&tp->tun_rsel, PZERO + 1);
+ KNOTE(&tp->tun_rsel.si_note, 0, 0);
+
+ knlist_destroy(&tp->tun_rsel.si_note);
+
TUNDEBUG (ifp, "closed\n");
return (0);
}
@@ -862,4 +882,84 @@
splx(s);
return (revents);
+}
+
+static int
+tun_kqfilter(struct cdev *dev, struct knote *kn)
+{
+ int s;
+ struct tun_softc *tp = dev->si_drv1;
+ struct ifnet *ifp = tp->tun_ifp;
+
+ s = splimp();
+ switch (kn->kn_filter) {
+ case EVFILT_READ:
+ TUNDEBUG(ifp, "kqfilter: EVFILT_READ, minor = %#x\n",
+ minor(dev));
+ kn->kn_fop = &tun_read_filterops;
+ break;
+ case EVFILT_WRITE:
+ TUNDEBUG(ifp, "kqfilter: EVFILT_WRITE, minor = %#x\n",
+ minor(dev));
+ kn->kn_fop = &tun_write_filterops;
+ break;
+ default:
+ TUNDEBUG(ifp, "kqfilter: invalid filter, minor = %#x\n",
+ 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 if there is data in the interface queue. */
+static int
+tun_kqread(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 = tp->tun_ifp;
+
+ s = splimp();
+ if ((kn->kn_data = ifp->if_snd.ifq_len) > 0) {
+ TUNDEBUG(ifp, "have data in queue. len = %d, " \
+ "minor = %#x\n", ifp->if_snd.ifq_len, minor(dev));
+ ret = 1;
+ } else {
+ TUNDEBUG(ifp, "waiting for data, minor = %#x\n", minor(dev));
+ ret = 0;
+ }
+ splx(s);
+
+ return ret;
+}
+
+/* Always can write. Return the MTU in kn->data. */
+static int
+tun_kqwrite(struct knote *kn, long hint)
+{
+ int s;
+ struct tun_softc *tp = ((struct cdev *)kn->kn_hook)->si_drv1;
+ struct ifnet *ifp = tp->tun_ifp;
+
+ s = splimp();
+ kn->kn_data = ifp->if_mtu;
+ splx(s);
+
+ return 1;
+}
+
+static void
+tun_kqdetach(struct knote *kn)
+{
+ struct tun_softc *tp = ((struct cdev *)kn->kn_hook)->si_drv1;
+
+ knlist_remove(&tp->tun_rsel.si_note, kn, 0);
}
>How-To-Repeat:
Use kqueue/kevent on a file descriptor obtained from opening /dev/tunX.
>Fix:
I've prepared a patch to address this issue: http://innoidea.com/~vili/if_tun.diff , it cleanly
applies against -CURRENT and RELENG_6_0 too.
Index: if_tun.c
===================================================================
RCS file: /home/ncvs/src/sys/net/if_tun.c,v
retrieving revision 1.152.2.2
diff -u -r1.152.2.2 if_tun.c
--- if_tun.c 25 Aug 2005 05:01:20 -0000 1.152.2.2
+++ if_tun.c 1 Mar 2006 10:47:48 -0000
@@ -117,6 +117,17 @@
struct rtentry *rt);
static void tunstart(struct ifnet *);
+/* kqueue(2) */
+static int tun_kqfilter(struct cdev *, struct knote *);
+static int tun_kqread(struct knote *, long);
+static int tun_kqwrite(struct knote *, long);
+static void tun_kqdetach(struct knote *);
+
+static struct filterops tun_read_filterops = { 1, NULL, tun_kqdetach,
+ tun_kqread };
+static struct filterops tun_write_filterops = { 1, NULL, tun_kqdetach,
+ tun_kqwrite };
+
static d_open_t tunopen;
static d_close_t tunclose;
static d_read_t tunread;
@@ -134,6 +145,7 @@
.d_ioctl = tunioctl,
.d_poll = tunpoll,
.d_name = TUNNAME,
+ .d_kqfilter = tun_kqfilter,
};
static void
@@ -252,6 +264,7 @@
} else
mtx_unlock(&tp->tun_mtx);
selwakeuppri(&tp->tun_rsel, PZERO + 1);
+ KNOTE(&tp->tun_rsel.si_note, 0, 0);
}
/* XXX: should return an error code so it can fail. */
@@ -323,6 +336,9 @@
tp->tun_flags |= TUN_OPEN;
mtx_unlock(&tp->tun_mtx);
ifp = TUN2IFP(tp);
+
+ knlist_init(&tp->tun_rsel.si_note, NULL, NULL, NULL, NULL);
+
TUNDEBUG(ifp, "open\n");
return (0);
@@ -376,6 +392,10 @@
funsetown(&tp->tun_sigio);
selwakeuppri(&tp->tun_rsel, PZERO + 1);
+ KNOTE(&tp->tun_rsel.si_note, 0, 0);
+
+ knlist_destroy(&tp->tun_rsel.si_note);
+
TUNDEBUG (ifp, "closed\n");
return (0);
}
@@ -862,4 +882,84 @@
splx(s);
return (revents);
+}
+
+static int
+tun_kqfilter(struct cdev *dev, struct knote *kn)
+{
+ int s;
+ struct tun_softc *tp = dev->si_drv1;
+ struct ifnet *ifp = tp->tun_ifp;
+
+ s = splimp();
+ switch (kn->kn_filter) {
+ case EVFILT_READ:
+ TUNDEBUG(ifp, "kqfilter: EVFILT_READ, minor = %#x\n",
+ minor(dev));
+ kn->kn_fop = &tun_read_filterops;
+ break;
+ case EVFILT_WRITE:
+ TUNDEBUG(ifp, "kqfilter: EVFILT_WRITE, minor = %#x\n",
+ minor(dev));
+ kn->kn_fop = &tun_write_filterops;
+ break;
+ default:
+ TUNDEBUG(ifp, "kqfilter: invalid filter, minor = %#x\n",
+ 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 if there is data in the interface queue. */
+static int
+tun_kqread(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 = tp->tun_ifp;
+
+ s = splimp();
+ if ((kn->kn_data = ifp->if_snd.ifq_len) > 0) {
+ TUNDEBUG(ifp, "have data in queue. len = %d, " \
+ "minor = %#x\n", ifp->if_snd.ifq_len, minor(dev));
+ ret = 1;
+ } else {
+ TUNDEBUG(ifp, "waiting for data, minor = %#x\n", minor(dev));
+ ret = 0;
+ }
+ splx(s);
+
+ return ret;
+}
+
+/* Always can write. Return the MTU in kn->data. */
+static int
+tun_kqwrite(struct knote *kn, long hint)
+{
+ int s;
+ struct tun_softc *tp = ((struct cdev *)kn->kn_hook)->si_drv1;
+ struct ifnet *ifp = tp->tun_ifp;
+
+ s = splimp();
+ kn->kn_data = ifp->if_mtu;
+ splx(s);
+
+ return 1;
+}
+
+static void
+tun_kqdetach(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