|WARNING| eal: do not dispatch newly registered callbacks
dpdklab at iol.unh.edu
dpdklab at iol.unh.edu
Tue Nov 3 14:06:16 CET 2026
Test-Label: ai-code-review
Test-Status: WARNING
https://dpdk.org/patch/172370
_AI Code Review_
=== Patch Review: series.patch (via Claude) ===
Review date: 2026-11-03
# DPDK Patch Review
## ERRORS
**Missing error handling in loop termination logic**
The loop termination condition `if (cb == last)` executes after calling the callback and re-acquiring the lock. If the callback unregistered itself (which is explicitly mentioned as a use case in the commit message), `cb` becomes invalid after the unlock, and comparing it to `last` is use-after-free.
```c
/* Current code - WRONG */
for (cb = TAILQ_FIRST(&src->callbacks); cb != NULL; cb = next) {
next = TAILQ_NEXT(cb, next);
active_cb = *cb;
rte_spinlock_unlock(&intr_lock);
/* cb may be freed here if callback unregisters itself */
active_cb.cb_fn(active_cb.cb_arg, active_events);
rte_spinlock_lock(&intr_lock);
if (cb == last) /* BUG: cb may have been freed */
break;
}
```
**Suggested fix**: Check the termination condition before unlocking:
```c
for (cb = TAILQ_FIRST(&src->callbacks); cb != NULL; cb = next) {
next = TAILQ_NEXT(cb, next);
bool is_last = (cb == last);
active_cb = *cb;
rte_spinlock_unlock(&intr_lock);
active_cb.cb_fn(active_cb.cb_arg, active_events);
rte_spinlock_lock(&intr_lock);
if (is_last)
break;
}
```
## WARNINGS
**Missing release notes update**
This patch fixes a significant correctness bug (infinite blocking in net/tap driver) that could affect users. The fix changes interrupt callback dispatch behavior, which is important enough to document in release notes.
Add an entry to `doc/guides/rel_notes/release_XX_XX.rst` under "Fixed Issues":
```rst
* **Fixed interrupt callback re-registration during dispatch.**
Fixed an issue where registering a new interrupt callback from within
a callback (e.g., net/tap LSC on netns change) could cause the new
callback to be invoked with a stale event, leading to blocking I/O.
```
**Inconsistent variable declaration style**
The `last` pointer is declared inside the `if (call)` block, while `next` is declared at loop initialization. For consistency and to make the loop logic clearer, declare both at the start of the block.
```c
/* Current - mixed declaration locations */
if (call) {
struct rte_intr_callback *last;
last = TAILQ_LAST(&src->callbacks, rte_intr_cb_list);
for (cb = TAILQ_FIRST(&src->callbacks); cb != NULL; cb = next) {
next = TAILQ_NEXT(cb, next);
/* Suggested - declare together */
if (call) {
struct rte_intr_callback *last, *next;
last = TAILQ_LAST(&src->callbacks, rte_intr_cb_list);
for (cb = TAILQ_FIRST(&src->callbacks); cb != NULL; cb = next) {
```
---
Review-Result: ERROR
More information about the test-report
mailing list