]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
shutdown: teach sync_with_progress() to optionally sync a specific fd only
authorLennart Poettering <lennart@poettering.net>
Mon, 9 Sep 2024 15:49:33 +0000 (17:49 +0200)
committerLennart Poettering <lennart@poettering.net>
Mon, 9 Sep 2024 17:12:31 +0000 (19:12 +0200)
This is preparation for reusing the logic for syncing DM and other
devices with a timeout applied.

src/shared/async.c
src/shared/async.h
src/shutdown/shutdown.c
src/shutdown/shutdown.h [new file with mode: 0644]

index bbb8b81011a15dc1cbcf83e5f162e6506cf82b33..bd043c8484a50888ba5a3ea343c7c7d4d908aad8 100644 (file)
@@ -34,6 +34,28 @@ int asynchronous_sync(pid_t *ret_pid) {
         return 0;
 }
 
+int asynchronous_fsync(int fd, pid_t *ret_pid) {
+        int r;
+
+        assert(fd >= 0);
+        /* Same as asynchronous_sync() above, but calls fsync() on a specific fd */
+
+        r = safe_fork_full("(sd-fsync)",
+                           /* stdio_fds= */ NULL,
+                           /* except_fds= */ &fd,
+                           /* n_except_fds= */ 1,
+                           FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|(ret_pid ? 0 : FORK_DETACH), ret_pid);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                /* Child process */
+                fsync(fd);
+                _exit(EXIT_SUCCESS);
+        }
+
+        return 0;
+}
+
 /* We encode the fd to close in the userdata pointer as an unsigned value. The highest bit indicates whether
  * we need to fork again */
 #define NEED_DOUBLE_FORK (1U << (sizeof(unsigned) * 8 - 1))
index 96148f9006e4c65b03ba5677c1646f35d22de069..2f5bbd51a52f3664202117d87d8ca59fe4c44c3a 100644 (file)
@@ -20,6 +20,7 @@
  * for avoiding threads. */
 
 int asynchronous_sync(pid_t *ret_pid);
+int asynchronous_fsync(int fd, pid_t *ret_pid);
 int asynchronous_close(int fd);
 int asynchronous_rm_rf(const char *p, RemoveFlags flags);
 
index 03e6e70fd85a83f9d1ad51f4fdcb47bf6bd79a27..e6c9e0f8066303de1acd13c1613ee5749a68f4ae 100644 (file)
@@ -40,6 +40,7 @@
 #include "process-util.h"
 #include "reboot-util.h"
 #include "rlimit-util.h"
+#include "shutdown.h"
 #include "signal-util.h"
 #include "string-util.h"
 #include "switch-root.h"
@@ -223,8 +224,10 @@ static int sync_making_progress(unsigned long long *prev_dirty) {
         return r;
 }
 
-static int sync_with_progress(void) {
+int sync_with_progress(int fd) {
         unsigned long long dirty = ULLONG_MAX;
+        _cleanup_free_ char *path = NULL;
+        const char *what;
         pid_t pid;
         int r;
 
@@ -233,11 +236,20 @@ static int sync_with_progress(void) {
         /* Due to the possibility of the sync operation hanging, we fork a child process and monitor
          * the progress. If the timeout lapses, the assumption is that the particular sync stalled. */
 
-        r = asynchronous_sync(&pid);
-        if (r < 0)
-                return log_error_errno(r, "Failed to fork sync(): %m");
+        if (fd >= 0) {
+                r = asynchronous_fsync(fd, &pid);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to fork fsync(): %m");
+
+                (void) fd_get_path(fd, &path);
+        } else {
+                r = asynchronous_sync(&pid);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to fork sync(): %m");
+        }
 
-        log_info("Syncing filesystems and block devices.");
+        what = path ?: "filesystems and block devices";
+        log_info("Syncing %s.", what);
 
         /* Start monitoring the sync operation. If more than
          * SYNC_PROGRESS_ATTEMPTS lapse without progress being made,
@@ -248,7 +260,7 @@ static int sync_with_progress(void) {
                         /* Sync finished without error (sync() call itself does not return an error code) */
                         return 0;
                 if (r != -ETIMEDOUT)
-                        return log_error_errno(r, "Failed to sync filesystems and block devices: %m");
+                        return log_error_errno(r, "Failed to sync %s: %m", what);
 
                 /* Reset the check counter if we made some progress */
                 if (sync_making_progress(&dirty) > 0)
@@ -258,7 +270,8 @@ static int sync_with_progress(void) {
         /* Only reached in the event of a timeout. We should issue a kill to the stray process. */
         (void) kill(pid, SIGKILL);
         return log_error_errno(SYNTHETIC_ERRNO(ETIMEDOUT),
-                               "Syncing filesystems and block devices - timed out, issuing SIGKILL to PID "PID_FMT".",
+                               "Syncing %s - timed out, issuing SIGKILL to PID "PID_FMT".",
+                               what,
                                pid);
 }
 
@@ -432,7 +445,7 @@ int main(int argc, char *argv[]) {
          * desperately trying to sync IO to disk within their timeout. Do not remove this sync, data corruption will
          * result. */
         if (!in_container)
-                (void) sync_with_progress();
+                (void) sync_with_progress(-EBADF);
 
         disable_coredumps();
         disable_binfmt();
@@ -600,7 +613,7 @@ int main(int argc, char *argv[]) {
          * which might have caused IO, hence let's do it once more. Do not remove this sync, data corruption
          * will result. */
         if (!in_container)
-                (void) sync_with_progress();
+                (void) sync_with_progress(-EBADF);
 
         notify_supervisor();
 
diff --git a/src/shutdown/shutdown.h b/src/shutdown/shutdown.h
new file mode 100644 (file)
index 0000000..99aaec6
--- /dev/null
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int sync_with_progress(int fd);