io_submit EAGAIN + abort on SEASTAR_ASSERT(clock::now() < retry_until) in
aio_general_context::flush().
Two programs:
Shows the kernel mechanics directly: creates an aio context, reads the actual
completion-ring capacity from the mmap'd ring header, then submits
IOCB_CMD_POLL iocbs on idle socketpairs (which never complete, so each holds
a ring slot forever) until io_submit returns EAGAIN, and finally retries for
1.5 s to show the EAGAIN never clears.
cc -O2 -o raw_aio_eagain raw_aio_eagain.c
ulimit -n 100000
./raw_aio_eagain 50 # or 1000, 10000 = seastar's default
Example output (kernel 6.17, 32 possible CPUs):
io_setup(50): kernel ring capacity (ring->nr) = 383
io_submit -> EAGAIN after 382 in-flight poll iocbs (ring capacity 383)
retrying for 1.5s (like aio_general_context::flush)...
...still EAGAIN after 7081409 attempts over 1.5s -- this is where seastar's
SEASTAR_ASSERT(clock::now() < retry_until) would abort
Measured ring sizes (io_setup(nr) → max(nr, 4*num_possible_cpus) * 2 + 2,
rounded up to page granularity, fs/aio.c ioctx_alloc/aio_setup_ring):
| requested nr | ring capacity | EAGAIN at (in-flight iocbs) |
|---|---|---|
| 50 | 383 | 382 |
| 1000 | 2047 | 2040 |
| 10000 (seastar default) | 20095 | 19819 |
Opens N idle loopback connections in one shard; each connection parks two
never-completing POLLIN polls (client fd + server fd). Once the parked polls
exhaust the ring, flush() spins on EAGAIN for 1 s and aborts.
clang++ -o aio_eagain_repro aio_eagain_repro.cc \
$(pkg-config --cflags --libs /path/to/seastar/build/dev/seastar.pc)
ulimit -n 100000
./aio_eagain_repro --smp 1 --reactor-backend linux-aio \
--max-networking-io-control-blocks 50 --conns 2000
Expected (crash while opening connection ~190 = (382 usable slots − 3 housekeeping polls) / 2 per connection):
opened 150 connections (302 parked polls)
src/core/reactor_backend.cc:462: size_t seastar::aio_general_context::flush():
Assertion `clock::now() < retry_until` failed.
Aborting on shard 0 ...
With --max-networking-io-control-blocks 1000 it dies at ~1019 connections
(2046 usable slots), and with the default 10000 it would take ~10k idle
connections per shard — the Redpanda field-crash scenario.