From: Jan Synacek <jsyna...@redhat.com> Journald options SystemMaxUse=, SystemKeepFree=, SystemMaxFileSize=, RuntimeMaxUse=, RuntimeKeepFree= and RuntimeMaxFileSize= can now be specified as a percentage of the available space. Ditto for coredump options MaxUse=, KeepFree=, ExternalSizeMax=, JournalSizeMax= and ProcessSizeMax=. --- man/journald.conf.xml | 4 +- src/journal/coredump.c | 39 ++++++++---- src/journal/journal-file.c | 140 +++++++++++++++++++++++++----------------- src/journal/journal-file.h | 17 +++-- src/journal/journald-server.c | 23 ++++--- src/shared/conf-parser.c | 35 +++++++++-- 6 files changed, 172 insertions(+), 86 deletions(-)
diff --git a/man/journald.conf.xml b/man/journald.conf.xml index 2cbe58b..ec871a8 100644 --- a/man/journald.conf.xml +++ b/man/journald.conf.xml @@ -230,7 +230,9 @@ <varname>RuntimeMaxUse=</varname>, so that usually seven rotated journal files are kept as history. Specify values in bytes or use K, M, G, T, P, E as units for the specified sizes - (equal to 1024, 1024??,... bytes). Note that size limits are + (equal to 1024, 1024??,... bytes). Alternatively, values can + be specified as a percentage (e.g. 33%) of the available + space of the respective file system. Note that size limits are enforced synchronously when journal files are extended, and no explicit rotation step triggered by time is needed.</para></listitem> diff --git a/src/journal/coredump.c b/src/journal/coredump.c index 1c747aa..938ea6a 100644 --- a/src/journal/coredump.c +++ b/src/journal/coredump.c @@ -23,6 +23,7 @@ #include <unistd.h> #include <stdio.h> #include <sys/prctl.h> +#include <sys/statvfs.h> #include <sys/xattr.h> #ifdef HAVE_ELFUTILS @@ -47,6 +48,7 @@ #include "acl-util.h" #include "capability.h" #include "journald-native.h" +#include "journal-file.h" #include "coredump-vacuum.h" #include "process-util.h" @@ -97,11 +99,11 @@ static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage, static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL; static bool arg_compress = true; -static off_t arg_process_size_max = PROCESS_SIZE_MAX; -static off_t arg_external_size_max = EXTERNAL_SIZE_MAX; -static size_t arg_journal_size_max = JOURNAL_SIZE_MAX; -static off_t arg_keep_free = (off_t) -1; -static off_t arg_max_use = (off_t) -1; +static SizeParameter arg_process_size_max = {PROCESS_SIZE_MAX, false}; +static SizeParameter arg_external_size_max = {EXTERNAL_SIZE_MAX, false}; +static SizeParameter arg_journal_size_max = {JOURNAL_SIZE_MAX, false}; +static SizeParameter arg_keep_free = {(uint64_t) -1, false}; +static SizeParameter arg_max_use = {(uint64_t) -1, false}; static int parse_config(void) { static const ConfigTableItem items[] = { @@ -109,7 +111,7 @@ static int parse_config(void) { { "Coredump", "Compress", config_parse_bool, 0, &arg_compress }, { "Coredump", "ProcessSizeMax", config_parse_iec_off, 0, &arg_process_size_max }, { "Coredump", "ExternalSizeMax", config_parse_iec_off, 0, &arg_external_size_max }, - { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max }, + { "Coredump", "JournalSizeMax", config_parse_iec_off, 0, &arg_journal_size_max }, { "Coredump", "KeepFree", config_parse_iec_off, 0, &arg_keep_free }, { "Coredump", "MaxUse", config_parse_iec_off, 0, &arg_max_use }, {} @@ -122,6 +124,15 @@ static int parse_config(void) { false, NULL); } +static uint64_t get_available(void) { + struct statvfs ss; + + if (statvfs("/var/lib/systemd/coredump", &ss) >= 0) + return ss.f_bsize * ss.f_bavail; + + return 0; +} + static int fix_acl(int fd, uid_t uid) { #ifdef HAVE_ACL @@ -229,7 +240,7 @@ static int maybe_remove_external_coredump(const char *filename, off_t size) { /* Returns 1 if might remove, 0 if will not remove, < 0 on error. */ if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) && - size <= arg_external_size_max) + size <= (off_t) size_parameter_evaluate(&arg_external_size_max, get_available())) return 0; if (!filename) @@ -311,7 +322,7 @@ static int save_external_coredump( if (fd < 0) return log_error_errno(errno, "Failed to create coredump file %s: %m", tmp); - r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max, false); + r = copy_bytes(STDIN_FILENO, fd, size_parameter_evaluate(&arg_process_size_max, get_available()), false); if (r == -EFBIG) { log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", info[INFO_PID], info[INFO_COMM]); goto fail; @@ -552,6 +563,7 @@ int main(int argc, char* argv[]) { pid_t pid; char *t; const char *p; + uint64_t max_use, keep_free, available, journal_size_max; /* Make sure we never enter a loop */ prctl(PR_SET_DUMPABLE, 0); @@ -779,8 +791,12 @@ int main(int argc, char* argv[]) { IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1"); IOVEC_SET_STRING(iovec[j++], "PRIORITY=2"); + available = get_available(); + max_use = size_parameter_evaluate(&arg_max_use, available); + keep_free = size_parameter_evaluate(&arg_keep_free, available); + /* Vacuum before we write anything again */ - coredump_vacuum(-1, arg_keep_free, arg_max_use); + coredump_vacuum(-1, keep_free, max_use); /* Always stream the coredump to disk, if that's possible */ r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size); @@ -803,7 +819,7 @@ int main(int argc, char* argv[]) { } /* Vacuum again, but exclude the coredump we just created */ - coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use); + coredump_vacuum(coredump_fd, keep_free, max_use); /* Now, let's drop privileges to become the user who owns the * segfaulted process and allocate the coredump memory under @@ -839,9 +855,10 @@ log: if (core_message) IOVEC_SET_STRING(iovec[j++], core_message); + journal_size_max = size_parameter_evaluate(&arg_journal_size_max, available); /* Optionally store the entire coredump in the journal */ if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) && - coredump_size <= (off_t) arg_journal_size_max) { + coredump_size <= (off_t) journal_size_max) { size_t sz = 0; /* Store the coredump itself in the journal */ diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index be6a552..ac32af6 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -353,7 +353,8 @@ static int journal_file_fstat(JournalFile *f) { } static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) { - uint64_t old_size, new_size; + uint64_t old_size, new_size, max_size, min_size, keep_free, available, free; + struct statvfs svfs; int r; assert(f); @@ -389,31 +390,32 @@ static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) /* Allocate more space. */ - if (f->metrics.max_size > 0 && new_size > f->metrics.max_size) - return -E2BIG; - - if (new_size > f->metrics.min_size && f->metrics.keep_free > 0) { - struct statvfs svfs; + if (fstatvfs(f->fd, &svfs) < 0) + return -errno; - if (fstatvfs(f->fd, &svfs) >= 0) { - uint64_t available; + free = svfs.f_bfree * svfs.f_bsize; + available = svfs.f_bsize * svfs.f_bavail; + max_size = size_parameter_evaluate(&f->metrics.max_size, available); + min_size = size_parameter_evaluate(&f->metrics.min_size, available); + keep_free = size_parameter_evaluate(&f->metrics.keep_free, available); - available = svfs.f_bfree * svfs.f_bsize; + if (max_size > 0 && new_size > max_size) + return -E2BIG; - if (available >= f->metrics.keep_free) - available -= f->metrics.keep_free; - else - available = 0; + if (new_size > min_size && keep_free > 0) { + if (free >= keep_free) + free -= keep_free; + else + free = 0; - if (new_size - old_size > available) - return -E2BIG; - } + if (new_size - old_size > free) + return -E2BIG; } /* Increase by larger blocks at once */ new_size = ((new_size+FILE_SIZE_INCREASE-1) / FILE_SIZE_INCREASE) * FILE_SIZE_INCREASE; - if (f->metrics.max_size > 0 && new_size > f->metrics.max_size) - new_size = f->metrics.max_size; + if (max_size > 0 && new_size > max_size) + new_size = max_size; /* Note that the glibc fallocate() fallback is very inefficient, hence we try to minimize the allocation area @@ -600,16 +602,20 @@ int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, O static int journal_file_setup_data_hash_table(JournalFile *f) { uint64_t s, p; Object *o; + struct statvfs ss; int r; assert(f); + if (fstatvfs(f->fd, &ss) < 0) + return -errno; + /* We estimate that we need 1 hash table entry per 768 of journal file and we want to make sure we never get beyond 75% fill level. Calculate the hash table size for the maximum file size based on these metrics. */ - s = (f->metrics.max_size * 4 / 768 / 3) * sizeof(HashItem); + s = (size_parameter_evaluate(&f->metrics.max_size, ss.f_bsize * ss.f_bavail) * 4 / 768 / 3) * sizeof(HashItem); if (s < DEFAULT_DATA_HASH_TABLE_SIZE) s = DEFAULT_DATA_HASH_TABLE_SIZE; @@ -2950,78 +2956,90 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6 } void journal_default_metrics(JournalMetrics *m, int fd) { - uint64_t fs_size = 0; + uint64_t fs_size = 0, available = 0, max_use, max_size, min_size, keep_free; struct statvfs ss; char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX]; assert(m); assert(fd >= 0); - if (fstatvfs(fd, &ss) >= 0) + if (fstatvfs(fd, &ss) >= 0) { fs_size = ss.f_frsize * ss.f_blocks; + available = ss.f_bsize * ss.f_bavail; + } - if (m->max_use == (uint64_t) -1) { + max_use = size_parameter_evaluate(&m->max_use, available); + max_size = size_parameter_evaluate(&m->max_size, available); + min_size = size_parameter_evaluate(&m->min_size, available); + keep_free = size_parameter_evaluate(&m->keep_free, available); + + if (max_use == (uint64_t) -1) { if (fs_size > 0) { - m->max_use = PAGE_ALIGN(fs_size / 10); /* 10% of file system size */ + max_use = PAGE_ALIGN(fs_size / 10); /* 10% of file system size */ - if (m->max_use > DEFAULT_MAX_USE_UPPER) - m->max_use = DEFAULT_MAX_USE_UPPER; + if (max_use > DEFAULT_MAX_USE_UPPER) + max_use = DEFAULT_MAX_USE_UPPER; - if (m->max_use < DEFAULT_MAX_USE_LOWER) - m->max_use = DEFAULT_MAX_USE_LOWER; + if (max_use < DEFAULT_MAX_USE_LOWER) + max_use = DEFAULT_MAX_USE_LOWER; } else - m->max_use = DEFAULT_MAX_USE_LOWER; + max_use = DEFAULT_MAX_USE_LOWER; } else { - m->max_use = PAGE_ALIGN(m->max_use); + max_use = PAGE_ALIGN(max_use); - if (m->max_use < JOURNAL_FILE_SIZE_MIN*2) - m->max_use = JOURNAL_FILE_SIZE_MIN*2; + if (max_use < JOURNAL_FILE_SIZE_MIN*2) + max_use = JOURNAL_FILE_SIZE_MIN*2; } - if (m->max_size == (uint64_t) -1) { - m->max_size = PAGE_ALIGN(m->max_use / 8); /* 8 chunks */ + if (max_size == (uint64_t) -1) { + max_size = PAGE_ALIGN(max_use / 8); /* 8 chunks */ - if (m->max_size > DEFAULT_MAX_SIZE_UPPER) - m->max_size = DEFAULT_MAX_SIZE_UPPER; + if (max_size > DEFAULT_MAX_SIZE_UPPER) + max_size = DEFAULT_MAX_SIZE_UPPER; } else - m->max_size = PAGE_ALIGN(m->max_size); + max_size = PAGE_ALIGN(max_size); - if (m->max_size < JOURNAL_FILE_SIZE_MIN) - m->max_size = JOURNAL_FILE_SIZE_MIN; + if (max_size < JOURNAL_FILE_SIZE_MIN) + max_size = JOURNAL_FILE_SIZE_MIN; - if (m->max_size*2 > m->max_use) - m->max_use = m->max_size*2; + if (max_size*2 > max_use) + max_use = max_size*2; - if (m->min_size == (uint64_t) -1) - m->min_size = JOURNAL_FILE_SIZE_MIN; + if (min_size == (uint64_t) -1) + min_size = JOURNAL_FILE_SIZE_MIN; else { - m->min_size = PAGE_ALIGN(m->min_size); + min_size = PAGE_ALIGN(min_size); - if (m->min_size < JOURNAL_FILE_SIZE_MIN) - m->min_size = JOURNAL_FILE_SIZE_MIN; + if (min_size < JOURNAL_FILE_SIZE_MIN) + min_size = JOURNAL_FILE_SIZE_MIN; - if (m->min_size > m->max_size) - m->max_size = m->min_size; + if (min_size > max_size) + max_size = min_size; } - if (m->keep_free == (uint64_t) -1) { + if (keep_free == (uint64_t) -1) { if (fs_size > 0) { - m->keep_free = PAGE_ALIGN(fs_size * 3 / 20); /* 15% of file system size */ + keep_free = PAGE_ALIGN(fs_size * 3 / 20); /* 15% of file system size */ - if (m->keep_free > DEFAULT_KEEP_FREE_UPPER) - m->keep_free = DEFAULT_KEEP_FREE_UPPER; + if (keep_free > DEFAULT_KEEP_FREE_UPPER) + keep_free = DEFAULT_KEEP_FREE_UPPER; } else - m->keep_free = DEFAULT_KEEP_FREE; + keep_free = DEFAULT_KEEP_FREE; } + m->max_use = (SizeParameter) {.value=max_use, .relative=false}; + m->max_size = (SizeParameter) {.value=max_size, .relative=false}; + m->min_size = (SizeParameter) {.value=min_size, .relative=false}; + m->keep_free = (SizeParameter) {.value=keep_free, .relative=false}; + log_debug("Fixed max_use=%s max_size=%s min_size=%s keep_free=%s", - format_bytes(a, sizeof(a), m->max_use), - format_bytes(b, sizeof(b), m->max_size), - format_bytes(c, sizeof(c), m->min_size), - format_bytes(d, sizeof(d), m->keep_free)); + format_bytes(a, sizeof(a), m->max_use.value), + format_bytes(b, sizeof(b), m->max_size.value), + format_bytes(c, sizeof(c), m->min_size.value), + format_bytes(d, sizeof(d), m->keep_free.value)); } int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to) { @@ -3144,3 +3162,13 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec) { return false; } + +uint64_t size_parameter_evaluate(const SizeParameter *sp, uint64_t available) { + if (sp->value == (uint64_t) -1) + return (uint64_t) -1; + + if (sp->relative) + return sp->value * 0.01 * available; + + return sp->value; +} diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index 403c8f7..ad79536 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -35,12 +35,17 @@ #include "mmap-cache.h" #include "hashmap.h" +typedef struct SizeParameter { + uint64_t value; + bool relative; +} SizeParameter; + typedef struct JournalMetrics { - uint64_t max_use; - uint64_t use; - uint64_t max_size; - uint64_t min_size; - uint64_t keep_free; + SizeParameter max_use; + SizeParameter use; + SizeParameter max_size; + SizeParameter min_size; + SizeParameter keep_free; } JournalMetrics; typedef enum direction { @@ -229,3 +234,5 @@ int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t * int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot, usec_t *from, usec_t *to); bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec); + +uint64_t size_parameter_evaluate(const SizeParameter *sp, uint64_t available); diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index b3a4b53..7c97643 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -91,7 +91,7 @@ static uint64_t available_space(Server *s, bool verbose) { _cleanup_free_ char *p = NULL; sd_id128_t machine; struct statvfs ss; - uint64_t sum = 0, ss_avail = 0, avail = 0; + uint64_t sum = 0, ss_avail = 0, avail = 0, max_use, keep_free; int r; _cleanup_closedir_ DIR *d = NULL; usec_t ts; @@ -155,18 +155,20 @@ static uint64_t available_space(Server *s, bool verbose) { } ss_avail = ss.f_bsize * ss.f_bavail; + max_use = size_parameter_evaluate(&m->max_use, ss_avail); + keep_free = size_parameter_evaluate(&m->keep_free, ss_avail); /* If we reached a high mark, we will always allow this much * again, unless usage goes above max_use. This watermark * value is cached so that we don't give up space on pressure, * but hover below the maximum usage. */ - if (m->use < sum) - m->use = sum; + if (size_parameter_evaluate(&m->use, ss_avail) < sum) + m->use = (SizeParameter) {.value=sum, .relative=false}; - avail = LESS_BY(ss_avail, m->keep_free); + avail = LESS_BY(ss_avail, keep_free); - s->cached_available_space = LESS_BY(MIN(m->max_use, avail), sum); + s->cached_available_space = LESS_BY(MIN(max_use, avail), sum); s->cached_available_space_timestamp = ts; if (verbose) { @@ -178,8 +180,8 @@ static uint64_t available_space(Server *s, bool verbose) { "trying to leave %s free of %s available ??? current limit %s).", s->system_journal ? "Permanent" : "Runtime", format_bytes(fb1, sizeof(fb1), sum), - format_bytes(fb2, sizeof(fb2), m->max_use), - format_bytes(fb3, sizeof(fb3), m->keep_free), + format_bytes(fb2, sizeof(fb2), max_use), + format_bytes(fb3, sizeof(fb3), keep_free), format_bytes(fb4, sizeof(fb4), ss_avail), format_bytes(fb5, sizeof(fb5), s->cached_available_space + sum)); } @@ -373,13 +375,18 @@ static void do_vacuum( JournalMetrics *metrics) { const char *p; + struct statvfs ss; + uint64_t max_use = 0; int r; if (!f) return; + if (fstatvfs(f->fd, &ss) >= 0) + max_use = size_parameter_evaluate(&metrics->max_use, ss.f_bsize * ss.f_bavail); + p = strjoina(path, id); - r = journal_directory_vacuum(p, metrics->max_use, s->max_retention_usec, &s->oldest_file_usec, false); + r = journal_directory_vacuum(p, max_use, s->max_retention_usec, &s->oldest_file_usec, false); if (r < 0 && r != -ENOENT) log_error_errno(r, "Failed to vacuum %s: %m", p); } diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index 2c85515..ef4ac08 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -26,6 +26,7 @@ #include "conf-parser.h" #include "conf-files.h" +#include "journal-file.h" #include "util.h" #include "macro.h" #include "strv.h" @@ -517,6 +518,27 @@ int config_parse_si_size(const char* unit, return 0; } +static int parse_percent(const char *rvalue, SizeParameter *sp) { + char *p, *e; + off_t percent; + + p = endswith(rvalue, "%"); + if (!p) + return -ERANGE; + + percent = strtoll(rvalue, &e, 10); + if (*e != '%' || e != p) + return -ERANGE; + + if (percent > 100) + return -ERANGE; + + sp->relative = true; + sp->value = percent; + + return 0; +} + int config_parse_iec_off(const char* unit, const char *filename, unsigned line, @@ -528,7 +550,7 @@ int config_parse_iec_off(const char* unit, void *data, void *userdata) { - off_t *bytes = data; + SizeParameter *sp = data; int r; assert(filename); @@ -538,10 +560,13 @@ int config_parse_iec_off(const char* unit, assert_cc(sizeof(off_t) == sizeof(uint64_t)); - r = parse_size(rvalue, 1024, bytes); - if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse size value, ignoring: %s", rvalue); - + r = parse_percent(rvalue, sp); + if (r < 0) { + sp->relative = false; + r = parse_size(rvalue, 1024, (off_t *)&sp->value); + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse size value, ignoring: %s", rvalue); + } return 0; } -- 2.1.0
_______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel