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>
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>
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>
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.
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.
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 abe3d570f8.
* 3e1930512d Downgrade dependency on dbus to recommends in sd-container
* 61d6ecf0a0 Conflict with sysuser-helper
* fbc4646437 autopkgtest: add dependency on procps
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.
While commit 83c1e8ff5f 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
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>
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.
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>
# 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.