[PATCH v3 2/2] eal/linux: handle interrupt epoll events

Kevin Traynor
2026-02-10T18:22:33Z

Add handling for epoll error and disconnect conditions EPOLLERR,
EPOLLHUP and EPOLLRDHUP.

These events indicate that the interrupt file descriptor is in
an error state or there has been a hangup.

Only do this for interrupts that are read in eal. Interrupts that
are read outside eal should deal with different interrupt scenarios
appropriate to their functionality. e.g. virtio interrupt handling
has reconnect mechanisms for some cases.

Also, treat no bytes read as an error condition.

Bugzilla ID: 1873
Fixes: af75078fece3 ("first public release")
Cc: stable@dpdk.org

Signed-off-by: Kevin Traynor <ktraynor@redhat.com>
Acked-by: David Marchand <david.marchand@redhat.com>
---
lib/eal/linux/eal_interrupts.c | 72 ++++++++++++++++++++++------------
1 file changed, 46 insertions(+), 26 deletions(-)

diff --git a/lib/eal/linux/eal_interrupts.c b/lib/eal/linux/eal_interrupts.c
index 9db978923a..f3f6bdd01d 100644
--- a/lib/eal/linux/eal_interrupts.c
+++ b/lib/eal/linux/eal_interrupts.c
@@ -887,4 +887,26 @@ rte_intr_disable(const struct rte_intr_handle *intr_handle)
}

+static void
+eal_intr_source_remove_and_free(struct rte_intr_source *src)
+{
+ struct rte_intr_callback *cb, *next;
+
+ /* Remove the interrupt source */
+ rte_spinlock_lock(&intr_lock);
+ TAILQ_REMOVE(&intr_sources, src, next);
+ rte_spinlock_unlock(&intr_lock);
+
+ /* Free callbacks */
+ for (cb = TAILQ_FIRST(&src->callbacks); cb; cb = next) {
+ next = TAILQ_NEXT(cb, next);
+ TAILQ_REMOVE(&src->callbacks, cb, next);
+ free(cb);
+ }
+
+ /* Free the interrupt source */
+ rte_intr_instance_free(src->intr_handle);
+ free(src);
+}
+
static int
eal_intr_process_interrupts(struct epoll_event *events, int nfds)
@@ -952,40 +974,38 @@ eal_intr_process_interrupts(struct epoll_event *events, int nfds)

if (bytes_read > 0) {
- /**
+ /*
+ * Check for epoll error or disconnect events for
+ * interrupts that are read directly in eal.
+ */
+ if (events[n].events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) {
+ EAL_LOG(ERR, "Disconnect condition on fd %d "
+ "(events=0x%x), removing from epoll",
+ events[n].data.fd, events[n].events);
+ eal_intr_source_remove_and_free(src);
+ return -1;
+ }
+
+ /*
* read out to clear the ready-to-be-read flag
* for epoll_wait.
*/
bytes_read = read(events[n].data.fd, &buf, bytes_read);
- if (bytes_read < 0) {
+ if (bytes_read > 0) {
+ call = true;
+ } else if (bytes_read < 0) {
if (errno == EINTR || errno == EWOULDBLOCK)
continue;

- EAL_LOG(ERR, "Error reading from file "
- "descriptor %d: %s",
+ EAL_LOG(ERR, "Error reading from file descriptor %d: %s",
events[n].data.fd,
strerror(errno));
- /*
- * The device is unplugged or buggy, remove
- * it as an interrupt source and return to
- * force the wait list to be rebuilt.
- */
- rte_spinlock_lock(&intr_lock);
- TAILQ_REMOVE(&intr_sources, src, next);
- rte_spinlock_unlock(&intr_lock);
-
- for (cb = TAILQ_FIRST(&src->callbacks); cb;
- cb = next) {
- next = TAILQ_NEXT(cb, next);
- TAILQ_REMOVE(&src->callbacks, cb, next);
- free(cb);
- }
- rte_intr_instance_free(src->intr_handle);
- free(src);
+ } else {
+ EAL_LOG(ERR, "Read nothing from file descriptor %d",
+ events[n].data.fd);
+ }
+ if (bytes_read <= 0) {
+ eal_intr_source_remove_and_free(src);
return -1;
- } else if (bytes_read == 0)
- EAL_LOG(ERR, "Read nothing from file "
- "descriptor %d", events[n].data.fd);
- else
- call = true;
+ }
}

--
2.52.0