From a3081a7a898d84f9f52e39cf141a02d307dc22a2 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 28 Jul 2020 19:10:31 +0200 Subject: [PATCH 1/7] units: Add special Desktop Environment user related units This adds app.slice, session.slice and background.slice. --- units/user/app.slice | 12 ++++++++++++ units/user/background.slice | 12 ++++++++++++ units/user/meson.build | 3 +++ units/user/session.slice | 12 ++++++++++++ units/user/systemd-exit.service | 4 ++++ 5 files changed, 43 insertions(+) create mode 100644 units/user/app.slice create mode 100644 units/user/background.slice create mode 100644 units/user/session.slice 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 33732e7d59..744c1e7c5e 100644 --- a/units/user/meson.build +++ b/units/user/meson.build @@ -1,6 +1,8 @@ # SPDX-License-Identifier: LGPL-2.1+ 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 1d3b61e3ab..87f906c3b7 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 From 0b432bdc2ebdbee63a2f6820916d18f33b4afda2 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 28 Jul 2020 19:12:39 +0200 Subject: [PATCH 2/7] basic: Define macros for special user slices --- src/basic/special.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/basic/special.h b/src/basic/special.h index 19ee30cd41..ccf9d50f10 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" From 7f3b86a4979bb84566b46006f3e04c1033504585 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 28 Jul 2020 19:12:56 +0200 Subject: [PATCH 3/7] core: Move user units into app.slice by default This changes the default from putting all units into the root slice to placing them into the app slice in the user manager. The advantage is that we get the right behaviour in most cases, and we'll need special case handling in all other cases anyway. Note that we have currently defined that applications *should* start their unit names with app-, so we could also move only these by creating a drop-in for app-.scope and app-.service. However, that would not answer the question on how we should manage session.slice. And we would end up placing anything that does not fit the system (e.g. anything started by dbus-broker currently) into the root slice. --- src/core/unit.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/core/unit.c b/src/core/unit.c index 680f4c569b..40578cb3fa 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -3345,12 +3345,16 @@ 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 (MANAGER_IS_SYSTEM(u->manager)) + slice_name = + unit_has_name(u, SPECIAL_INIT_SCOPE) + ? SPECIAL_ROOT_SLICE + : SPECIAL_SYSTEM_SLICE; + else + slice_name = SPECIAL_APP_SLICE; + } r = manager_load_unit(u->manager, slice_name, NULL, NULL, &slice); if (r < 0) From 0f7793bebd0ca2bee62783c38d65e6221552d9c9 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 28 Jul 2020 19:11:19 +0200 Subject: [PATCH 4/7] man: Document app, session and background special user slice units Add documentation for the special slice user slice units. --- man/systemd.special.xml | 54 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/man/systemd.special.xml b/man/systemd.special.xml index fe40da7fbe..c3c6b144d1 100644 --- a/man/systemd.special.xml +++ b/man/systemd.special.xml @@ -1192,6 +1192,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. + + + + From 1c2fd33d84f5e86b5eaafa8dbb0406cdf3eb47f2 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 3 Aug 2020 12:51:25 +0200 Subject: [PATCH 5/7] units: Move user tmpfiles clean service into background.slice --- units/user/systemd-tmpfiles-clean.service | 1 + 1 file changed, 1 insertion(+) diff --git a/units/user/systemd-tmpfiles-clean.service b/units/user/systemd-tmpfiles-clean.service index 3be0de5f7d..b989d41373 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 From 39c79477ace14fd57a77eb629bfed5816c485ccb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 5 Nov 2020 13:08:02 +0100 Subject: [PATCH 6/7] pid1: expose "extrinsic" status of swaps and mounts The only visible change from this is that we show Extrinsic: yes/no in dumps for swap units (this was already done for mount units). --- src/core/mount.c | 13 +++++------ src/core/swap.c | 59 ++++++++++++++++++++++++++++-------------------- src/core/unit.c | 4 ++++ src/core/unit.h | 8 +++++++ 4 files changed, 52 insertions(+), 32 deletions(-) diff --git a/src/core/mount.c b/src/core/mount.c index dbbb73f3d5..07c038b345 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 fa600a9797..2d3d488a7b 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 40578cb3fa..f80d9f4099 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; diff --git a/src/core/unit.h b/src/core/unit.h index 1e6d7ccf6b..991a05bafe 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); From 5ee24fa0a09ded9d842499a251e2e939a1a26b0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 5 Nov 2020 13:59:17 +0100 Subject: [PATCH 7/7] user: move "extrinsic" units to their root slice With the grandparent change to move most units to app.slice, those units would be ordered After=app.slice which doesn't make any sense. Actually they appear earlier, before the manager is even started, and conceputally it doesn't seem useful to put them under any slice. --- src/core/unit.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/core/unit.c b/src/core/unit.c index f80d9f4099..e87b951d9f 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -3350,15 +3350,16 @@ int unit_set_default_slice(Unit *u) { slice_name = strjoina("system-", escaped, ".slice"); else slice_name = strjoina("app-", escaped, ".slice"); - } else { - if (MANAGER_IS_SYSTEM(u->manager)) - slice_name = - unit_has_name(u, SPECIAL_INIT_SCOPE) - ? SPECIAL_ROOT_SLICE - : SPECIAL_SYSTEM_SLICE; - else - slice_name = SPECIAL_APP_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)