Skip to content

Answer pselect6's poll fallback as select does - #318

Merged
jserv merged 1 commit into
sysprog21:mainfrom
xalestar:pselect-fallback-pollnval
Aug 22, 2026
Merged

Answer pselect6's poll fallback as select does#318
jserv merged 1 commit into
sysprog21:mainfrom
xalestar:pselect-fallback-pollnval

Conversation

@xalestar

@xalestarxalestar commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

pselect6 falls back to poll() when a host descriptor is out of the fd_set range, causing the guest to receive poll()'s behavior in two ways: descriptors refused by macOS poll() (/dev/null, /dev/zero, /dev/urandom, directories, and kqueue fds) arrive as not ready, whereas Linux select() reports them as ready; additionally, the return value counts each descriptor once, whereas select() counts it once per set. The refused entries are routed through poll_eval_unpollable(), the helper sys_ppoll() already uses for the same descriptor set, and the count comes from the bits set by the write-back.

Reproduction is covered by the two high-descriptor cases in tests/test-poll.c, which fail on main and pass here. make check is clean, make lint is unchanged, and tests/test-matrix.sh passes all within baseline with zero failures (elfuse-aarch64 255, qemu-aarch64 234, elfuse-x86_64 78).

cubic-dev-ai[bot]

This comment was marked as resolved.

Comment threadsrc/syscall/poll.c Outdated
Comment threadsrc/syscall/poll.c
Comment threadtests/test-poll.c
@xalestar
xalestarforce-pushed the pselect-fallback-pollnval branch from 54e9703 to a2444a7CompareAugust 22, 2026 03:34
@xalestarxalestar changed the title Stop pselect6's fallback hiding a ready descriptorAnswer pselect6's poll fallback as select doesAug 22, 2026
Comment threadsrc/syscall/poll.c Outdated
pselect6 leaves fd_set behind and waits in poll() when a host
descriptor lands at or above FD_SETSIZE, and hands the guest poll()'s
view of that wait in two places.
macOS poll() answers POLLNVAL for every descriptor it will not put on a
kqueue: /dev/null, /dev/zero, /dev/random, /dev/urandom, directories,
and kqueue descriptors themselves. The fallback copies that revents
into the request, where POLLNVAL maps to no fd_set bit, so a guest
whose descriptor table is close to full is told a live /dev/null is not
ready. Linux select() reports every one of them ready.
poll_eval_unpollable() answers those entries for sys_ppoll already, so
the fallback takes them out of the poll set on the pass that exposes
them and hands them to the same function. A refused entry makes poll()
return at once, so that single restart waits for nothing.
The return value is the second place. poll() counts a descriptor once
however many events it reports, while select() counts it once per set
it is reported in. Measured against the qemu-system-aarch64 reference
VM, a /dev/null in both readfds and writefds answers 2 there and 1
through the fallback, and a socket pair holding a byte answers the same
way. The count comes from the bits the write-back lights.
Reaching the fallback needs no descriptor pressure beyond a high
number. host_fd_ref_open_io() borrows the host descriptor instead of
duplicating it while one thread is active, so a single-threaded guest
gets there as soon as a borrowed descriptor is numbered at or above
FD_SETSIZE.
The poll pass moves into pselect_fallback_pass() with its state in one
struct, since sys_pselect6 crosses the 400-line clang-tidy advisory
otherwise. An allocation failure there leaves the wait loop at once,
ahead of the interrupt predicates that can overwrite errno.
Verified: tests/test-poll.c carries three new cases and reports 22
passed, 0 failed both under elfuse and inside the reference VM, and
its two high-descriptor cases fail against main as it stands. make
check is clean, make lint reports no new finding, and
tests/test-matrix.sh all stays within baseline with zero failures:
elfuse-aarch64 255 passed, qemu-aarch64 234 passed, elfuse-x86_64
78 passed.
@xalestar
xalestarforce-pushed the pselect-fallback-pollnval branch from a2444a7 to 272932bCompareAugust 22, 2026 08:23
@jserv
jserv merged commit b3f8ddc into sysprog21:mainAug 22, 2026
14 checks passed
@xalestar
xalestar deleted the pselect-fallback-pollnval branch August 22, 2026 09:44
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@xalestar@jserv