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>
