[systemd-devel] [PATCH] journal, coredump: allow relative values in some configuration options
jsynacek at redhat.com
jsynacek at redhat.com
Mon May 25 04:48:36 PDT 2015
From: Jan Synacek <jsynacek at 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
More information about the systemd-devel
mailing list