Skip to content

Commit

Permalink
journal: Fix disabling NO_COW on btrfs filesystems
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
DaanDeMeyer committed Dec 6, 2021
1 parent 5a98019 commit d71ece3
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 27 deletions.
26 changes: 26 additions & 0 deletions src/journal/journald-file.c
Expand Up @@ -4,13 +4,15 @@
#include <unistd.h>

#include "chattr-util.h"
#include "copy.h"
#include "fd-util.h"
#include "format-util.h"
#include "journal-authenticate.h"
#include "journald-file.h"
#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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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:
Expand Down
26 changes: 0 additions & 26 deletions src/libsystemd/sd-journal/journal-file.c
Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand All @@ -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;
}

Expand Down
1 change: 0 additions & 1 deletion src/libsystemd/sd-journal/journal-file.h
Expand Up @@ -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;
Expand Down

0 comments on commit d71ece3

Please sign in to comment.