journal: Fix disabling NO_COW on btrfs filesystems

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.
This commit is contained in:
Daan De Meyer
2021-12-03 11:06:36 +01:00
parent 5a980196ab
commit d71ece3f0b
3 changed files with 26 additions and 27 deletions

View 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:

View 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;
}

View 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;