diff --git a/sys/netgraph/ng_device.c b/sys/netgraph/ng_device.c --- a/sys/netgraph/ng_device.c +++ b/sys/netgraph/ng_device.c @@ -3,6 +3,7 @@ * * Copyright (c) 2002 Mark Santcroos * Copyright (c) 2004-2005 Gleb Smirnoff + * Copyright (c) 2025 Quentin Thebault * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -50,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -117,12 +119,15 @@ struct ng_node *node; struct ng_hook *hook; struct cdev *ngddev; + struct selinfo rsel; + struct selinfo wsel; struct mtx ngd_mtx; int unit; int ether_align; uint16_t flags; #define NGDF_OPEN 0x0001 #define NGDF_RWAIT 0x0002 +#define NGDF_DYING 0x0004 }; typedef struct ngd_private *priv_p; @@ -138,6 +143,24 @@ static d_write_t ngdwrite; static d_ioctl_t ngdioctl; static d_poll_t ngdpoll; +static d_kqfilter_t ngdkqfilter; + +static int ngd_kqread_event(struct knote *, long); +static int ngd_kqwrite_event(struct knote *, long); +static void ngd_kqread_detach(struct knote *); +static void ngd_kqwrite_detach(struct knote *); + +static const struct filterops ngd_read_filterops = { + .f_isfd = 1, + .f_detach = ngd_kqread_detach, + .f_event = ngd_kqread_event +}; + +static const struct filterops ngd_write_filterops = { + .f_isfd = 1, + .f_detach = ngd_kqwrite_detach, + .f_event = ngd_kqwrite_event +}; static struct cdevsw ngd_cdevsw = { .d_version = D_VERSION, @@ -146,6 +169,7 @@ .d_read = ngdread, .d_write = ngdwrite, .d_ioctl = ngdioctl, + .d_kqfilter = ngdkqfilter, .d_poll = ngdpoll, .d_name = NG_DEVICE_DEVNAME, }; @@ -198,6 +222,9 @@ mtx_init(&priv->readq.ifq_mtx, "ng_device queue", NULL, MTX_DEF); IFQ_SET_MAXLEN(&priv->readq, ifqmaxlen); + knlist_init_mtx(&priv->rsel.si_note, &priv->ngd_mtx); + knlist_init_mtx(&priv->wsel.si_note, &priv->ngd_mtx); + /* Link everything together */ NG_NODE_SET_PRIVATE(node, priv); priv->node = node; @@ -206,6 +233,8 @@ GID_WHEEL, 0600, NG_DEVICE_DEVNAME "%d", priv->unit); if (priv->ngddev == NULL) { printf("%s(): make_dev() failed\n", __func__); + knlist_destroy(&priv->rsel.si_note); + knlist_destroy(&priv->wsel.si_note); mtx_destroy(&priv->ngd_mtx); mtx_destroy(&priv->readq.ifq_mtx); free_unr(ngd_unit, priv->unit); @@ -319,6 +348,8 @@ priv->flags &= ~NGDF_RWAIT; wakeup(priv); } + selwakeup(&priv->rsel); + KNOTE_LOCKED(&priv->rsel.si_note, 0); mtx_unlock(&priv->ngd_mtx); return (0); @@ -334,9 +365,22 @@ DBG; + mtx_lock(&priv->ngd_mtx); + priv->flags |= NGDF_DYING; + wakeup(priv); + mtx_unlock(&priv->ngd_mtx); + destroy_dev(priv->ngddev); + + knlist_clear(&priv->rsel.si_note, 0); + knlist_clear(&priv->wsel.si_note, 0); + knlist_destroy(&priv->rsel.si_note); + knlist_destroy(&priv->wsel.si_note); mtx_destroy(&priv->ngd_mtx); + seldrain(&priv->rsel); + seldrain(&priv->wsel); + IF_DRAIN(&priv->readq); mtx_destroy(&(priv)->readq.ifq_mtx); @@ -487,15 +531,16 @@ /* get an mbuf */ do { + if (priv->flags & NGDF_DYING) + return (ENXIO); IF_DEQUEUE(&priv->readq, m); if (m == NULL) { if (flag & O_NONBLOCK) return (EWOULDBLOCK); mtx_lock(&priv->ngd_mtx); priv->flags |= NGDF_RWAIT; - if ((error = msleep(priv, &priv->ngd_mtx, - PDROP | PCATCH | PZERO, - "ngdread", 0)) != 0) + if ((error = mtx_sleep(priv, &priv->ngd_mtx, + PDROP | PCATCH, "ngdread", 0)) != 0) return (error); } } while (m == NULL); @@ -538,9 +583,12 @@ if (m == NULL) return (ENOBUFS); + // Setting VNET is required if connecting to a ng_bridge. + CURVNET_SET(priv->node->nd_vnet); NET_EPOCH_ENTER(et); NG_SEND_DATA_ONLY(error, priv->hook, m); NET_EPOCH_EXIT(et); + CURVNET_RESTORE(); return (error); } @@ -561,3 +609,66 @@ return (revents); } + +static void +ngd_kqread_detach(struct knote *kn) +{ + priv_p priv = (priv_p)kn->kn_hook; + + knlist_remove(&priv->rsel.si_note, kn, 0); +} + +static int +ngd_kqread_event(struct knote *kn, long hint) +{ + priv_p priv = (priv_p)kn->kn_hook; + struct mbuf *m; + + IFQ_LOCK(&priv->readq); + if (IFQ_IS_EMPTY(&priv->readq)) { + kn->kn_data = 0; + } else { + IF_POLL(&priv->readq, m); + kn->kn_data = m->m_len; + } + IFQ_UNLOCK(&priv->readq); + + return (kn->kn_data > 0); +} + +static void +ngd_kqwrite_detach(struct knote *kn) +{ + priv_p priv = (priv_p)kn->kn_hook; + + knlist_remove(&priv->wsel.si_note, kn, 0); +} + +static int +ngd_kqwrite_event(struct knote *kn, long hint) +{ + kn->kn_data = IP_MAXPACKET; + + return (1); +} + +static int +ngdkqfilter(struct cdev *dev, struct knote *kn) +{ + priv_p priv = (priv_p)dev->si_drv1; + + switch (kn->kn_filter) { + case EVFILT_READ: + kn->kn_fop = &ngd_read_filterops; + kn->kn_hook = priv; + knlist_add(&priv->rsel.si_note, kn, 0); + return (0); + case EVFILT_WRITE: + kn->kn_fop = &ngd_write_filterops; + kn->kn_hook = priv; + knlist_add(&priv->wsel.si_note, kn, 0); + return (0); + default: + return (EINVAL); + } +}