On Mon, 20 Jul 2026 14:06:56 +0200
Maxime Leroy <[email protected]> wrote:
> The tap PMD registers each queue file descriptor for Rx interrupts but the
> feature is not usable through the generic API. This series fixes two
> distinct issues against the original Rx interrupt support, plus a small
> type cleanup.
>
> Patch 1 adds the missing rx_queue_intr_enable/disable ops. Without them
> rte_eth_dev_rx_intr_enable() returns -ENOTSUP, so applications that arm
> queues before sleeping (l3fwd-power and similar) treat tap as having no
> interrupt support and fall back to polling.
>
> Patch 2 fixes a traffic stall specific to interrupt mode: the Rx burst
> skips reading the queue fd until the SIGIO trigger advances, but in
> interrupt mode the application wakes through epoll, a distinct signal, so
> the burst can return 0 right after a wakeup and the fd stays readable with
> no further edge. The fd is now drained unconditionally in interrupt mode
> and the SIGIO trigger is not armed on the data queue fds.
>
> Patch 3 converts the driver's int-as-boolean fields to bool.
>
> Patches 1 and 2 are candidates for stable.
>
> v2:
> - use bool for the new interrupt-mode fields instead of int (Stephen);
> both fit existing padding, so neither struct grows
> - add patch 3 converting the existing int booleans to bool
>
> The coding_style.rst note that discouraged bool in structures no longer
> matches DPDK practice; it is dropped by a separate patch.
>
> Maxime Leroy (3):
> net/tap: support Rx queue interrupt enable/disable
> net/tap: drain queue FD in Rx interrupt mode
> net/tap: use bool for boolean flags
>
> drivers/net/tap/rte_eth_tap.c | 25 +++++++++++++++++++-
> drivers/net/tap/rte_eth_tap.h | 11 ++++++---
> drivers/net/tap/tap_flow.c | 4 ++--
> drivers/net/tap/tap_intr.c | 44 +++++++++++++++++++++++++++++++++++
> 4 files changed, 78 insertions(+), 6 deletions(-)
>
Like this but detailed AI review found some things.
Review of [PATCH v2 0/3] net/tap: Rx interrupt fixes
Applied clean to 38f72e5. All three commits build independently under
-Dwerror=true, so the series is bisect-safe. No compiler diagnostics.
Verified against a running tap port (/dev/net/tun, kernel-side traffic),
not just by reading the diffs.
Patch 1/3: net/tap: support Rx queue interrupt enable/disable
------------------------------------------------------------
Confirmed working. rte_eth_dev_rx_intr_enable()/disable() return 0 in
interrupt mode where they previously returned -ENOTSUP, and an
epoll-driven receive loop delivers 100% of offered packets.
The -ENOTSUP branch is not dead code: rte_eth_dev_rx_intr_enable()
validates only the queue id and the op pointer, never intr_conf.rxq, so
the driver is the sole guard.
tap.ini already advertises "Rx interrupt = Y", so no features-matrix
change is needed -- that overclaim is precisely what this patch fixes.
Info: pmd->intr_mode duplicates dev->data->dev_conf.intr_conf.rxq, which
tap_rx_intr_vec_install() already reads directly. The rx_queue copy earns
its place on the fast path; the pmd_internals copy is redundant state
that can drift. Not blocking.
Error (pre-existing, but patch 2 makes it reachable)
---------------------------------------------------
tap_tx_queue_release() dereferences dev->data->rx_queues after ethdev has
already freed it:
/* lib/ethdev/rte_ethdev.c */
reset_queues:
eth_dev_rx_queue_config(dev, 0); /* rte_free(rx_queues); = NULL */
eth_dev_tx_queue_config(dev, 0); /* -> tap_tx_queue_release() */
/* drivers/net/tap/rte_eth_tap.c */
if (dev->data->rx_queues[qid] == NULL) /* rx_queues is NULL here */
Confirmed by three independent reproducers, all segfaulting in
tap_tx_queue_release():
1. reconfigure that trips patch 2's new intr_mode check
2. reconfigure that trips the pre-existing nb_rx != nb_tx check
(intr_conf.rxq never set -- so this is NOT introduced by the series)
3. rte_eth_dev_internal_reset() on a normally configured port, with no
dev_configure failure at all
Path 3 matters independently of this series: bonding's member_remove()
calls rte_eth_dev_internal_reset() unconditionally, so removing a tap
port from a bonding device crashes on current main.
Both ethdev call sites free rx before tx, so tap_rx_queue_release()'s
symmetric read of dev->data->tx_queues[qid] is always safe. Only the tx
side needs the guard, though guarding both is cheap.
This is not your bug. But patch 2 adds a second failure return to
tap_dev_configure() that fires on an ordinary reconfigure, and the commit
message points users straight at it -- an application trying to toggle
interrupt mode crashes instead of receiving -ENOTSUP. Suggest a
preceding patch in the series fixing the deref, with its own Fixes: and
Cc: [email protected].
Patch 2/3: net/tap: drain queue FD in Rx interrupt mode
-------------------------------------------------------
The mechanism works as designed. Verified at the kernel fd-flag level
(/proc/PID/fdinfo) that in interrupt mode the data queue fd is created
without O_ASYNC while the keep-alive fd retains it:
patch 1 only : ka fd O_ASYNC=yes, data fd O_ASYNC=yes
full series : ka fd O_ASYNC=yes, data fd O_ASYNC=no
The early return in tun_alloc() sits after the O_NONBLOCK fcntl, so
nothing required is skipped and there is no fd leak. Reading dev_private
is safe: rte_eth_dev_release_port() frees it on close, so "close and
reopen the port to change it" is accurate.
Warning: I could not reproduce the stall this patch fixes. With patch 1
applied alone -- SIGIO still armed on the data fd -- three traffic
patterns all delivered every packet with zero empty bursts after a
wakeup:
- low-rate broadcast ARP : 117/117 rx, 0 empty after wake
- flood ping, static neighbour : 437/437 rx, 0 empty after wake
- strict l3fwd-power-style loop
(one burst per wakeup, no
re-poll) with bursty traffic : 100/100 rx, 0 empty after wake
The full series produced identical numbers in each case. Single queue,
single core, kernel-side tap traffic, so this does not disprove the race
-- it may need multiple queues, higher rates than the tap path gives, or
signal-queue pressure. But the justification is not demonstrated by any
reproducer I could build, and the patch has real cost: a permanent
restriction that interrupt mode cannot be toggled after configure, a new
error path that exposes the crash above, a fast-path branch, and a
readv() syscall on every idle burst (exactly what the SIGIO trigger
exists to avoid).
Could you share a reproducer, or describe the configuration where the
stall appears? Note that the Rx interrupt epoll registration is
edge-triggered (EPOLLIN|EPOLLPRI|EPOLLET in rte_intr_rx_ctl), which is
what would turn a single missed read into a permanent stall rather than
a spin -- that detail seems worth stating explicitly in the commit
message, since it is the part that makes the failure non-self-healing.
For what it is worth, the EAL side is safe: tap sets
RTE_INTR_HANDLE_EXT, so eal_intr_proc_rxtx_intr() returns early and never
consumes packet bytes from the queue fd.
Patch 3/3: net/tap: use bool for boolean flags
----------------------------------------------
Warning: the conversion is incomplete on its own terms.
- tap_flow.c:1247 still reads "if (pmd->flow_init == 0 && ...)";
on a bool this should be "!pmd->flow_init"
- the three converted fields keep "/* 1 if ... */" comments
- the local "int persist" in rte_pmd_tap_probe() and the "int persist"
parameter of pmd_init_internals() were not converted
Info: rte_eth_tap.h uses bool without including <stdbool.h>, relying on a
transitive include via ethdev_driver.h. It compiles; a direct include
would make the header self-contained.
I initially flagged the (set ^ pmd->flow_isolate) idiom in
tap_flow_isolate() as losing idempotency for non-0/1 values under the
bool conversion. There is a "if (set) set = 1;" normalization immediately
above it, so the XOR is correct. Dropped.
Summary
-------
Patch 1 is good and independently verified. Patch 3 needs the leftovers
cleaned up. Patch 2 is mechanically sound but its justification is
unreproduced here, and it should not go in ahead of a fix for the
tap_tx_queue_release() NULL dereference it makes easy to hit.