diff --git a/man/systemd.special.xml b/man/systemd.special.xml index d9439c23de..a70e9ee0ca 100644 --- a/man/systemd.special.xml +++ b/man/systemd.special.xml @@ -1179,6 +1179,60 @@ + + + Special User Slice Units + + There are four .slice units which form the basis of the user hierarchy for + assignment of resources for user applications and services. See + systemd.slice7 + for details about slice units and the documentation about + Desktop Environments + for further information. + + + + -.slice + + The root slice is the root of the user's slice hierarchy. + It usually does not contain units directly, but may be used to set defaults for the whole tree. + + + + + app.slice + + By default, all user services and applications managed by + systemd are found in this slice. + All interactively launched applications like web browsers and text editors + as well as non-critical services should be placed into this slice. + + + + + session.slice + + All essential services and applications required for the + session should use this slice. + These are services that either cannot be restarted easily + or where latency issues may affect the interactivity of the system and applications. + This includes the display server, screen readers and other services such as DBus or XDG portals. + Such services should be configured to be part of this slice by + adding Slice=session.slice to their unit files. + + + + + background.slice + + All services running low-priority background tasks should use this slice. + This permits resources to be preferentially assigned to the other slices. + Examples include non-interactive tasks like file indexing or backup operations + where latency is not important. + + + + diff --git a/src/basic/special.h b/src/basic/special.h index b602bb097c..d55b3289de 100644 --- a/src/basic/special.h +++ b/src/basic/special.h @@ -107,3 +107,8 @@ /* The root directory. */ #define SPECIAL_ROOT_MOUNT "-.mount" + +/* Special slices valid for the user instance */ +#define SPECIAL_SESSION_SLICE "session.slice" +#define SPECIAL_APP_SLICE "app.slice" +#define SPECIAL_BACKGROUND_SLICE "background.slice" diff --git a/src/core/mount.c b/src/core/mount.c index c96c137d29..41dc7e9967 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -412,8 +412,9 @@ static int mount_add_quota_dependencies(Mount *m) { return 0; } -static bool mount_is_extrinsic(Mount *m) { +static bool mount_is_extrinsic(Unit *u) { MountParameters *p; + Mount *m = MOUNT(u); assert(m); /* Returns true for all units that are "magic" and should be excluded from the usual @@ -422,10 +423,7 @@ static bool mount_is_extrinsic(Mount *m) { * ourselves but it's fine if the user operates on them with us. */ /* We only automatically manage mounts if we are in system mode */ - if (!MANAGER_IS_SYSTEM(UNIT(m)->manager)) - return true; - - if (UNIT(m)->perpetual) /* All perpetual units never change state */ + if (MANAGER_IS_USER(u->manager)) return true; p = get_mount_parameters(m); @@ -493,7 +491,7 @@ static int mount_add_default_dependencies(Mount *m) { * guaranteed to stay mounted the whole time, since our system is on it. Also, don't * bother with anything mounted below virtual file systems, it's also going to be virtual, * and hence not worth the effort. */ - if (mount_is_extrinsic(m)) + if (mount_is_extrinsic(UNIT(m))) return 0; p = get_mount_parameters(m); @@ -790,7 +788,7 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) { prefix, p ? strna(p->options) : "n/a", prefix, yes_no(m->from_proc_self_mountinfo), prefix, yes_no(m->from_fragment), - prefix, yes_no(mount_is_extrinsic(m)), + prefix, yes_no(mount_is_extrinsic(u)), prefix, m->directory_mode, prefix, yes_no(m->sloppy_options), prefix, yes_no(m->lazy_unmount), @@ -2161,6 +2159,7 @@ const UnitVTable mount_vtable = { .will_restart = unit_will_restart_default, .may_gc = mount_may_gc, + .is_extrinsic = mount_is_extrinsic, .sigchld_event = mount_sigchld_event, diff --git a/src/core/swap.c b/src/core/swap.c index d984ac0f2d..76e491ad94 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -56,6 +56,35 @@ static bool SWAP_STATE_WITH_PROCESS(SwapState state) { SWAP_CLEANING); } +_pure_ static UnitActiveState swap_active_state(Unit *u) { + assert(u); + + return state_translation_table[SWAP(u)->state]; +} + +_pure_ static const char *swap_sub_state_to_string(Unit *u) { + assert(u); + + return swap_state_to_string(SWAP(u)->state); +} + +_pure_ static bool swap_may_gc(Unit *u) { + Swap *s = SWAP(u); + + assert(s); + + if (s->from_proc_swaps) + return false; + + return true; +} + +_pure_ static bool swap_is_extrinsic(Unit *u) { + assert(SWAP(u)); + + return MANAGER_IS_USER(u->manager); +} + static void swap_unset_proc_swaps(Swap *s) { assert(s); @@ -610,13 +639,15 @@ static void swap_dump(Unit *u, FILE *f, const char *prefix) { "%sClean Result: %s\n" "%sWhat: %s\n" "%sFrom /proc/swaps: %s\n" - "%sFrom fragment: %s\n", + "%sFrom fragment: %s\n" + "%sExtrinsic: %s\n", prefix, swap_state_to_string(s->state), prefix, swap_result_to_string(s->result), prefix, swap_result_to_string(s->clean_result), prefix, s->what, prefix, yes_no(s->from_proc_swaps), - prefix, yes_no(s->from_fragment)); + prefix, yes_no(s->from_fragment), + prefix, yes_no(swap_is_extrinsic(u))); if (s->devnode) fprintf(f, "%sDevice Node: %s\n", prefix, s->devnode); @@ -1028,29 +1059,6 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD return 0; } -_pure_ static UnitActiveState swap_active_state(Unit *u) { - assert(u); - - return state_translation_table[SWAP(u)->state]; -} - -_pure_ static const char *swap_sub_state_to_string(Unit *u) { - assert(u); - - return swap_state_to_string(SWAP(u)->state); -} - -_pure_ static bool swap_may_gc(Unit *u) { - Swap *s = SWAP(u); - - assert(s); - - if (s->from_proc_swaps) - return false; - - return true; -} - static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) { Swap *s = SWAP(u); SwapResult f; @@ -1649,6 +1657,7 @@ const UnitVTable swap_vtable = { .will_restart = unit_will_restart_default, .may_gc = swap_may_gc, + .is_extrinsic = swap_is_extrinsic, .sigchld_event = swap_sigchld_event, diff --git a/src/core/unit.c b/src/core/unit.c index 1d9691fb57..45a417a090 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -1988,6 +1988,10 @@ int unit_stop(Unit *u) { bool unit_can_stop(Unit *u) { assert(u); + /* Note: if we return true here, it does not mean that the unit may be successfully stopped. + * Extrinsic units follow external state and they may stop following external state changes + * (hence we return true here), but an attempt to do this through the manager will fail. */ + if (!unit_type_supported(u->type)) return false; @@ -3345,12 +3349,17 @@ int unit_set_default_slice(Unit *u) { if (MANAGER_IS_SYSTEM(u->manager)) slice_name = strjoina("system-", escaped, ".slice"); else - slice_name = strjoina(escaped, ".slice"); - } else - slice_name = - MANAGER_IS_SYSTEM(u->manager) && !unit_has_name(u, SPECIAL_INIT_SCOPE) - ? SPECIAL_SYSTEM_SLICE - : SPECIAL_ROOT_SLICE; + slice_name = strjoina("app-", escaped, ".slice"); + + } else if (unit_is_extrinsic(u)) + /* Keep all extrinsic units (e.g. perpetual units and swap and mount units in user mode) in + * the root slice. They don't really belong in one of the subslices. */ + slice_name = SPECIAL_ROOT_SLICE; + + else if (MANAGER_IS_SYSTEM(u->manager)) + slice_name = SPECIAL_SYSTEM_SLICE; + else + slice_name = SPECIAL_APP_SLICE; r = manager_load_unit(u->manager, slice_name, NULL, NULL, &slice); if (r < 0) diff --git a/src/core/unit.h b/src/core/unit.h index 8cb7135723..02b2b24206 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -531,6 +531,9 @@ typedef struct UnitVTable { * even though nothing references it and it isn't active in any way. */ bool (*may_gc)(Unit *u); + /* Return true when the unit is not controlled by the manager (e.g. extrinsic mounts). */ + bool (*is_extrinsic)(Unit *u); + /* When the unit is not running and no job for it queued we shall release its runtime resources */ void (*release_resources)(Unit *u); @@ -684,6 +687,11 @@ int unit_set_description(Unit *u, const char *description); bool unit_may_gc(Unit *u); +static inline bool unit_is_extrinsic(Unit *u) { + return u->perpetual || + (UNIT_VTABLE(u)->is_extrinsic && UNIT_VTABLE(u)->is_extrinsic(u)); +} + void unit_add_to_load_queue(Unit *u); void unit_add_to_dbus_queue(Unit *u); void unit_add_to_cleanup_queue(Unit *u); diff --git a/units/user/app.slice b/units/user/app.slice new file mode 100644 index 0000000000..065ea77e96 --- /dev/null +++ b/units/user/app.slice @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=User Application Slice +Documentation=man:systemd.special(7) diff --git a/units/user/background.slice b/units/user/background.slice new file mode 100644 index 0000000000..03c89b66be --- /dev/null +++ b/units/user/background.slice @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=User Background Tasks Slice +Documentation=man:systemd.special(7) diff --git a/units/user/meson.build b/units/user/meson.build index b2a599b6d8..337a92092d 100644 --- a/units/user/meson.build +++ b/units/user/meson.build @@ -1,6 +1,8 @@ # SPDX-License-Identifier: LGPL-2.1-or-later units = [ + 'app.slice', + 'background.slice', 'basic.target', 'bluetooth.target', 'default.target', @@ -9,6 +11,7 @@ units = [ 'graphical-session.target', 'paths.target', 'printer.target', + 'session.slice', 'shutdown.target', 'smartcard.target', 'sockets.target', diff --git a/units/user/session.slice b/units/user/session.slice new file mode 100644 index 0000000000..e0b38c5e32 --- /dev/null +++ b/units/user/session.slice @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=User Core Session Slice +Documentation=man:systemd.special(7) diff --git a/units/user/systemd-exit.service b/units/user/systemd-exit.service index 91953e0524..18725253bb 100644 --- a/units/user/systemd-exit.service +++ b/units/user/systemd-exit.service @@ -14,3 +14,7 @@ DefaultDependencies=no Requires=shutdown.target After=shutdown.target SuccessAction=exit-force + +[Service] +# Place into the root slice to not keep another slice unit alive +Slice=-.slice diff --git a/units/user/systemd-tmpfiles-clean.service b/units/user/systemd-tmpfiles-clean.service index ff319d2b3a..6a937071f7 100644 --- a/units/user/systemd-tmpfiles-clean.service +++ b/units/user/systemd-tmpfiles-clean.service @@ -19,3 +19,4 @@ Type=oneshot ExecStart=systemd-tmpfiles --user --clean SuccessExitStatus=DATAERR IOSchedulingClass=idle +Slice=background.slice