]> git.ipfire.org Git - thirdparty/systemd.git/log
thirdparty/systemd.git
8 days agonsresourced: Add more debug logging 42224/head
Daan De Meyer [Thu, 21 May 2026 08:37:56 +0000 (08:37 +0000)] 
nsresourced: Add more debug logging

8 days agonsresourced: Verify user namespace identity on registry lookup
Daan De Meyer [Thu, 21 May 2026 11:34:44 +0000 (11:34 +0000)] 
nsresourced: Verify user namespace identity on registry lookup

When a user namespace dies and its registry entry is torn down, the kernel
can recycle its inode number for a freshly-created namespace. A subsequent
registration or operation request can therefore find a stale registry entry
keyed by the same inode that actually belongs to a different, now-dead user
namespace.

Use NS_GET_ID to compare the kernel-assigned namespace identifier against
the stored one whenever we look up the registry from a live userns fd
(AddMount/AddControlGroup/AddNetworkInterface, plus the two registration
paths). Extract release_userns_by_info()/release_userns_by_inode() into
userns-registry.c so nsresourcework can fully clean up stale entries
(BPF allowlist, fdstore fd, cgroups, netifs, on-disk record) before reusing
the slot, and remove the now-unused userns_registry_inode_exists().

Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
8 days agosd-dhcp-relay: reimplement DHCP relay agent and drop legacy DHCP relay mode from...
Daan De Meyer [Thu, 21 May 2026 14:23:44 +0000 (16:23 +0200)] 
sd-dhcp-relay: reimplement DHCP relay agent and drop legacy DHCP relay mode from sd-dhcp-server (#42130)

8 days agoIntroduce support for running code in fibers (#39771)
Daan De Meyer [Thu, 21 May 2026 14:21:13 +0000 (16:21 +0200)] 
Introduce support for running code in fibers (#39771)

Traditionally, asynchronous programming in systemd has been achieved
using
sd-event along with the asynchronous interfaces of sd-bus and
sd-varlink.
This works well when the system is reacting to events and all code
triggered
by those events can run without blocking. In these scenarios, the global
Manager object is passed as userdata to the callback, and the callback
can
use the stack as usual, declaring local state and ensuring proper
cleanup via
_cleanup_. Control flow structures, such as loops, work as expected, and
everything runs smoothly.

However, challenges arise when the code needs to perform long-running
operations within these callbacks. Since the system cannot block
execution
within the callback, we can't directly invoke a long-running operation
and
wait for its result without introducing complexities. Instead, we need
to
initiate the long-running task, register for completion with sd-event,
sd-bus, or sd-varlink, and provide a callback to be invoked when the
operation completes.

This callback, however, only receives a single userdata pointer, which
forces us to bundle all local variables into a struct and pass it along
as
part of the callback. On top of that, after queuing the asynchronous
operation, the caller continues executing. As the caller's stack unwinds
when the function exits, the resources and state within the local scope
may
be prematurely cleaned up. Therefore, the struct must store copies of
the
local variables or ensure proper reference counting to prevent premature
resource cleanup.

When multiple long-running operations need to be initiated within a
loop,
the complexity grows further. We must introduce additional shared state
to
track the completion of all operations before we can run any code that
depends on their results.

Furthermore, since the daemon may be shut down at any time, we must
track
the lifecycle of each long-running operation in the global Manager
struct,
ensuring proper cleanup even when stack unwinding can no longer manage
the
resources for us.

Fibers, or green threads, provide a more natural way of handling
asynchronous operations. By enabling cooperative multitasking within a
single thread, fibers allow us to write code that looks like it’s
running
synchronously, but with the ability to yield control at predefined
points,
such as when waiting for long-running tasks to complete.

With fibers, we can simplify the control flow by running asynchronous
operations within a fiber, allowing us to "pause" execution while
waiting
for the long-running operation to finish and then "resume" the operation
once
it's complete. This eliminates the need for multiple callback chains,
extensive state tracking, and the potential pitfalls of stack unwinding.

This commit introduces the ability to execute long-running operations in
a
non-blocking manner while maintaining the simplicity and readability of
synchronous code. The fiber-based approach will significantly improve
the
handling of complex workflows, making the code easier to write and
maintain.

The implementation is based on ucontext.h's makecontext() (with a
fallback
to the venerable sigaltstack() approach on musl),
sigsetjmp()/siglongjmp()
and sd-event. ucontext.h provides us with alternate stacks that we can
switch
between. We use sigsetjmp()/siglongjmp() instead of swapcontext()
because the
latter forcibly saves/restores a per context signal mask every time it
is called.
Using sigsetjmp()/siglongjmp(), we can avoid the unnecessary syscall and
maintain
a per thread signal mask, which makes much more sense than having a per
fiber
signal mask.

The default stack size is the same as a regular thread. Because we
use mmap() to allocate the stack, the memory won't actually be used
until it
is paged in by the kernel, so we don't actually use 8MB per fiber.

To integrate fibers with the event loop, each fiber is assigned a
deferred
event source which resumes the fiber when enabled. The deferred event
source
is oneshot by default so the fiber will run immediately until it yields
or
suspends. If it yields, the deferred event source is enabled again
(oneshot)
immediately. If it suspends, before it suspends, one or more event
sources
are registered with sd-event that will enable the deferred event source
(oneshot) to resume the fiber once the operation it is waiting for
completes.

Yielding or suspending the fiber is done by calling sd_fiber_yield() or
sd_fiber_suspend() respectively. Both of these return zero on success or
any
error value from the async operation that caused the fiber to resume.

This is also how fiber cancellation is implemented. When a fiber is
cancelled,
sd_fiber_yield() and sd_fiber_suspend() will return ECANCELED when the
fiber
is resumed, allowing the fiber to unwind its stack (which allows cleanup
to
happen automatically) and finish.

Instead of having applications work directly with fibers, we hide them
behind
a generic futures interface to represent long-running operations,
regardless of
whether those operations are running on a fiber or not. Aside from
fibers, the
futures library (sd-future) will for example allow waiting for sd-event
sources
and doing sd-bus calls in the background as well. Fibers can suspend
until a
future is ready with sd_fiber_await() or by having the future wake up
the fiber
explicitly in its callback. A future always defaults to waking up the
current
fiber.

Each future kind plugs into the library by providing an sd_future_ops
vtable
(alloc, free, cancel, set_priority). The library treats the impl pointer
returned by alloc() as a black box. Future Implementations retrieve it
via
sd_future_get_private().

A future starts in SD_FUTURE_PENDING and transitions exactly once to
SD_FUTURE_RESOLVED, carrying an integer result. Consumers can react to
that
transition either by installing a one-shot callback with
sd_future_set_callback() (callback-style code) or by waiting on it from
a
fiber via sd_fiber_await() (synchronous-looking fiber code).
sd_fiber_await()
is itself built on a "wait future" that resolves when its target
resolves;
sd_future_new_wait() exposes the same primitive directly so non-fiber
callers
can chain futures without involving a fiber.

Cancellation is cooperative: sd_future_cancel() invokes the future
impl's
cancel callback, which is responsible for tearing down its work and
ultimately
resolving the promise with -ECANCELED. For fiber futures this is what
surfaces as the ECANCELED return from
sd_fiber_yield()/sd_fiber_suspend()
mentioned above.

Fire-and-forget fibers — created by passing a NULL ret to sd_fiber_new()

take a self-reference on their future so they outlive the caller's
scope.
The self-ref is dropped when the fiber resolves. This floating mechanism
(sd_fiber_set_floating()) is restricted to fiber futures because they
uniquely guarantee resolution; allowing it for arbitrary future kinds
would
risk silent leaks for kinds that may never resolve.

Note that fiber cleanup depends on the runtime operating normally. Each
fiber's _cleanup_-style cleanups live on the fiber's own stack and run
only when the fiber is resumed and allowed to unwind, which requires a
working event loop to drive it to completion. The exit event source
registered for top-level fibers ensures unwind on a normal
sd_event_exit(),
but if the event loop itself terminates abnormally (e.g. an
unrecoverable
allocation failure mid-dispatch) before all fibers have resolved, their
stacks never unwind and any resources they own leak.

The code lives in libsystemd as sd-future (not exported) for the
following reasons:
- We may want to make this a public libsystemd API in the future
- The code can't live in src/basic as it makes heavy use of sd-event
- The code can't live in src/shared as sd-bus and sd-event make use of
it

The log and log-context headers are updated with functions to allow
fibers to have their own log prefix and log context.

8 days agoupdatectl: Apply --no-ask-password argument to polkit agent
Philip Withnall [Thu, 21 May 2026 10:40:19 +0000 (11:40 +0100)] 
updatectl: Apply --no-ask-password argument to polkit agent

Accidentally missed out of commit
8c0f9073c7da808461acfc016cc291e4aba9c1a2.

Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: 8c0f9073c7da808461acfc016cc291e4aba9c1a2
8 days agoinclude/uapi: add linux/openat2.h (#42220)
Zbigniew Jędrzejewski-Szmek [Thu, 21 May 2026 11:29:28 +0000 (13:29 +0200)] 
include/uapi: add linux/openat2.h (#42220)

We include the header through src/include/override/fcntl.h, hence we should
have the latest copy of the header.

(E.g. compilation fails with musl-libc-1.2.5-6.fc44.x86_64 and older kernel
headers.)

8 days agocore: add FileDescriptorStorePreserve=on-success option (#42160)
Luca Boccassi [Thu, 21 May 2026 11:08:33 +0000 (12:08 +0100)] 
core: add FileDescriptorStorePreserve=on-success option (#42160)

Currently with FileDescriptorStorePreserve=yes the FD store is kept
around
regardless of what happens to a unit, which is useful in many cases. But
in
some cases, for example when complex services crash horribly, it's hard
to
reason about what was in the intermediate state, and it's better to
start
fresh.

Add a new 'on-success' option for the FileDescriptorStorePreserve=
setting
that keeps it around only for as long as the unit doesn't go to a
persistently
failed state.

This is especially useful in combination with LUO, where we don't want
to
keep around LUO sessions created by units that then proceeded to crash
and
burn, and might be in a bad state afterwards.

8 days agotest-btrfs: skip info test when GET_SUBVOL_INFO ioctl is unsupported
Daan De Meyer [Wed, 20 May 2026 20:46:40 +0000 (20:46 +0000)] 
test-btrfs: skip info test when GET_SUBVOL_INFO ioctl is unsupported

On 32-bit userspace running against a 64-bit kernel
BTRFS_IOC_GET_SUBVOL_INFO returns -ENOTTY: struct
btrfs_ioctl_get_subvol_info_args embeds four btrfs_ioctl_timespec
values, and that timespec struct (__u64 sec; __u32 nsec) packs to 12
bytes on i386 but 16 on x86_64 due to differing __u64 alignment.
sizeof(struct) is part of the ioctl cmd number via _IOR(), so the cmd
emitted by 32-bit userspace doesn't match the case label compiled by
the 64-bit kernel and the switch falls through to -ENOTTY.

btrfs already handles this exact class of bug for
BTRFS_IOC_SET_RECEIVED_SUBVOL via a btrfs_ioctl_timespec_32 struct
plus a _32 cmd alias in fs/btrfs/ioctl.c, but GET_SUBVOL_INFO (added
in 2018, four years after that fix) didn't get the same treatment.
Until a kernel patch lands the test can't exercise the ioctl on
32-bit, so convert TEST(info) to TEST_RET(info) and return
EXIT_TEST_SKIP with a clear message when -ENOTTY comes back. The
other tests in the file use ioctls that already have working compat
paths and remain unaffected.

8 days agonetwork: several fixlets for NDisc (#42218)
Luca Boccassi [Thu, 21 May 2026 11:05:34 +0000 (12:05 +0100)] 
network: several fixlets for NDisc (#42218)

Unfortunately, previously the path to test-ndisc-send has been wrong, so
some test cases have not been checked in our mkosi CIs. And two test
cases have been broken.

The test case `test_ndisc_redirect` was not updated when the logic in
networkd was changed by 9142bd5a8e9ed94ecbb1e335305e24760b90ad2a. The
change itself should be OK. So, the test case is updated.

The test case `test_ndisc_mtu` was broken when the commit
32417c172383847ec78b672c537594e3efe8f0e0 is merged. The commit is not
correct, as we cannot set IPv6 MTU larger than interface MTU. So, the
offending commit is reverted.

8 days agocore: better errors and more fields for io.systemd.Unit.StartTransient (#42161)
Daan De Meyer [Thu, 21 May 2026 11:03:29 +0000 (13:03 +0200)] 
core: better errors and more fields for io.systemd.Unit.StartTransient (#42161)

core: add User,Group,SupplementaryGroups,Nice to varlink
 Unit.StartTransient

This commit adds more writable fields to the
io.systemd.Unit.StartTransient
varlink method. With this its possible to set:
User,Group,SupplementaryGroups,Nice values.

Plus tests for them.

---

core: report unsupported service fields in varlink calls

Just like for the unsupported/bad exec_fields we should show
a message about what field is bad for service parameters. This
commit adds it using the same pattern. The JSON parser works in
fail-fast mode so we only display the first bad field (and
it depends on the parser what it finds first).

8 days agomath-util: round to declared FP precision consistently across architectures
Daan De Meyer [Wed, 20 May 2026 12:37:15 +0000 (12:37 +0000)] 
math-util: round to declared FP precision consistently across architectures

Add -fexcess-precision=standard so gcc inserts ISO C99 conformant
rounding at assignments, casts, and returns — without it, double values
on x87 happily stay at 80-bit extended precision across operations and
diverge from the SSE/x86_64 behavior, making strict equality comparisons
architecture-dependent.

The flag doesn't fully cover x87: per gcc PR#323
(https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323), a function return
value carried in ST(0) can arrive at the caller still at 80-bit, so a
double that ought to compare equal to a same-magnitude literal picks up
extra mantissa bits and doesn't. Wrap fp_equal in volatile-double
temporaries to force a memory roundtrip — the only operation that
reliably truncates on x87 — so its callers get consistent results
regardless of how the operands were produced.

Add a TEST(fp_equal) case that exercises the previously-broken pattern:
a runtime 1.0/10.0 computed inside a noinline helper, returned across
the function ABI boundary, then compared against the literal 0.1.
Without the volatile truncation this assertion fails on 32-bit gcc.

8 days agotest-qmp-client: run mock QMP servers as fibers on the shared event loop 39771/head
Daan De Meyer [Fri, 24 Apr 2026 09:49:10 +0000 (09:49 +0000)] 
test-qmp-client: run mock QMP servers as fibers on the shared event loop

The mock servers used to be driven out-of-band: each test created a
socketpair, forked a child, ran a hand-coded request/response script
against the raw fd, and sent SIGTERM to tear it down. That worked but
required pidref/process-util/signal plumbing in every test, two
distinct execution contexts that couldn't share state, and a JsonStream
attached to the mock side that pretended to be event-loop-driven while
actually being driven manually via blocking reads.

Now that JsonStream suspends when on a fiber, the mocks can live
inside the same process and event loop as the client. Each mock is
rewritten as an sd-fiber that runs alongside the client fiber: so the
mock fiber yields on I/O and the event loop schedules the client in the
meantime. Both sides progress cooperatively, no fork/SIGTERM/PID tracking,
no manual phase tracking.

Two cleanups fall out of the rewrite:

- A QMP_TEST(name, mock_fn) { ... } macro encapsulates the per-test
  scaffolding (event loop, socketpair, mock fiber spawn, exit-on-idle
  shim) and injects an already-connected QmpClient *client into the
  test body. Each test now reads as a flat sequence of
  qmp_client_call() invocations against that client.

- Repeated mock command/reply scripting is factored into
  mock_qmp_expect(), mock_qmp_reply(), mock_qmp_expect_and_reply(),
  mock_qmp_handshake(), and mock_qmp_query_status_running(). The
  greeting JSON is built with sd_json_buildo() instead of being parsed
  from a literal.

The file shrinks from 756 to 494 lines, mostly through deletions.

8 days agoqmp-client: add fiber-aware call paths
Daan De Meyer [Fri, 24 Apr 2026 09:49:02 +0000 (09:49 +0000)] 
qmp-client: add fiber-aware call paths

The synchronous qmp_client_call() pumps the event loop until its reply
arrives, pinning the parsed reply on c->current so it can hand out
borrowed pointers to the caller. That model only fits one in-flight
sync call: a second qmp_client_call() on the same client clears
c->current before issuing its own send, invalidating the first
caller's borrowed pointers. On a single-threaded event loop that was
fine, but with fibers two concurrent calls on the same client can
interleave through the pump (json_stream_wait() suspends the running
fiber) and trample each other.

To fix this, make qmp_client_call() detect when it's running on a fiber
whose event loop matches the client and transparently delegate to
qmp_client_call_suspend(), which makes use of a new QmpFuture to allow
multiple concurrent calls to qmp_client_call().

To make this work concurrently, we also change qmp_client_call() to
hand out references and copies of errors so that we don't have to store
the borrowed pointers we hand out in the QmpClient struct.

8 days agosd-varlink: make sd-varlink fiber-aware
Daan De Meyer [Tue, 14 Apr 2026 08:54:49 +0000 (08:54 +0000)] 
sd-varlink: make sd-varlink fiber-aware

Add varlink_server_bind_fiber() and varlink_server_bind_fiber_many()
in varlink-util.{c,h} for registering a method handler that should
run on a dedicated fiber per dispatch. The fiber-bound methods live
in a separate s->fiber_methods map alongside the regular s->methods;
bind_internal()/bind_many_internal() are factored out so the regular
and fiber bind variants share their parsing/insertion code.
Registering the same method in both maps is rejected because the
dispatcher consults the regular map first and would otherwise
silently shadow the fiber binding.

varlink_dispatch_fiber() builds a VarlinkFiberData (refs to the
connection, parameters, and method name), spawns a fiber via
sd_fiber_new(), and makes the future floating so the fiber
self-manages its lifetime — neither the dispatcher nor the
connection has to track it. The fiber's priority is set to one
below the connection's quit event source so that on graceful
shutdown the fiber's exit handler fires (and runs its cleanup)
before varlink's quit_callback() closes the connection underneath
it; this is what lets a fiber-bound handler reply or flush its
sentinel on a still-open connection during shutdown.

The connection state transitions are reordered so they happen before
the fiber spawn rather than after the synchronous callback returns:
the fiber runs after dispatch has already moved past PROCESSING, which
matches the behaviour expected for a deferred reply (the fiber may
either reply immediately, or stash the connection and reply later, in
which case the post-callback logic treats it as a PENDING_METHOD).

Note that all the synchronous varlink APIs (sd_varlink_call() and friends)
already behave properly when on a fiber because they call json_stream_wait()
which calls ppoll_usec() which we already fixed to suspend when called from
a fiber.

The client/server varlink tests are migrated to fibers (threads → mock
server fibers on the same event loop) to exercise the new paths.

8 days agosd-bus: make sd-bus fiber-aware
Daan De Meyer [Mon, 11 May 2026 14:27:34 +0000 (16:27 +0200)] 
sd-bus: make sd-bus fiber-aware

Two changes to teach sd-bus how to behave when called from a fiber, in
order of increasing depth:

2. sd_bus_call() now redirects to a new bus_call_suspend() helper when
   the caller is a fiber whose event loop is the same one the bus is
   attached to. The plain bus_poll() path serializes all bus traffic on
   the slot's reply (only one method call can be in flight per
   sd_bus*), which would defeat the point of running multiple fibers
   against one bus. bus_call_suspend() builds on the async sd-bus API:
   it wraps the call in a new BusFuture (sd-bus/bus-future.{c,h}) that
   resolves when the reply or method-error arrives, lets the fiber
   await that future, and surfaces the reply to the caller via
   future_get_bus_reply(). Because the futures live on the event loop
   rather than a per-bus slot, multiple fibers can drive concurrent
   method calls against the same bus.

3. A new private SD_BUS_VTABLE_METHOD_FIBER flag dispatches a vtable
   method handler on its own fiber, so handlers are free to use
   sd_bus_call() against the same bus, sd_fiber_sleep(), loop_read(),
   etc. without stalling the event loop for other connections or
   handlers. The flag stays out of sd-bus-vtable.h (its bit value is
   reserved there to prevent collisions) — the fiber runtime is a
   systemd-internal implementation detail.

Lifecycle of fiber-dispatched handlers is tracked on the bus itself: a
new bus->fiber_futures set holds a ref to each in-flight handler.
bus_enter_closing() cancels every entry and process_closing() returns
with the bus still in CLOSING state until the set drains, so we can be
sure no fiber handler outlives the bus. bus_fiber_resolved() removes
the entry on completion. bus_free()'s assert(set_isempty()) makes the
invariant load-bearing.

Note that plain sd_bus_call() already works correctly on a fiber as it
calls ppoll_usec() which has already been modified to suspend when
running on a fiber.

To exercise these changes the existing thread-based client/server
sd-bus tests (test-bus-chat, test-bus-objects, test-bus-peersockaddr,
test-bus-server, test-bus-watch-bind) are migrated to fibers, and a
new test-bus-fiber is added that covers SD_BUS_VTABLE_METHOD_FIBER —
including handlers that issue nested sd_bus_call() on the same bus, the
cancel-on-close path, and concurrent dispatches across multiple fibers.

8 days agosd-event: suspend instead of blocking when sd_event_run() runs on a fiber
Daan De Meyer [Mon, 23 Mar 2026 09:15:27 +0000 (10:15 +0100)] 
sd-event: suspend instead of blocking when sd_event_run() runs on a fiber

sd_event_run() blocks the calling thread on the event loop's epoll fd
until something happens. When the caller is a fiber, that's the wrong
behaviour: blocking the thread also stalls every other fiber and the
outer event loop driving them. The most common way to hit this is a
fiber that creates its own inner event loop (e.g. a server-style fiber
that wants to dispatch its own sources independently of whatever loop
the test or supervising fiber is running on) — with the existing
implementation the inner sd_event_run() would hold the thread while the
outer scheduler should be free to advance other fibers.

Add an event_run_suspend() variant in sd-event/event-future.c that
performs the same prepare/wait/dispatch dance, but when the fast path
finds nothing ready it (a) creates an IO future watching the inner
event loop's epoll fd on the *outer* event loop, (b) optionally creates
a time future for the timeout, and (c) suspends the fiber. When either
future fires the fiber is resumed and the prepare/wait/dispatch sequence
runs once more to actually dispatch what's pending. sd_event_run()
checks sd_fiber_is_running() and delegates to this variant when on a
fiber; profile_delays accounting is intentionally skipped on that path
since the underlying prepare/wait/dispatch primitives already account
for themselves.

8 days agosd-future: make src/basic blocking helpers fiber-aware
Daan De Meyer [Sat, 25 Apr 2026 20:06:54 +0000 (22:06 +0200)] 
sd-future: make src/basic blocking helpers fiber-aware

Some helpers in src/basic — ppoll_usec_full() (used by fd_wait_for_event()),
loop_read(), loop_read_exact(), loop_write_full() and
pidref_wait_for_terminate_full() — block the calling thread. That's the
right behaviour outside a fiber but not inside one, where blocking the
thread also stalls every other fiber running on the same event loop.
Rewriting every caller to pick a fiber or non-fiber variant explicitly
would be a lot of churn and would split otherwise-shared code paths in
two.

Instead, the helpers detect at runtime whether they're running on a fiber
and dispatch to a suspending variant when they are. FiberOps in
fiber-ops.h holds five function pointers (ppoll, read, write, timeout,
cancel_wait_unref); a fiber_ops global constant is populated whenever we
enter a fiber with functions that delegate to suspending variants of common
syscalls. With this approach, the variants themselves stay in libsystemd
which is required because they make use of sd-event.

- loop_read()/loop_read_exact() take the fiber read hook on a fiber
  unless the caller asked for a non-blocking attempt (do_poll=false) and
  the fd is already non-blocking — in that case we fall through to read()
  to preserve the existing return-EAGAIN-immediately semantic. The hook
  itself suspends on EAGAIN until data is available, so neither the
  do_poll knob nor the explicit fd_wait_for_event() retry loop are
  needed on the fiber path.

- loop_write_full() likewise takes the fiber write hook on a fiber,
  except when timeout=0 with an already-non-blocking fd (preserving the
  fast-return-EAGAIN semantic). The fiber path runs inside a
  FIBER_OPS_TIMEOUT() scope so the caller's timeout is honoured via a
  deadline future, mirroring SD_FIBER_TIMEOUT() but reachable from
  src/basic without pulling in sd-future.h.

- pidref_wait_for_terminate_full() polls the pidfd via fd_wait_for_event()
  before each waitid() when either a finite timeout is set or we're on a
  fiber, and requires pidref->fd >= 0 in those cases (returning
  -ENOMEDIUM otherwise — extending the rule that already applied to
  finite timeouts). The poll suspends the fiber via the ppoll hook above;
  the subsequent waitid() doesn't block because the pidfd is already
  signalled.

8 days agosd-future: add fiber-aware non-blocking I/O wrappers
Daan De Meyer [Sat, 25 Apr 2026 20:31:58 +0000 (22:31 +0200)] 
sd-future: add fiber-aware non-blocking I/O wrappers

Add a family of sd_fiber_*() I/O wrappers that, when called from a
fiber, behave like blocking I/O from the caller's perspective but
yield to the event loop instead of blocking the thread:

  sd_fiber_read / sd_fiber_write
  sd_fiber_readv / sd_fiber_writev
  sd_fiber_recv / sd_fiber_send
  sd_fiber_connect
  sd_fiber_recvmsg / sd_fiber_sendmsg
  sd_fiber_recvfrom / sd_fiber_sendto
  sd_fiber_accept
  sd_fiber_ppoll

Most of them share a single helper, fiber_io_operation(), which when
invoked outside a fiber falls through to the underlying syscall
directly, preserving the regular blocking behaviour. Inside a fiber
the helper flips the fd to non-blocking (restoring its original mode
on return), tries the syscall once on the fast path, and on EAGAIN/
EWOULDBLOCK creates an sd-event-backed IO future via future_new_io(),
suspends the fiber, and retries the syscall once the event source
fires.

future_new_io() itself is added to sd-event/event-future.{c,h} as a
new IoFuture kind. It wraps sd_event_add_io() into an sd_future:
oneshot enable, EPOLLERR translated via SO_ERROR (suppressed for
non-sockets), and the fd duplicated with F_DUPFD_CLOEXEC to avoid
EEXIST when multiple sources watch the same descriptor.

Together these let fiber-using code write straight-line socket and
pipe I/O without bundling state into callbacks.

8 days agoIntroduce support for running code in fibers
Daan De Meyer [Wed, 12 Nov 2025 16:53:47 +0000 (17:53 +0100)] 
Introduce support for running code in fibers

Traditionally, asynchronous programming in systemd has been achieved using
sd-event along with the asynchronous interfaces of sd-bus and sd-varlink.
This works well when the system is reacting to events and all code triggered
by those events can run without blocking. In these scenarios, the global
Manager object is passed as userdata to the callback, and the callback can
use the stack as usual, declaring local state and ensuring proper cleanup via
_cleanup_. Control flow structures, such as loops, work as expected, and
everything runs smoothly.

However, challenges arise when the code needs to perform long-running
operations within these callbacks. Since the system cannot block execution
within the callback, we can't directly invoke a long-running operation and
wait for its result without introducing complexities. Instead, we need to
initiate the long-running task, register for completion with sd-event,
sd-bus, or sd-varlink, and provide a callback to be invoked when the
operation completes.

This callback, however, only receives a single userdata pointer, which
forces us to bundle all local variables into a struct and pass it along as
part of the callback. On top of that, after queuing the asynchronous
operation, the caller continues executing. As the caller's stack unwinds
when the function exits, the resources and state within the local scope may
be prematurely cleaned up. Therefore, the struct must store copies of the
local variables or ensure proper reference counting to prevent premature
resource cleanup.

When multiple long-running operations need to be initiated within a loop,
the complexity grows further. We must introduce additional shared state to
track the completion of all operations before we can run any code that
depends on their results.

Furthermore, since the daemon may be shut down at any time, we must track
the lifecycle of each long-running operation in the global Manager struct,
ensuring proper cleanup even when stack unwinding can no longer manage the
resources for us.

Fibers, or green threads, provide a more natural way of handling
asynchronous operations. By enabling cooperative multitasking within a
single thread, fibers allow us to write code that looks like it’s running
synchronously, but with the ability to yield control at predefined points,
such as when waiting for long-running tasks to complete.

With fibers, we can simplify the control flow by running asynchronous
operations within a fiber, allowing us to "pause" execution while waiting
for the long-running operation to finish and then "resume" the operation once
it's complete. This eliminates the need for multiple callback chains,
extensive state tracking, and the potential pitfalls of stack unwinding.

This commit introduces the ability to execute long-running operations in a
non-blocking manner while maintaining the simplicity and readability of
synchronous code. The fiber-based approach will significantly improve the
handling of complex workflows, making the code easier to write and maintain.

The implementation is based on ucontext.h's makecontext() (with a fallback
to the venerable sigaltstack() approach on musl), sigsetjmp()/siglongjmp()
and sd-event. ucontext.h provides us with alternate stacks that we can switch
between. We use sigsetjmp()/siglongjmp() instead of swapcontext() because the
latter forcibly saves/restores a per context signal mask every time it is called.
Using sigsetjmp()/siglongjmp(), we can avoid the unnecessary syscall and maintain
a per thread signal mask, which makes much more sense than having a per fiber
signal mask.

The default stack size is the same as a regular thread. Because we
use mmap() to allocate the stack, the memory won't actually be used until it
is paged in by the kernel, so we don't actually use 8MB per fiber.

To integrate fibers with the event loop, each fiber is assigned a deferred
event source which resumes the fiber when enabled. The deferred event source
is oneshot by default so the fiber will run immediately until it yields or
suspends. If it yields, the deferred event source is enabled again (oneshot)
immediately. If it suspends, before it suspends, one or more event sources
are registered with sd-event that will enable the deferred event source
(oneshot) to resume the fiber once the operation it is waiting for completes.

Yielding or suspending the fiber is done by calling sd_fiber_yield() or
sd_fiber_suspend() respectively. Both of these return zero on success or any
error value from the async operation that caused the fiber to resume.

This is also how fiber cancellation is implemented. When a fiber is cancelled,
sd_fiber_yield() and sd_fiber_suspend() will return ECANCELED when the fiber
is resumed, allowing the fiber to unwind its stack (which allows cleanup to
happen automatically) and finish.

Instead of having applications work directly with fibers, we hide them behind
a generic futures interface to represent long-running operations, regardless of
whether those operations are running on a fiber or not. Aside from fibers, the
futures library (sd-future) will for example allow waiting for sd-event sources
and doing sd-bus calls in the background as well. Fibers can suspend until a
future is ready with sd_fiber_await() or by having the future wake up the fiber
explicitly in its callback. A future always defaults to waking up the current
fiber.

Each future kind plugs into the library by providing an sd_future_ops vtable
(alloc, free, cancel, set_priority). The library treats the impl pointer
returned by alloc() as a black box. Future Implementations retrieve it via
sd_future_get_private().

A future starts in SD_FUTURE_PENDING and transitions exactly once to
SD_FUTURE_RESOLVED, carrying an integer result. Consumers can react to that
transition either by installing a one-shot callback with
sd_future_set_callback() (callback-style code) or by waiting on it from a
fiber via sd_fiber_await() (synchronous-looking fiber code). sd_fiber_await()
is itself built on a "wait future" that resolves when its target resolves;
sd_future_new_wait() exposes the same primitive directly so non-fiber callers
can chain futures without involving a fiber.

Cancellation is cooperative: sd_future_cancel() invokes the future impl's
cancel callback, which is responsible for tearing down its work and ultimately
resolving the promise with -ECANCELED. For fiber futures this is what
surfaces as the ECANCELED return from sd_fiber_yield()/sd_fiber_suspend()
mentioned above.

Fire-and-forget fibers — created by passing a NULL ret to sd_fiber_new() —
take a self-reference on their future so they outlive the caller's scope.
The self-ref is dropped when the fiber resolves. This floating mechanism
(sd_fiber_set_floating()) is restricted to fiber futures because they
uniquely guarantee resolution; allowing it for arbitrary future kinds would
risk silent leaks for kinds that may never resolve.

Note that fiber cleanup depends on the runtime operating normally. Each
fiber's _cleanup_-style cleanups live on the fiber's own stack and run
only when the fiber is resumed and allowed to unwind, which requires a
working event loop to drive it to completion. The exit event source
registered for top-level fibers ensures unwind on a normal sd_event_exit(),
but if the event loop itself terminates abnormally (e.g. an unrecoverable
allocation failure mid-dispatch) before all fibers have resolved, their
stacks never unwind and any resources they own leak.

The code lives in libsystemd as sd-future (not exported) for the following reasons:
- We may want to make this a public libsystemd API in the future
- The code can't live in src/basic as it makes heavy use of sd-event
- The code can't live in src/shared as sd-bus and sd-event make use of it

The log and log-context headers are updated with functions to allow
fibers to have their own log prefix and log context.

8 days agodocs: extend credentials docs with notes about imds
Lennart Poettering [Thu, 21 May 2026 08:11:53 +0000 (10:11 +0200)] 
docs: extend credentials docs with notes about imds

8 days agocore: add FileDescriptorStorePreserve=on-success option 42160/head
Luca Boccassi [Mon, 18 May 2026 14:47:32 +0000 (15:47 +0100)] 
core: add FileDescriptorStorePreserve=on-success option

Currently with FileDescriptorStorePreserve=yes the FD store is kept around
regardless of what happens to a unit, which is useful in many cases. But in
some cases, for example when complex services crash horribly, it's hard to
reason about what was in the intermediate state, and it's better to start
fresh.

Add a new 'on-success' option for the FileDescriptorStorePreserve= setting
that keeps it around only for as long as the unit doesn't go to a persistently
failed state.

This is especially useful in combination with LUO, where we don't want to
keep around LUO sessions created by units that then proceeded to crash and
burn, and might be in a bad state afterwards.

8 days agotest: avoid test-fdstore failing units when exiting
Luca Boccassi [Mon, 18 May 2026 14:49:23 +0000 (15:49 +0100)] 
test: avoid test-fdstore failing units when exiting

This is used as payload, so ensure the wrapping unit/container
doesn't register a failure when this exits

Follow-up for 9de91f0f4c715b278637af8b73cabac892d7e000

8 days agocleanup: VLAN Id -> VLAN ID
Sebastian Bernardt [Sat, 18 Apr 2026 11:31:04 +0000 (21:31 +1000)] 
cleanup: VLAN Id -> VLAN ID

"VLAN ID" is used throughout the documentation and codebase, but appears
(when present) in `networkctl status` tables as `VLan Id`.

8 days agonetwork: restart DHCPv6, NDisc, and RADV when tracked IPv6LL is dropped
Aritra Basu [Tue, 21 Apr 2026 23:09:20 +0000 (19:09 -0400)] 
network: restart DHCPv6, NDisc, and RADV when tracked IPv6LL is dropped

When the tracked IPv6 link-local address is removed, networkd clears
link->ipv6ll_address, but keeps DHCPv6, NDisc, and RADV running. These
engines keep using a stale source identity which affects the following:
- DHCPv6 client continues to send Solicit/Renew/Rebind from a nonexistent
  source address.
- NDisc continues to send Router Solicitations from a nonexistent source
  address. Router Advertisements cannot be received properly.
- RADV continues to advertise with a stale source address. This can lead
  to downstream hosts configuring invalid routes.
- DHCP-PD prefixes remain configured without a valid upstream DHCPv6 path.

Added link_ipv6ll_lost() to stop IPv6 dynamic engines and related states:
- sd_dhcp6_client_stop()
- ndisc_stop() + ndisc_flush()
- sd_radv_stop()

This is called from address_drop() when the dropped address matches the
tracked IPv6LL. After clearing the tracked address, it scans for another
ready link-local address on the interface. If found, this is set as
link->ipv6ll_address and link_ipv6ll_gained() is called to restart the
engines with the new source identity.

This resolves the FIXME in address_drop().

Signed-off-by: Aritra Basu <aritrbas+gh@cisco.com>
8 days agoNEWS: mention new DHCP relay agent 42130/head
Yu Watanabe [Tue, 12 May 2026 06:15:08 +0000 (15:15 +0900)] 
NEWS: mention new DHCP relay agent

8 days agosd-dhcp-server: drop legacy DHCP relay mode
Yu Watanabe [Sun, 3 May 2026 23:43:41 +0000 (08:43 +0900)] 
sd-dhcp-server: drop legacy DHCP relay mode

8 days agonetwork: drop use of legacy DHCP relay agent in sd-dhcp-server
Yu Watanabe [Sun, 3 May 2026 23:29:51 +0000 (08:29 +0900)] 
network: drop use of legacy DHCP relay agent in sd-dhcp-server

Then, this deprecates
- BindToInterface=
- RelayTarget=
- RelayAgentCircuitId=
- RelayAgentRemoteId=
settings in [DHCPServer] section.
These are gracefully translated as new settings.

8 days agoman: document DHCP relay configuration
Yu Watanabe [Sun, 10 May 2026 23:56:40 +0000 (08:56 +0900)] 
man: document DHCP relay configuration

8 days agotest-network: add test case for DHCP relay
Yu Watanabe [Sun, 10 May 2026 22:48:27 +0000 (07:48 +0900)] 
test-network: add test case for DHCP relay

8 days agonetwork: re-implement DHCP relay support with new sd-dhcp-relay
Yu Watanabe [Sun, 3 May 2026 00:56:45 +0000 (09:56 +0900)] 
network: re-implement DHCP relay support with new sd-dhcp-relay

8 days agofuzz: introduce fuzzer for DHCP relay agent
Yu Watanabe [Sat, 2 May 2026 14:32:33 +0000 (23:32 +0900)] 
fuzz: introduce fuzzer for DHCP relay agent

8 days agotest: add unit test for DHCP relay agent
Yu Watanabe [Sat, 2 May 2026 14:18:32 +0000 (23:18 +0900)] 
test: add unit test for DHCP relay agent

8 days agosd-dhcp-relay: introduce new DHCP relay agent
Yu Watanabe [Tue, 28 Apr 2026 04:27:43 +0000 (13:27 +0900)] 
sd-dhcp-relay: introduce new DHCP relay agent

Previously, sd-dhcp-server can be run as a DHCP relay agent.
But, DHCP server and DHCP relay agent behave completely differently,
hence there is almost no code that can be shared between the two modes.
Let's split out the DHCP relay agent feature from sd-dhcp-server.

The new DHCP relay agent supports:
- multiple upstream and downstream interfaces,
- gateway address (giaddr field in DHCP message header) is configurable,
- supports more DHCP relay agent information sub-options,
- each interface has their own socket fd, and each socket is bound to
  the interface, so that we can enable/disable each interface
  safely without affecting other interfaces, and we can filter out any
  unexpected packets from unmanaged interfaces,

networkd integration and test cases will be added later.

8 days agologind: keep lingering users at startup-time GC
Rocker Zhang [Sat, 16 May 2026 05:07:56 +0000 (13:07 +0800)] 
logind: keep lingering users at startup-time GC

manager_startup() runs manager_gc(m, /* drop_not_started= */ false)
before the user_start() loop. user_may_gc()'s linger guard requires
user_unit_active() to be true to keep a user, but at this point the
per-user units have not been started yet, so for any lingering user
that ended up in the user_gc_queue the guard falls through and
manager_gc frees the User struct before user_start() ever runs.

This only manifests after `systemctl soft-reboot`, because /run is
tmpfs and survives soft-reboot: /run/systemd/users/UID files persist,
and manager_enumerate_users() in src/login/logind.c explicitly calls
user_add_to_gc_queue() for every UID it loads from there. Cold boot is
unaffected because /run is empty, so the linger users that come in via
manager_enumerate_linger_users() never enter the GC queue at all and
reach user_start() directly.

Special-case the startup-time GC: if a linger file exists, keep the
user regardless of unit state — user_start() is about to run and will
queue the appropriate jobs. Steady-state GC (drop_not_started=true, in
the event loop) still requires user_unit_active() so we don't hold on
to records for lingering users whose units genuinely died.

Fixes: https://github.com/systemd/systemd/issues/41789
Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
8 days agovmspawn: add shell completion for --coco
Paul Meyer [Thu, 21 May 2026 06:07:51 +0000 (08:07 +0200)] 
vmspawn: add shell completion for --coco

Fixes 4f8215add24ac9018fb68399f0b957cf0eb3b0c6.

Signed-off-by: Paul Meyer <katexochen0@gmail.com>
8 days agotree-wide: standardize header names across src/fundamental, src/basic and src/shared
Daan De Meyer [Mon, 18 May 2026 20:29:04 +0000 (20:29 +0000)] 
tree-wide: standardize header names across src/fundamental, src/basic and src/shared

Drop the -fundamental suffix from src/fundamental/ headers in favor of names
that match their src/basic/ or src/shared/ counterparts (e.g.
macro-fundamental.h -> macro.h, assert-fundamental.h -> assert-util.h,
cleanup-fundamental.h -> cleanup-util.h). Rename src/basic/{btrfs,label}.{c,h}
to use the -util suffix to match the existing shared/btrfs-util and
shared/label-util siblings. Rename src/shared/mkdir-label.{c,h} to mkdir.{c,h}
and src/shared/tmpfile-util-label.{c,h} to tmpfile-util.{c,h} to match the
corresponding src/basic names.

This saves us from having to come up with separate names for files that do
the same thing across tiers, and it makes it easier to move stuff between
src/fundamental, src/basic and src/shared: consumers just #include "foo.h"
and pick up whichever tier their -I path resolves to first, so call sites
don't need to be updated when an API moves between layers.

Where a higher-tier wrapper exists (e.g. src/basic/macro.h wrapping
src/fundamental/macro.h), the wrapper uses an explicit "../fundamental/foo.h"
or "../basic/foo.h" relative include for the lower-tier header. We can't use
GCC's #include_next directive for this — when the wrapper is reachable both
via same-dir-as-source lookup and via -I (e.g. -Isrc/shared) for the
directory it lives in, #include_next advances by exactly one slot in libcpp's
internal directory chain and lands on the same physical directory it was
already in, never reaching the lower-tier sibling (see make_cpp_dir() in
gcc/libcpp/files.cc:1986).

To make sure the right headers are always picked up, the include directories
are reordered so that e.g. src/shared always takes priority over src/basic and
similar for the other directories.

Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
8 days agotest-network: follow-up for modem manager test and add test case for networkctl dhcp...
Yu Watanabe [Thu, 21 May 2026 00:10:30 +0000 (09:10 +0900)] 
test-network: follow-up for modem manager test and add test case for networkctl dhcp-lease (#42203)

8 days agofirstboot: make clear where the full screen console wizards end
Lennart Poettering [Wed, 20 May 2026 21:17:11 +0000 (23:17 +0200)] 
firstboot: make clear where the full screen console wizards end

The three first boot screens are visually separated from the console
output before them via the "chome" and welcome strings and sufficient
whitespace. But so far they weren't from the output after them. This is
sometimes a big confusing. Let's add a bit of a separator between the
end and what comes next, too.

Just cosmetics, nothing else.

8 days agovmspawn: rearrange --help and man page a bit (#42200)
Yu Watanabe [Thu, 21 May 2026 00:07:34 +0000 (09:07 +0900)] 
vmspawn: rearrange --help and man page a bit (#42200)

This does not change man page or --help contents at all, but it does
introduce new sections, and moves some knobs between sections. It also
reorders things in the man page so that man page and --help show items
in the same order again.

8 days agoreport: use new --help formatting calls
Lennart Poettering [Wed, 20 May 2026 13:45:30 +0000 (15:45 +0200)] 
report: use new --help formatting calls

8 days agotest-network: fix path to test-ndisc-send 42218/head
Yu Watanabe [Wed, 20 May 2026 14:44:49 +0000 (23:44 +0900)] 
test-network: fix path to test-ndisc-send

Follow-up for 9dcdf16b25545d942b872cc0abdbb7c9a6b5f9f1.

Previously, test cases that use test-ndisc-send executable did not
tested in our mkosi CIs...

8 days agoRevert "network: fix max MTU check for IPv6 MTU adjustments"
Yu Watanabe [Wed, 20 May 2026 22:47:37 +0000 (07:47 +0900)] 
Revert "network: fix max MTU check for IPv6 MTU adjustments"

This reverts commit 32417c172383847ec78b672c537594e3efe8f0e0.

IPv6 MTU cannot be larger than the current interface MTU.
The previous behavior is correct.

8 days agotest-network: fix test case for Neighbor Announcement message handling
Yu Watanabe [Wed, 20 May 2026 22:34:43 +0000 (07:34 +0900)] 
test-network: fix test case for Neighbor Announcement message handling

After 9142bd5a8e9ed94ecbb1e335305e24760b90ad2a, when NA without router
flag is received, the corresponding redirect route and the default route
is removed, but the other routes are kept.

The corresponding test case was not updated by the commit, and the test
case has been unfortunately skipped...

This fixes the test case, and added more checks.

8 days agotest-network: add test cases for 'networkctl dhcp-lease' command 42203/head
Yu Watanabe [Wed, 20 May 2026 15:28:38 +0000 (00:28 +0900)] 
test-network: add test cases for 'networkctl dhcp-lease' command

Addresses the request:
https://github.com/systemd/systemd/pull/42137#pullrequestreview-4305664376

8 days agotest-network: try to stop test-modem-manager-mock.service only when necessary
Yu Watanabe [Wed, 20 May 2026 14:40:51 +0000 (23:40 +0900)] 
test-network: try to stop test-modem-manager-mock.service only when necessary

Otherwise, all test cases that does not create/start the service emits
the following error:
```
Failed to stop test-modem-manager-mock.service: Unit test-modem-manager-mock.service not loaded.
```

Moreover, without this change, extra 'systemctl daemon-reload' is triggered after
all test cases. That's super heavy, especially when the test is running on
sanitizers.

Follow-up for abe3d570f8006fca5138b2d5cfb4e8b530be02e5.

8 days agoinclude: update kernel headers from v7.1-rc4
Yu Watanabe [Wed, 20 May 2026 19:57:21 +0000 (04:57 +0900)] 
include: update kernel headers from v7.1-rc4

It seems there is no notable changes to us.

8 days agotest-network: use networkctl_status() helper function at one more place
Yu Watanabe [Wed, 20 May 2026 19:33:01 +0000 (04:33 +0900)] 
test-network: use networkctl_status() helper function at one more place

8 days agoBump minimum required version of musl to 1.2.6 (#42208)
Yu Watanabe [Wed, 20 May 2026 22:04:12 +0000 (07:04 +0900)] 
Bump minimum required version of musl to 1.2.6 (#42208)

musl v1.2.6 has been released on 2026-03-20, and alpine-edge already has it:
https://pkgs.alpinelinux.org/package/edge/main/x86_64/musl

8 days agomkosi: update debian commit reference to 3e1930512d1efee7e11b619a5f493b3229594a51
Luca Boccassi [Wed, 20 May 2026 20:18:35 +0000 (21:18 +0100)] 
mkosi: update debian commit reference to 3e1930512d1efee7e11b619a5f493b3229594a51

3e1930512d Downgrade dependency on dbus to recommends in sd-container
61d6ecf0a0 Conflict with sysuser-helper
fbc4646437 autopkgtest: add dependency on procps

8 days agoman: order --secure-boot= to the same location in man page as in --help for vmspawn 42200/head
Lennart Poettering [Wed, 20 May 2026 14:23:36 +0000 (16:23 +0200)] 
man: order --secure-boot= to the same location in man page as in --help for vmspawn

8 days agovmspawn: move --kernel=/--initrd= under the "Execution" section
Lennart Poettering [Wed, 20 May 2026 13:52:57 +0000 (15:52 +0200)] 
vmspawn: move --kernel=/--initrd= under the "Execution" section

This has little to do with host configuration (where it was so far), and
a lot with what being executed, let's move it over.

Note that --help and man page so far differed here quite a bit: the
former had the "Execution" section, the latter didn't. This creates it
in the man page, to bring the two back in sync.

8 days agovmspawn: split out networking related options into its own "Networking" section in...
Lennart Poettering [Wed, 20 May 2026 13:50:32 +0000 (15:50 +0200)] 
vmspawn: split out networking related options into its own "Networking" section in --help

8 days agovmspawn: split 'Integration' section into 'Logging' and 'SSH'
Lennart Poettering [Wed, 20 May 2026 13:48:44 +0000 (15:48 +0200)] 
vmspawn: split 'Integration' section into 'Logging' and 'SSH'

Each of the two has multiple options, hence let's split them out.

8 days agovmspawn: move image related knobs close to --image=
Lennart Poettering [Wed, 20 May 2026 13:45:57 +0000 (15:45 +0200)] 
vmspawn: move image related knobs close to --image=

(Also, arrange the order if the image options group in the man page to
the same as in the --help text)

8 days agoNEWS: mention bump of musl requirement 42208/head
Yu Watanabe [Wed, 20 May 2026 19:07:36 +0000 (04:07 +0900)] 
NEWS: mention bump of musl requirement

8 days agomusl: drop several statx definitions
Yu Watanabe [Wed, 20 May 2026 16:14:59 +0000 (01:14 +0900)] 
musl: drop several statx definitions

Those dropped definitions have been provided since musl v1.2.6.

8 days agomusl: drop renameat2() wrapper
Yu Watanabe [Wed, 20 May 2026 16:08:24 +0000 (01:08 +0900)] 
musl: drop renameat2() wrapper

musl provides renameat2() since v1.2.6:
https://git.musl-libc.org/cgit/musl/commit/?id=05ce67fea99ca09cd4b6625cff7aec9cc222dd5a

8 days agoREADME: bump required minimum version of musl to 1.2.6
Yu Watanabe [Wed, 20 May 2026 16:30:43 +0000 (01:30 +0900)] 
README: bump required minimum version of musl to 1.2.6

musl v1.2.6 has been released on 2026-03-20:
https://git.musl-libc.org/cgit/musl/commit/?id=9fa28ece75d8a2191de7c5bb53bed224c5947417

alpine/postmarketos already have musl v1.2.6 in the edge repository:
https://pkgs.alpinelinux.org/package/edge/main/x86_64/musl

Also, Yocto Project 6.0 (wrynose) already has v1.2.6.

Let's bump the requirement now.

8 days agoupdatectl: Add a --no-ask-password argument
Philip Withnall [Wed, 20 May 2026 16:15:00 +0000 (17:15 +0100)] 
updatectl: Add a --no-ask-password argument

While commit 83c1e8ff5f9 added support for interactive polkit
authentication in `updatectl`, some users might want to disable that for
some use cases; so add the standard `--no-ask-password` argument.

Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Helps: https://github.com/systemd/systemd/issues/37412

8 days agoCouple of rearragements for src/core/meson.build (#42207)
Yu Watanabe [Wed, 20 May 2026 19:18:14 +0000 (04:18 +0900)] 
Couple of rearragements for src/core/meson.build (#42207)

Net result is `import-creds.c` moving from libcore to systemd

8 days agoci: run the musl build & test under mkosi with a postmarketOS tools tree (#42171)
Daan De Meyer [Wed, 20 May 2026 19:03:14 +0000 (21:03 +0200)] 
ci: run the musl build & test under mkosi with a postmarketOS tools tree (#42171)

8 days agoinclude: move several missing definitions to musl
Yu Watanabe [Wed, 20 May 2026 15:54:26 +0000 (00:54 +0900)] 
include: move several missing definitions to musl

Those moved ones have been defined in glibc <= 2.34, and only
necessary when built with musl.

Follow-up for c8c1bcf1941047d1fe43d9827ad4826b4620297a.

8 days agofirstboot: auto-fill keymap/locale questions with data from firmware, if available...
Yu Watanabe [Wed, 20 May 2026 18:50:43 +0000 (03:50 +0900)] 
firstboot: auto-fill keymap/locale questions with data from firmware, if available (#42177)

Let's make the installer experience a tiny bit nicer, and prefill
everything with the data the firmware gives us.

8 days agodns-packet: bail out early if the packet is too short (#42189)
Yu Watanabe [Wed, 20 May 2026 18:43:18 +0000 (03:43 +0900)] 
dns-packet: bail out early if the packet is too short (#42189)

This should address the nit from Claude in
https://github.com/systemd/systemd/pull/42178#pullrequestreview-4320763076.

9 days agoci: run the musl build & test under mkosi with a postmarketOS tools tree 42171/head
Daan De Meyer [Tue, 19 May 2026 10:36:01 +0000 (10:36 +0000)] 
ci: run the musl build & test under mkosi with a postmarketOS tools tree

Drop the standalone Unit-tests (musl) workflow that ran on an Alpine sandbox
spun up by jirutka/setup-alpine, and merge it into unit-tests.yml as a new
build-musl job that provisions a postmarketOS tools tree via mkosi and runs
the meson build + test suite through 'mkosi box'. postmarketOS is musl-native,
so the musl-gcc / -idirafter /usr/include wrappers the Fedora tools tree
needed are gone; the linter.yml's own musl build step also goes away since
the unit-tests workflow now covers it (and tests it).

postmarketOS doesn't ship a downstream systemd packaging spec, so the new
tools tree config in mkosi.tools.conf/mkosi.conf.d/postmarketos.conf does not
set PrepareScripts and lists build deps manually. mkosi.sync now early-exits
when PKG_SUBDIR is unset so the missing pkgenv entry doesn't trip set -u.

Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
9 days agotest-path: Skip test when we can't create a cgroup
Daan De Meyer [Wed, 20 May 2026 17:21:13 +0000 (17:21 +0000)] 
test-path: Skip test when we can't create a cgroup

Instead of having CI runner specific checks, let's just
skip the test if we get EXIT_CGROUP which is what we get
when we can't create a cgroup. This makes the check work
independently of CI runner, and specifically also on github
actions.

9 days agomeson: move one source file from libcore to systemd array 42207/head
Luca Boccassi [Wed, 20 May 2026 17:15:52 +0000 (18:15 +0100)] 
meson: move one source file from libcore to systemd array

9 days agomeson: reorder core sources alphabetically
Luca Boccassi [Wed, 20 May 2026 16:32:08 +0000 (17:32 +0100)] 
meson: reorder core sources alphabetically

9 days agomeson: reorder core sources array
Luca Boccassi [Wed, 20 May 2026 16:30:31 +0000 (17:30 +0100)] 
meson: reorder core sources array

To hint that sources should be added to the lowest common denominator
first

9 days agotest-path: Migrate to test framework and macros
Daan De Meyer [Wed, 20 May 2026 13:30:28 +0000 (13:30 +0000)] 
test-path: Migrate to test framework and macros

- Also clean up the logging in check_states() to
  only log on state changes so it's less noisy.

9 days agotest-path: Fail earlier on start-limit-hit
Daan De Meyer [Wed, 20 May 2026 08:52:21 +0000 (08:52 +0000)] 
test-path: Fail earlier on start-limit-hit

9 days agomeson: Skip coccinelle suite by default
Daan De Meyer [Wed, 20 May 2026 07:18:41 +0000 (07:18 +0000)] 
meson: Skip coccinelle suite by default

It's not fully passing yet so disable it by default until it is.
clang-tidy follows the same model.

9 days agotest-ukify: Skip kernel images we can't access
Daan De Meyer [Wed, 20 May 2026 08:51:56 +0000 (08:51 +0000)] 
test-ukify: Skip kernel images we can't access

9 days agomkosi: update mkosi ref to be746d51bc90568b196951a60095ba87bf51ca8b
Daan De Meyer [Tue, 19 May 2026 21:07:10 +0000 (21:07 +0000)] 
mkosi: update mkosi ref to be746d51bc90568b196951a60095ba87bf51ca8b

be746d51bc Make full $PATH available when building tools tree

9 days agoptyfwd: Imply PTY_FORWARD_READ_ONLY if stdin isn't readable
Daan De Meyer [Wed, 20 May 2026 12:14:52 +0000 (12:14 +0000)] 
ptyfwd: Imply PTY_FORWARD_READ_ONLY if stdin isn't readable

if stdin is connected to a closed pipe or similar, imply
PTY_FORWARD_READ_ONLY so we don't even try to read from it
in the first place. Otherwise we'll immediately get a hangup
which will cause the forwarder to call sd_event_exit() and
shut down the event loop.

Debugged-by: Christian Brauner <brauner@kernel.org>
9 days agovmspawn: initial support for SEV-SNP guests (#42193)
Lennart Poettering [Wed, 20 May 2026 15:42:26 +0000 (17:42 +0200)] 
vmspawn: initial support for SEV-SNP guests  (#42193)

9 days agosystemd-udevd: configure NIC IRQs CPU affinity (#40304)
Yu Watanabe [Wed, 20 May 2026 15:39:47 +0000 (00:39 +0900)] 
systemd-udevd: configure NIC IRQs CPU affinity (#40304)

# Context

#40195 defines the initial proposal and the motivation behind this PR.

This PR introduces 3 new options for `.link` files `[Link]` section:
- `IRQAffinityPolicy=`
- `IRQAffinity=`
- `IRQAffinityNUMA=`

The purpose is to allow `systemd-udevd` to configure a NIC's IRQs
affinity to specific CPU(s).

`IRQAffinityPolicy=` supports two policies:
- `single`: assign all the NIC IRQs to CPU 0, or the first CPU in the
CPU set resulting from the union of `IRQAffinity=` and
`IRQAffinityNUMA=`.
- `spread`: assign all the NIC IRQs to all the CPUs (or the union of
`IRQAffinity=` and `IRQAffinityNUMA=` if defined) in a round-robin
fashion while optimizing for cache locally while spreading apart queues
on CPUs as much as possible.

Both `IRQAffinity=` and `IRQAffinityNUMA=` behaves as filters to reduce
the CPU set to assign IRQs to, and are only valid if
`IRQAffinityPolicy=` is defined.

# Spreading IRQs

This section describes the algorithm responsible for spreading IRQs over
different CPUs to maximize performance.

## 1. Discover CPU topology

Read from `/sys/devices/system/cpu/cpu*/topology` to identify:
- L3 cache domains (dies)
- Physical cores VS hyperthreads
- NUMA nodes
- Core ordering within each die

```
Example: Dual-socket server with 2 dies per socket, 4 cores per die

      ┌─────────────────────────────────────────────────────────────────┐
      │                         NUMA Node 0                             │
      │   ┌─────────────────────────┐   ┌─────────────────────────┐     │
      │   │   Die 0 (L3 Cache)      │   │   Die 1 (L3 Cache)      │     │
      │   │  ┌────┐┌────┐┌────┐┌────│   │  ┌────┐┌────┐┌────┐┌────│     │
      │   │  │ C0 ││ C1 ││ C2 ││ C3 │   │  │ C4 ││ C5 ││ C6 ││ C7 │     │
      │   │  │0,16││1,17││2,18││3,19│   │  │4,20││5,21││6,22││7,23│     │
      │   │  └────┘└────┘└────┘└────│   │  └────┘└────┘└────┘└────│     │
      │   └─────────────────────────┘   └─────────────────────────┘     │
      └─────────────────────────────────────────────────────────────────┘
      ┌─────────────────────────────────────────────────────────────────┐
      │                         NUMA Node 1                             │
      │   ┌─────────────────────────┐   ┌─────────────────────────┐     │
      │   │   Die 2 (L3 Cache)      │   │   Die 3 (L3 Cache)      │     │
      │   │  ┌────┐┌────┐┌────┐┌────│   │  ┌────┐┌────┐┌────┐┌────│     │
      │   │  │ C8 ││ C9 ││C10 ││C11 │   │  │C12 ││C13 ││C14 ││C15 │     │
      │   │  │8,24││9,25││10,2││11,2│   │  │12,2││13,2││14,3││15,3│     │
      │   │  └────┘└────┘└────┘└────│   │  └────┘└────┘└────┘└────│     │
      │   └─────────────────────────┘   └─────────────────────────┘     │
      └─────────────────────────────────────────────────────────────────┘
                  (Numbers show CPU IDs: first HT, second HT)
```

## 2. Filter the first hyperthread only

Use only the first hyperthread of each physical core to avoid SMT
contention. Two IRQs on sibling HTs contend for ALU/cache without cache
benefit.

```
      Before: CPUs 0-31 (16 cores × 2 HTs)
      After:  CPUs 0-15 (first HT of each core)
```

## 3. Equidistant permutations

Reorder dies and CPUs so consecutive selections are maximally spread
apart.

```
        Original order:  [0, 1, 2, 3]          (adjacent dies/CPUs)
                            |
                            v
        Equidistant:     [0, 2, 1, 3]          (spread apart)
```

This ensures that even if only 2 IRQs are assigned, they land on dies 0
and 2 (not 0 and 1), maximizing physical distance. The permutation is
also applied within each die:

```
        Die permutation:   Die0 -> Die2 -> Die1 -> Die3

        Within each die:
        ┌───────────────────────────────────────────────┐
        │ Die 0: [C0,C1,C2,C3]     -> [C0,C2,C1,C3]     │
        │ Die 1: [C4,C5,C6,C7]     -> [C4,C6,C5,C7]     │
        │ Die 2: [C8,C9,C10,C11]   -> [C8,C10,C9,C11]   │
        │ Die 3: [C12,C13,C14,C15] -> [C12,C14,C13,C15] │
        └───────────────────────────────────────────────┘
```

## 4. Round-robin selection across dies

Pick one CPU from each die in rotation, following permuted order.

```

      Round 1:     Die0->C0   Die2->C8   Die1->C4   Die3->C12
                      |          |          |          |
                      v          v          v          v
      IRQs:         [IRQ0]     [IRQ1]     [IRQ2]     [IRQ3]

      Round 2:     Die0->C2   Die2->C10  Die1->C6   Die3->C14
                      |          |          |          |
                      v          v          v          v
      IRQs:         [IRQ4]     [IRQ5]     [IRQ6]     [IRQ7]
```

If there are more IRQs than physical cores, this logic wraps around and
reuse CPUs. Only the first hyperthread of each core is used to avoid
cache line contention between queues.

Closes #40195.

9 days agoRevert "meson: shrink developer-mode build artifacts"
Luca Boccassi [Wed, 20 May 2026 14:51:17 +0000 (15:51 +0100)] 
Revert "meson: shrink developer-mode build artifacts"

This reverts commit 68910161491cdd161bff29a32032e52301831164.

9 days agoupdate TODO 42177/head
Lennart Poettering [Tue, 19 May 2026 13:54:48 +0000 (15:54 +0200)] 
update TODO

9 days agofirstboot: port help() to help-util.[ch] APIs
Lennart Poettering [Wed, 20 May 2026 14:34:13 +0000 (16:34 +0200)] 
firstboot: port help() to help-util.[ch] APIs

9 days agofirstboot: prefill language prompt with firmware language if it makes
Lennart Poettering [Tue, 19 May 2026 13:50:39 +0000 (15:50 +0200)] 
firstboot: prefill language prompt with firmware language if it makes
sense

9 days agobootctl: show platform lang in bootctl status output
Lennart Poettering [Tue, 19 May 2026 13:50:35 +0000 (15:50 +0200)] 
bootctl: show platform lang in bootctl status output

9 days agolocale-setup: pick up platform lang from firmware
Lennart Poettering [Tue, 19 May 2026 13:50:20 +0000 (15:50 +0200)] 
locale-setup: pick up platform lang from firmware

9 days agosysinstall: prefill firmware variable question with 'yes'
Lennart Poettering [Tue, 19 May 2026 13:08:46 +0000 (15:08 +0200)] 
sysinstall: prefill firmware variable question with 'yes'

9 days agofirstboot: prefill keymap question with EFI provided info
Lennart Poettering [Tue, 19 May 2026 13:07:32 +0000 (15:07 +0200)] 
firstboot: prefill keymap question with EFI provided info

9 days agoask-string: support prefilling a string query with an explicit string
Lennart Poettering [Tue, 19 May 2026 12:59:38 +0000 (14:59 +0200)] 
ask-string: support prefilling a string query with an explicit string

9 days agovconsole-util: move code to read EFI keyboard layout into generic code
Lennart Poettering [Tue, 19 May 2026 12:51:41 +0000 (14:51 +0200)] 
vconsole-util: move code to read EFI keyboard layout into generic code

9 days agobasic/math-util: drop libm where possible
Daan De Meyer [Fri, 15 May 2026 14:59:49 +0000 (14:59 +0000)] 
basic/math-util: drop libm where possible

- test-random-util is reworked to not use sqrt()
- pretty-print.c inlines ceil() so libm doesn't have
  to be linked into libshared
- We add fno-math-errno to allow inlining of more math
  functions by not requiring standard math functions to
  set errno on invalid input.

9 days agomeson: shrink developer-mode build artifacts
Daan De Meyer [Fri, 15 May 2026 20:46:54 +0000 (20:46 +0000)] 
meson: shrink developer-mode build artifacts

Two complementary changes in the developer-mode branch of meson.build:

  1. -ffunction-sections -fdata-sections: pair with the existing
     -Wl,--gc-sections so the linker can drop unused individual functions
     and data instead of being forced to pull whole .o files into each
     binary. Biggest impact on statically-linked NSS/PAM modules (a single
     call into creds-util.c used to drag in the entire creds-util
     translation unit, which transitively pulled TPM2, OpenSSL, PKCS11 and
     KDF helpers via tpm2-util.c / openssl-util.c) and on tests that embed
     daemon objects via meson's objects: extraction.

  2. -gz=zstd + -fdebug-types-section + -Wl,--compress-debug-sections=zstd:
     compress every .debug_* section with zstd, and move type DIEs into a
     COMDAT-mergeable section so identical types described across many TUs
     land once. Both are transparent to GDB / readelf / addr2line.

Gated to mode == 'developer' for now: no major distro (Fedora, Debian/
Ubuntu, Arch, Alpine, Gentoo, openSUSE, Yocto) enables -ffunction-sections
in their system-wide default CFLAGS, and the interaction with -flto=auto +
-ffat-lto-objects (which Fedora et al. ship by default) deserves a broader
evaluation before turning it on for release builds. Developer mode benefits
straightforwardly: smaller plugins, smaller tests, smaller libraries, no
interference with the hardening/LTO flag combinations distros pin.

Size impact on a clean developer-mode build, 626 ELF objects:

| Category                   |   n |      Before |       After |               Δ |     % |
| -------------------------- | --: | ----------: | ----------: | --------------: | ----: |
| NSS plugins                |   4 |  22,163,272 |   6,681,120 |    −15,482,152  | −69.9 |
| PAM plugins                |   3 |  18,132,160 |   5,764,880 |    −12,367,280  | −68.2 |
| libsystemd.so (public ABI) |   1 |   6,009,360 |   3,350,168 |     −2,659,192  | −44.3 |
| libudev.so (public ABI)    |   1 |   4,379,336 |   1,441,256 |     −2,938,080  | −67.1 |
| libsystemd-shared-261.so   |   1 |  15,130,264 |  11,293,952 |     −3,836,312  | −25.4 |
| libsystemd-core-261.so     |   1 |   7,356,408 |   4,992,600 |     −2,363,808  | −32.1 |
| cryptsetup-token plugins   |   3 |     139,800 |     113,344 |        −26,456  | −18.9 |
| daemons / CLI tools        | 178 |  43,581,288 |  31,911,368 |    −11,669,920  | −26.8 |
| test binaries              | 386 | 124,717,072 |  60,185,904 |    −64,531,168  | −51.7 |
| fuzzers                    |  48 |  33,138,056 |  13,864,952 |    −19,273,104  | −58.2 |
| **TOTAL**                  | 626 | 274,747,016 | 139,599,544 | **−135,147,472** | **−49.2** |

Biggest individual wins:

| Binary                   |   Before |    After |      Δ |
| ------------------------ | -------: | -------: | -----: |
| test-networkd-address    |  6.21 MB |  0.82 MB | −86.8% |
| test-network-tables      |  6.23 MB |  0.85 MB | −86.4% |
| test-networkd-conf       |  6.27 MB |  1.32 MB | −78.9% |
| libnss_myhostname.so.2   |  6.40 MB |  1.53 MB | −76.1% |
| fuzz-netdev-parser       |  6.22 MB |  1.51 MB | −75.7% |
| fuzz-network-parser      |  6.22 MB |  1.70 MB | −72.6% |
| libnss_systemd.so.2      |  6.90 MB |  2.04 MB | −70.4% |
| libnss_resolve.so.2      |  4.42 MB |  1.38 MB | −68.8% |
| pam_systemd_home.so      |  6.91 MB |  2.22 MB | −67.9% |
| libudev.so.1.7.13        |  4.18 MB |  1.37 MB | −67.1% |
| pam_systemd.so           |  7.02 MB |  2.55 MB | −63.6% |
| libsystemd-shared-261.so | 14.43 MB | 10.77 MB | −25.4% |

The big test wins come from the ~30 daemons (systemd-networkd,
systemd-resolved, systemd-journald, systemd-logind, systemd-homed,
systemd-importd, systemd-machined, …) whose compiled .o files are embedded
directly into their unit tests via meson's objects: extraction mechanism.
With per-function sections on the daemon sources, the test binary can GC
the bulk of code it never exercises; the remaining DWARF is then shared
zstd-compressed across every .o.

Build-speed cost is below noise on a 24-core build: across four clean
builds (with-flags / sections-only / baseline / with-flags rerun) the
range was 23.6–26.0 s real time and 7m39s–7m48s user time, with the
two with-flags runs faster than the baseline by a couple of seconds —
overhead from per-function-section bookkeeping and zstd compression
disappears into parallel-build noise.

9 days agorepart: canonicalize node in varlink Run method
Daan De Meyer [Wed, 13 May 2026 21:01:41 +0000 (23:01 +0200)] 
repart: canonicalize node in varlink Run method

Run acquire_root_devno() on the varlink-provided node so symlinks (e.g.
/dev/disk/by-id/...) resolve to their canonical /dev/ path before being
used. Without this, sym_fdisk_partname() produces a "-partN" symlink
that udev hasn't created yet when repart calls open() on it right after
BLKPG_ADD_PARTITION, failing with ENOENT.

This also brings the varlink path in line with the CLI path's
partition-to-whole-disk and dm-crypt-to-backing resolution.

9 days agocore: move StartTransient varlink tests to the right place 42161/head
Michael Vogt [Wed, 20 May 2026 09:47:00 +0000 (11:47 +0200)] 
core: move StartTransient varlink tests to the right place

This commit moves the io.systemd.Unit.StartTransient tests into
the right place in TEST-74-AUX-UTILS.varlinkctl-unit.sh.

Thanks to Ivan Kruglov for suggesting this.

9 days agocore: tweak error handling around VARLINK_ERROR_UNIT_BAD_SETTING
Michael Vogt [Wed, 20 May 2026 06:52:20 +0000 (08:52 +0200)] 
core: tweak error handling around VARLINK_ERROR_UNIT_BAD_SETTING

This commit move the erros in StartTransient from
VARLINK_ERROR_UNIT_BAD_SETTING to SD_VARLINK_ERROR_INVALID_PARAMETER
and it also ensures we have the bad field in the error.

Thanks to Ivan Kruglov for suggesting this.

9 days agoupdate TODO
Lennart Poettering [Wed, 20 May 2026 13:43:24 +0000 (15:43 +0200)] 
update TODO

9 days agocore: add User,Group,SupplementaryGroups,Nice to varlink Unit.StartTransient
Michael Vogt [Mon, 18 May 2026 16:32:23 +0000 (18:32 +0200)] 
core: add User,Group,SupplementaryGroups,Nice to varlink Unit.StartTransient

This commit adds more writable fields to the io.systemd.Unit.StartTransient
varlink method. With this its possible to set:
User,Group,SupplementaryGroups,Nice values.

Plus tests for them.

9 days agopo: skip automated fuzzy translations when generating new po files
Luca Boccassi [Mon, 18 May 2026 11:56:39 +0000 (12:56 +0100)] 
po: skip automated fuzzy translations when generating new po files

The fuzzy translations are always wrong, but meson's integration does
not allow skipping them. Add a tiny wrapper for 'msgmerge' to
workaround the issue and skip them when running ninja systemd-update-po

9 days agovmspawn: use EPYC-v4 cpu for SNP 42193/head
Paul Meyer [Tue, 19 May 2026 11:56:46 +0000 (13:56 +0200)] 
vmspawn: use EPYC-v4 cpu for SNP

SNP requires a named, stable CPU model so the launch measurement is
reproducible across hosts. EPYC-v4 is the baseline that covers all
SNP-capable processors (Milan and later).

Signed-off-by: Paul Meyer <katexochen0@gmail.com>
9 days agovmspawn: initial support for SEV-SNP guests
Paul Meyer [Mon, 18 May 2026 05:50:34 +0000 (07:50 +0200)] 
vmspawn: initial support for SEV-SNP guests

Add --confidential-computing=sev-snp to run the guest as an AMD SEV-SNP
confidential VM. Loads a raw OVMF firmware blob via -bios (SNP doesn't
support the pflash + NVRAM split), attaches a sev-snp-guest object,
and hashes the kernel, initrd and cmdline into the launch measurement
when direct kernel boot is used. Incompatible features (Secure Boot,
CXL, virtio-balloon, SMBIOS credentials) are rejected or disabled; an
attached vTPM must be treated as untrusted by the guest.

The feature is marked experimental in the man page.

Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Paul Meyer <katexochen0@gmail.com>
9 days agoupdate TODO
Lennart Poettering [Wed, 20 May 2026 10:15:33 +0000 (12:15 +0200)] 
update TODO

9 days agoudev/net: document IRQAffinityPolicy=, IRQAffinity=, and 40304/head
Quentin Deslandes [Thu, 8 Jan 2026 00:25:56 +0000 (01:25 +0100)] 
udev/net: document IRQAffinityPolicy=, IRQAffinity=, and
IRQAffinityNUMA=

9 days agoudev/net: add IRQAffinityNUMA= option for NUMA-aware filtering
Quentin Deslandes [Mon, 16 Feb 2026 19:43:37 +0000 (20:43 +0100)] 
udev/net: add IRQAffinityNUMA= option for NUMA-aware filtering

Add support for filtering IRQ affinity to CPUs on a specific NUMA node
via the new IRQAffinityNUMA= option in .link files. The option accepts:
- "local": use the NUMA node local to the NIC's PCIe slot
- Explicit node number (0, 1, 2, ...): use CPUs on the specified node

When both IRQAffinity= and IRQAffinityNUMA= are specified, their
intersection is used. If the intersection is empty, an error is logged
and IRQ affinity configuration is skipped.

When "local" is specified but the device's NUMA node cannot be
determined (numa_node shows -1), a warning is logged and IRQ affinity
configuration is skipped.