core/swap: don't adopt a swap unit that isn't backed by anything
systemd creates one .swap unit per udev device link of every device listed in /proc/swaps. When such a device link disappears while the swap itself stays on -- e.g. because the link is not unique and udev repoints it at another partition -- /proc/swaps doesn't change, so nothing re-examines the unit and it stays active. The next daemon-reload then orphans it: all units are dropped and rebuilt, the enumeration from /proc/swaps no longer produces this name, and only the deserialization brings the unit back, i.e. by name and without a fragment on disk. swap_coldplug() restored its active state regardless, leaving a unit that is active, unloadable and has no device, and that is never garbage collected because active units aren't. Refuse to adopt such a unit: if neither /proc/swaps nor a fragment vouches for it, it describes a swap that doesn't exist anymore. Units that still have a control process around are left alone, so that the process is re-adopted and the timeout re-armed; those end up dead once it exits, as they aren't backed by /proc/swaps either. Issue: https://github.com/systemd/systemd/issues/43442
This commit is contained in:
@@ -566,6 +566,23 @@ static int swap_coldplug(Unit *u) {
|
||||
else if (s->from_proc_swaps)
|
||||
new_state = SWAP_ACTIVE;
|
||||
|
||||
/* If /proc/swaps doesn't know this unit, and we couldn't load a fragment for it either, then it is
|
||||
* a leftover of the serialization describing a swap that doesn't exist anymore — typically because
|
||||
* the device link the unit is named after has been repointed at some other device in the meantime.
|
||||
* Don't adopt such a unit: it has no device we could pass to swapoff, and since active units are
|
||||
* never garbage collected it would stick around until the end of time. Note that units with a
|
||||
* fragment are explicitly *not* covered by this, as those are not backed by /proc/swaps to begin
|
||||
* with when their name doesn't match What=.
|
||||
*
|
||||
* Units with a control process still around are left alone too: we want to re-adopt the process
|
||||
* and re-arm the timeout rather than drop both on the floor. Once it is gone the state machine
|
||||
* ends up in dead anyway, as the unit isn't backed by /proc/swaps. */
|
||||
if (!s->from_proc_swaps && u->load_state != UNIT_LOADED && !SWAP_STATE_WITH_PROCESS(new_state)) {
|
||||
log_unit_debug(u, "Not backed by /proc/swaps and has no fragment, ignoring.");
|
||||
unit_add_to_gc_queue(u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (new_state == s->state)
|
||||
return 0;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user