]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
mount: disable mount-storm protection while mount unit is starting.
authorNeilBrown <neilb@suse.com>
Sun, 16 Dec 2018 22:32:58 +0000 (09:32 +1100)
committerLennart Poettering <lennart@poettering.net>
Tue, 18 Dec 2018 23:44:19 +0000 (00:44 +0100)
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
src/core/mount.c

index 9f8fc46434e7a97373d98c91e4b9cbea8d9fdaa7..18219a184b293d4662b4a1b6ce1a1a0665180344 100644 (file)
@@ -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;
index cfdcc6e6f541b09dd1eccf1765f0acfb2ec9e6f7..823024b4160a25305becabef330f651547e0d834 100644 (file)
@@ -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