Add kqueue support to if_tun. Loosely based on if_tap changes.

Two almost identical patches based on the if_tap work were submitted
via GNATS; I started out with the patch in 100796 from David Gilbert,
but could have easily started with the patch from Vilmos Nebehaj which
I found only later.

MFC after:	1 week
PR:		93976, 100796
This commit is contained in:
Robert Watson 2006-08-08 19:22:25 +00:00
parent 05680ab622
commit ae476dd78f
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=161103

View File

@ -123,6 +123,25 @@ static d_read_t tunread;
static d_write_t tunwrite;
static d_ioctl_t tunioctl;
static d_poll_t tunpoll;
static d_kqfilter_t tunkqfilter;
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,
@ -133,6 +152,7 @@ static struct cdevsw tun_cdevsw = {
.d_write = tunwrite,
.d_ioctl = tunioctl,
.d_poll = tunpoll,
.d_kqfilter = tunkqfilter,
.d_name = TUNNAME,
};
@ -179,6 +199,7 @@ tun_destroy(struct tun_softc *tp)
if_detach(TUN2IFP(tp));
if_free(TUN2IFP(tp));
destroy_dev(dev);
knlist_destroy(&tp->tun_rsel.si_note);
mtx_destroy(&tp->tun_mtx);
free(tp, M_TUN);
}
@ -231,6 +252,7 @@ tunstart(struct ifnet *ifp)
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 +274,7 @@ tunstart(struct ifnet *ifp)
} 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. */
@ -285,10 +308,13 @@ tuncreate(struct cdev *dev)
IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
ifp->if_snd.ifq_drv_maxlen = 0;
IFQ_SET_READY(&ifp->if_snd);
knlist_init(&sc->tun_rsel.si_note, NULL, NULL, NULL, NULL);
if_attach(ifp);
bpfattach(ifp, DLT_NULL, sizeof(u_int32_t));
dev->si_drv1 = sc;
TUNDEBUG(ifp, "interface %s is created, minor = %#x\n",
ifp->if_xname, minor(dev));
}
static int
@ -376,6 +402,7 @@ tunclose(struct cdev *dev, int foo, int bar, struct thread *td)
funsetown(&tp->tun_sigio);
selwakeuppri(&tp->tun_rsel, PZERO + 1);
KNOTE_UNLOCKED(&tp->tun_rsel.si_note, 0);
TUNDEBUG (ifp, "closed\n");
return (0);
}
@ -712,7 +739,7 @@ tunread(struct cdev *dev, struct uio *uio, int flag)
mtx_lock(&tp->tun_mtx);
tp->tun_flags |= TUN_RWAIT;
mtx_unlock(&tp->tun_mtx);
if((error = tsleep(tp, PCATCH | (PZERO + 1),
if ((error = tsleep(tp, PCATCH | (PZERO + 1),
"tunread", 0)) != 0) {
splx(s);
return (error);
@ -857,3 +884,94 @@ tunpoll(struct cdev *dev, int events, struct thread *td)
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);
}
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);
}