From fcfb1f775ed0e9d282607bb118ba788b98952855 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 17 Dec 2018 09:32:58 +1100 Subject: [PATCH] mount: disable mount-storm protection while mount unit is starting. The starting of mount units requires that changes to /proc/self/mountinfo be processed before the SIGCHILD from the completion of /sbin/mount is processed, as described by the comment /* Note that due to the io event priority logic, we can be sure the new mountinfo is loaded * before we process the SIGCHLD for the mount command. */ The recently-added mount-storm protection can defeat this as it will sometimes deliberately delay processing of /proc/self/mountinfo. So we need to disable mount-storm protection when a mount unit is starting. We do this by keeping a counter of the number of pending mounts, and disabling the protection when this is non-zero. Thanks to @asavah for finding and reporting this problem. --- src/core/manager.h | 1 + src/core/mount.c | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/core/manager.h b/src/core/manager.h index 9f8fc46..18219a1 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -230,6 +230,7 @@ struct Manager { sd_event_source *mount_timeout_source; usec_t mount_last_read_usec; usec_t mount_last_duration_usec; + unsigned mount_pending_count; /* Data specific to the swap filesystem */ FILE *proc_swaps; diff --git a/src/core/mount.c b/src/core/mount.c index cfdcc6e..823024b 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -218,6 +218,12 @@ static void mount_done(Unit *u) { assert(m); + if (!IN_SET(m->state, MOUNT_DEAD, MOUNT_MOUNTED, MOUNT_FAILED)) { + /* This was pending, so need to udpate the count */ + assert(u->manager->mount_pending_count > 0); + u->manager->mount_pending_count--; + } + m->where = mfree(m->where); mount_parameters_done(&m->parameters_proc_self_mountinfo); @@ -650,6 +656,7 @@ static int mount_load(Unit *u) { static void mount_set_state(Mount *m, MountState state) { MountState old_state; + int was_pending, is_pending; assert(m); if (m->state != state) @@ -658,6 +665,17 @@ static void mount_set_state(Mount *m, MountState state) { old_state = m->state; m->state = state; + was_pending = !IN_SET(old_state, MOUNT_DEAD, MOUNT_MOUNTED, MOUNT_FAILED); + is_pending = !IN_SET(state, MOUNT_DEAD, MOUNT_MOUNTED, MOUNT_FAILED); + + if (was_pending && !is_pending) { + assert(UNIT(m)->manager->mount_pending_count > 0); + UNIT(m)->manager->mount_pending_count--; + } + + if (is_pending && !was_pending) + UNIT(m)->manager->mount_pending_count++; + if (!MOUNT_STATE_WITH_PROCESS(state)) { m->timer_event_source = sd_event_source_unref(m->timer_event_source); mount_unwatch_control_pid(m); @@ -1790,7 +1808,12 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, usec_t next_read = usec_add(m->mount_last_read_usec, m->mount_last_duration_usec * 10); - if (now(CLOCK_MONOTONIC) < next_read) { + /* If there are pending mounts initiated by systemd, then + * we need to process changes promptly, otherwise we + * rate limit re-reading the file. + */ + if (m->mount_pending_count == 0 && + now(CLOCK_MONOTONIC) < next_read) { /* The (current) API for getting mount events from the Linux kernel * involves getting a "something changed" notification, and then having * to re-read the entire /proc/self/mountinfo file. When there are lots -- 2.7.4