core: do not GC units that have FDs stored (#41435)
If a unit has FileDescriptorStorePreserve=yes we'll keep its FDs around
in case it starts again. But if there are no reverse dependencies
referencing it, we'll also GC it and lose all the FDs, which defeats the
point of the setting (which is opt-in).
Do not GC units that have FDs stored to avoid this.
Follow-up for b9c1883a9c
This commit is contained in:
@@ -1250,9 +1250,10 @@ RestartMaxDelaySec=160s</programlisting>
|
||||
file descriptor store is automatically released when the service is stopped; if
|
||||
<constant>restart</constant> (the default) it is kept around as long as the unit is neither inactive
|
||||
nor failed, or a job is queued for the service, or the service is expected to be restarted. If
|
||||
<constant>yes</constant> the file descriptor store is kept around until the unit is removed from
|
||||
memory (i.e. is not referenced anymore and inactive). The latter is useful to keep entries in the
|
||||
file descriptor store pinned until the service manager exits.</para>
|
||||
<constant>yes</constant> the file descriptor store is kept around and garbage collection of the unit
|
||||
is disabled. The latter is useful to keep entries in the file descriptor store pinned until the unit
|
||||
is removed, the service manager exits, or the file descriptors get <constant>EPOLLHUP</constant> or
|
||||
<constant>EPOLLERR</constant>.</para>
|
||||
|
||||
<para>Use <command>systemctl clean --what=fdstore …</command> to release the file descriptor store
|
||||
explicitly.</para>
|
||||
|
||||
@@ -1082,6 +1082,11 @@
|
||||
resources, …) are flushed out immediately after the unit completed, except for what is stored in the logging
|
||||
subsystem. Defaults to <option>inactive</option>.</para>
|
||||
|
||||
<para>Since v261, if <varname>FileDescriptorStorePreserve=</varname> is set to <option>yes</option>,
|
||||
and the unit has file descriptors stored, garbage collection will be disabled until the unit is
|
||||
removed, the service manager exits, or the file descriptors get <constant>EPOLLHUP</constant> or
|
||||
<constant>EPOLLERR</constant>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v236"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
+19
-8
@@ -60,6 +60,8 @@
|
||||
|
||||
#define service_spawn(...) service_spawn_internal(__func__, __VA_ARGS__)
|
||||
|
||||
#define SERVICE_FD_STORE_POPULATED(s) (!!(s)->fd_store)
|
||||
|
||||
static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
|
||||
[SERVICE_DEAD] = UNIT_INACTIVE,
|
||||
[SERVICE_CONDITION] = UNIT_ACTIVATING,
|
||||
@@ -138,6 +140,8 @@ static void service_enter_reload_by_notify(Service *s);
|
||||
|
||||
static bool service_can_reload_extensions(Service *s, bool warn);
|
||||
|
||||
static void service_set_state(Service *s, ServiceState state);
|
||||
|
||||
static bool SERVICE_STATE_WITH_MAIN_PROCESS(ServiceState state) {
|
||||
return IN_SET(state,
|
||||
SERVICE_START, SERVICE_START_POST,
|
||||
@@ -479,12 +483,12 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(ServiceFDStore*, service_fd_store_unlink);
|
||||
static void service_release_fd_store(Service *s) {
|
||||
assert(s);
|
||||
|
||||
if (!s->fd_store)
|
||||
if (!SERVICE_FD_STORE_POPULATED(s))
|
||||
return;
|
||||
|
||||
log_unit_debug(UNIT(s), "Releasing all stored fds.");
|
||||
|
||||
while (s->fd_store)
|
||||
while (SERVICE_FD_STORE_POPULATED(s))
|
||||
service_fd_store_unlink(s->fd_store);
|
||||
|
||||
assert(s->n_fd_store == 0);
|
||||
@@ -571,15 +575,20 @@ static void service_done(Unit *u) {
|
||||
|
||||
static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *userdata) {
|
||||
ServiceFDStore *fs = ASSERT_PTR(userdata);
|
||||
Service *s = fs->service;
|
||||
|
||||
assert(e);
|
||||
|
||||
/* If we get either EPOLLHUP or EPOLLERR, it's time to remove this entry from the fd store */
|
||||
log_unit_debug(UNIT(fs->service),
|
||||
log_unit_debug(UNIT(s),
|
||||
"Received %s on stored fd %d (%s), closing.",
|
||||
revents & EPOLLERR ? "EPOLLERR" : "EPOLLHUP",
|
||||
fs->fd, strna(fs->fdname));
|
||||
service_fd_store_unlink(fs);
|
||||
|
||||
if (s->state == SERVICE_DEAD_RESOURCES_PINNED && !SERVICE_FD_STORE_POPULATED(s))
|
||||
service_set_state(s, SERVICE_DEAD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2126,7 +2135,7 @@ static bool service_will_restart(Unit *u) {
|
||||
static ServiceState service_determine_dead_state(Service *s) {
|
||||
assert(s);
|
||||
|
||||
return s->fd_store && s->fd_store_preserve_mode == EXEC_PRESERVE_YES ? SERVICE_DEAD_RESOURCES_PINNED : SERVICE_DEAD;
|
||||
return SERVICE_FD_STORE_POPULATED(s) && s->fd_store_preserve_mode == EXEC_PRESERVE_YES ? SERVICE_DEAD_RESOURCES_PINNED : SERVICE_DEAD;
|
||||
}
|
||||
|
||||
static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) {
|
||||
@@ -3950,8 +3959,10 @@ static bool service_may_gc(Unit *u) {
|
||||
return false;
|
||||
|
||||
/* Only allow collection of actually dead services, i.e. not those that are in the transitionary
|
||||
* SERVICE_DEAD_BEFORE_AUTO_RESTART/SERVICE_FAILED_BEFORE_AUTO_RESTART states. */
|
||||
if (!IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_DEAD_RESOURCES_PINNED))
|
||||
* SERVICE_DEAD_BEFORE_AUTO_RESTART/SERVICE_FAILED_BEFORE_AUTO_RESTART states, and not those
|
||||
* that still have resources pinned (fd store with FileDescriptorStorePreserve=yes) in case they are
|
||||
* started again later despite not having any reverse dependency. */
|
||||
if (!IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -5612,7 +5623,7 @@ static int service_clean(Unit *u, ExecCleanMask mask) {
|
||||
|
||||
/* If we are done, leave quickly */
|
||||
if (strv_isempty(l)) {
|
||||
if (s->state == SERVICE_DEAD_RESOURCES_PINNED && !s->fd_store)
|
||||
if (s->state == SERVICE_DEAD_RESOURCES_PINNED && !SERVICE_FD_STORE_POPULATED(s))
|
||||
service_set_state(s, SERVICE_DEAD);
|
||||
return 0;
|
||||
}
|
||||
@@ -5865,7 +5876,7 @@ static void service_release_resources(Unit *u) {
|
||||
if (s->fd_store_preserve_mode != EXEC_PRESERVE_YES)
|
||||
service_release_fd_store(s);
|
||||
|
||||
if (s->state == SERVICE_DEAD_RESOURCES_PINNED && !s->fd_store)
|
||||
if (s->state == SERVICE_DEAD_RESOURCES_PINNED && !SERVICE_FD_STORE_POPULATED(s))
|
||||
service_set_state(s, SERVICE_DEAD);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user