]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
journal: Fix disabling NO_COW on btrfs filesystems 21598/head
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Fri, 3 Dec 2021 10:06:36 +0000 (11:06 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 6 Dec 2021 21:17:41 +0000 (22:17 +0100)
Disabling NOCOW when data has been written to a file doesn't work.
Instead, when we're done writing to a journal file (after archiving),
let's rewrite the file with COW enabled. This also takes care of
properly defragmenting the file.

With zstd compression level 3, journal files are compressed to 12%
of their original size with default journal settings.

As rewriting the file might take a while since we also do an fsync()
after the rewrite, this work is done in the offline thread to avoid
blocking the journald event loop.

src/journal/journald-file.c
src/libsystemd/sd-journal/journal-file.c
src/libsystemd/sd-journal/journal-file.h

index fc46aa1963b9875769801cd803b5826d39b31100..7f607d4aa60f877426a545e37bbf555932453ebf 100644 (file)
@@ -4,6 +4,7 @@
 #include <unistd.h>
 
 #include "chattr-util.h"
+#include "copy.h"
 #include "fd-util.h"
 #include "format-util.h"
 #include "journal-authenticate.h"
@@ -11,6 +12,7 @@
 #include "path-util.h"
 #include "random-util.h"
 #include "set.h"
+#include "stat-util.h"
 #include "sync-util.h"
 
 static int journald_file_truncate(JournalFile *f) {
@@ -120,6 +122,8 @@ static int journald_file_punch_holes(JournalFile *f) {
  * As a result we use atomic operations on f->offline_state for inter-thread communications with
  * journal_file_set_offline() and journal_file_set_online(). */
 static void journald_file_set_offline_internal(JournaldFile *f) {
+        int r;
+
         assert(f);
         assert(f->file->fd >= 0);
         assert(f->file->header);
@@ -154,6 +158,28 @@ static void journald_file_set_offline_internal(JournaldFile *f) {
 
                         f->file->header->state = f->file->archive ? STATE_ARCHIVED : STATE_OFFLINE;
                         (void) fsync(f->file->fd);
+
+                        /* If we've archived the journal file, first try to re-enable COW on the file. If the
+                         * FS_NOCOW_FL flag was never set or we succesfully removed it, continue. If we fail
+                         * to remove the flag on the archived file, rewrite the file without the NOCOW flag.
+                         * We need this fallback because on some filesystems (BTRFS), the NOCOW flag cannot
+                         * be removed after data has been written to a file. The only way to remove it is to
+                         * copy all data to a new file without the NOCOW flag set. */
+
+                        if (f->file->archive) {
+                                r = chattr_fd(f->file->fd, 0, FS_NOCOW_FL, NULL);
+                                if (r >= 0)
+                                        continue;
+
+                                log_debug_errno(r, "Failed to re-enable copy-on-write for %s: %m, rewriting file", f->file->path);
+
+                                r = copy_file_atomic(f->file->path, f->file->path, f->file->mode, 0, FS_NOCOW_FL, COPY_REPLACE | COPY_FSYNC);
+                                if (r < 0) {
+                                        log_debug_errno(r, "Failed to rewrite %s: %m", f->file->path);
+                                        continue;
+                                }
+                        }
+
                         break;
 
                 case OFFLINE_OFFLINING:
index bc80a51524d8f0d4a769002094a86c345e866349..11b9da1cb54b52a29db0d8cc76642f13e6c0736b 100644 (file)
@@ -220,18 +220,6 @@ JournalFile* journal_file_close(JournalFile *f) {
         if (f->mmap && f->cache_fd)
                 mmap_cache_fd_free(f->cache_fd);
 
-        if (f->fd >= 0 && f->defrag_on_close) {
-
-                /* Be friendly to btrfs: turn COW back on again now,
-                 * and defragment the file. We won't write to the file
-                 * ever again, hence remove all fragmentation, and
-                 * reenable all the good bits COW usually provides
-                 * (such as data checksumming). */
-
-                (void) chattr_fd(f->fd, 0, FS_NOCOW_FL, NULL);
-                (void) btrfs_defrag_fd(f->fd);
-        }
-
         if (f->close_fd)
                 safe_close(f->fd);
         free(f->path);
@@ -3566,16 +3554,11 @@ int journal_file_archive(JournalFile *f, char **ret_previous_path) {
          * occurs. */
         f->archive = true;
 
-        /* Currently, btrfs is not very good with out write patterns and fragments heavily. Let's defrag our journal
-         * files when we archive them */
-        f->defrag_on_close = true;
-
         return 0;
 }
 
 int journal_file_dispose(int dir_fd, const char *fname) {
         _cleanup_free_ char *p = NULL;
-        _cleanup_close_ int fd = -1;
 
         assert(fname);
 
@@ -3596,15 +3579,6 @@ int journal_file_dispose(int dir_fd, const char *fname) {
         if (renameat(dir_fd, fname, dir_fd, p) < 0)
                 return -errno;
 
-        /* btrfs doesn't cope well with our write pattern and fragments heavily. Let's defrag all files we rotate */
-        fd = openat(dir_fd, p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
-        if (fd < 0)
-                log_debug_errno(errno, "Failed to open file for defragmentation/FS_NOCOW_FL, ignoring: %m");
-        else {
-                (void) chattr_fd(fd, 0, FS_NOCOW_FL, NULL);
-                (void) btrfs_defrag_fd(fd);
-        }
-
         return 0;
 }
 
index 5bcf591337d23a3379e8f6205792bf92fc8d62cf..ecda2b3cc0c11d3ac6a05d6ad94fae4d6ab8da27 100644 (file)
@@ -68,7 +68,6 @@ typedef struct JournalFile {
         bool compress_lz4:1;
         bool compress_zstd:1;
         bool seal:1;
-        bool defrag_on_close:1;
         bool close_fd:1;
         bool archive:1;
         bool keyed_hash:1;