anchao opened a new pull request, #21477:
URL: https://github.com/apache/nuttx/pull/21477
## Summary
This PR reduces the poll registration churn in the default epoll
implementation.
Previously, epoll rebuilt driver poll registrations repeatedly across
`epoll_wait()` calls: notified fds were torn down with `file_poll(..., false)`,
and the next wait cycle registered them again with `file_poll(..., true)`. With
many monitored fds, this makes the wait path spend a significant amount of time
in driver poll teardown/setup instead of only returning the ready set.
This PR changes epoll to use an internal ready list and only revalidates the
nodes that were actually delivered to userspace.
The change is split into two commits:
1. `fs/epoll: Avoid rebuilding all poll registrations on every wait`
- Adds an epoll ready list.
- Keeps idle driver poll registrations armed after `EPOLL_CTL_ADD`.
- Enqueues notified nodes from the driver callback.
- Delivers at most `maxevents` nodes per wait and keeps the remaining
ready nodes queued for the next wait.
- Preserves `EPOLLONESHOT` semantics by tearing down one-shot nodes only
after the event is actually delivered to userspace.
- Revalidates delivered level-triggered nodes after delivery.
- Adds a generic `file_poll()` compatibility fallback for legacy poll
drivers that return `-EBUSY` when an already-bound `pollfd_s` is offered again.
2. `drivers,net,fs: Migrate poll drivers to the refresh contract`
- Updates in-tree poll lower-half implementations so that `setup == true
&& fds->priv != NULL` is treated as a refresh instead of a new slot allocation.
- Prevents persistent epoll users from consuming extra poll slots.
- Adds guards for socket/protocol poll implementations that cannot
refresh in-place, allowing the generic fallback to rebuild the binding.
- Fixes teardown paths where needed so the saved poll slot is released
precisely.
## Background
The old implementation effectively performed repeated unregister/register
work around epoll waits. For example, with a large epoll set, each wait cycle
could cause many driver `poll(false)` / `poll(true)` calls even though most fds
were already registered before.
This is especially expensive for drivers or socket protocols whose poll
setup touches locks, callback allocation, wait queues, or protocol state.
The new model is:
- `EPOLL_CTL_ADD` registers the driver poll once.
- Driver `poll_notify()` enqueues the epoll node into an internal ready list.
- `epoll_wait()` consumes the ready list.
- `EPOLLONESHOT` nodes are disabled after delivery until re-armed by
`EPOLL_CTL_MOD`.
- Level-triggered nodes are revalidated after delivery so they can be
reported again if they remain ready.
To avoid races with asynchronous `poll_notify()`, level-triggered
revalidation tears down the delivered node before clearing `revents`, then sets
it up again to query current readiness. This still avoids rebuilding the whole
epoll set on every wait; only delivered nodes are revalidated.
## Compatibility with existing poll drivers
The generic `file_poll()` helper now handles legacy drivers that do not yet
support the refresh contract.
If a persistent user passes an already-bound `pollfd_s` to a driver and the
driver returns `-EBUSY`, `file_poll()` restores the saved `priv` pointer and
rebuilds the binding through the legacy teardown + setup cycle.
Other setup errors are not hidden by this fallback, so real failures from
`EPOLL_CTL_MOD` are propagated instead of silently tearing down an existing
registration.
This keeps out-of-tree drivers compatible while allowing in-tree drivers to
migrate incrementally.
## Driver migration contract
For poll implementations that support refresh in-place:
```c
if (setup)
{
if (fds->priv == NULL)
{
/* First-time slot binding */
}
/* Recompute readiness and poll_notify() if ready */
}
else
{
/* Teardown using fds->priv / saved slot */
}
```
For protocols or lower halves that cannot safely refresh in-place, the
implementation returns `-EBUSY` when `fds->priv != NULL`, so the generic
fallback can rebuild the binding safely.
## Notes
- This PR does not implement EPOLLET; existing behavior is unchanged.
- poll() itself still uses the traditional setup/wait/teardown model.
- The optimization targets epoll's repeated wait path.
## Testing
Tested on qemu-armv7a:nsh / sim:nsh
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]