diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 0df03077d3..853e679aaa 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -371,8 +371,6 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents int n_retries; bool truncated = false; - assert(ret_contents); - /* Virtual filesystems such as sysfs or procfs use kernfs, and kernfs can work with two sorts of * virtual files. One sort uses "seq_file", and the results of the first read are buffered for the * second read. The other sort uses "raw" reads which always go direct to the device. In the latter @@ -486,7 +484,9 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents return -EBADMSG; buf[n] = 0; - *ret_contents = TAKE_PTR(buf); + + if (ret_contents) + *ret_contents = TAKE_PTR(buf); return !truncated; } diff --git a/src/basic/log.h b/src/basic/log.h index 51ba3d8fde..738c181070 100644 --- a/src/basic/log.h +++ b/src/basic/log.h @@ -236,6 +236,29 @@ int log_emergency_level(void); #define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__) #define log_emergency_errno(error, ...) log_full_errno(log_emergency_level(), error, __VA_ARGS__) +/* This logs at the specified level the first time it is called, and then + * logs at debug. If the specified level is debug, this logs only the first + * time it is called. */ +#define log_once(level, ...) \ + ({ \ + if (ONCE) \ + log_full(level, __VA_ARGS__); \ + else if (LOG_PRI(level) != LOG_DEBUG) \ + log_debug(__VA_ARGS__); \ + }) + +#define log_once_errno(level, error, ...) \ + ({ \ + int _err = (error); \ + if (ONCE) \ + _err = log_full_errno(level, _err, __VA_ARGS__); \ + else if (LOG_PRI(level) != LOG_DEBUG) \ + _err = log_debug_errno(_err, __VA_ARGS__); \ + else \ + _err = -ERRNO_VALUE(_err); \ + _err; \ + }) + #if LOG_TRACE # define log_trace(...) log_debug(__VA_ARGS__) #else diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h index 6ff8372f3c..967518600d 100644 --- a/src/fundamental/macro-fundamental.h +++ b/src/fundamental/macro-fundamental.h @@ -50,6 +50,17 @@ #define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq)) #define UNIQ __COUNTER__ +/* Note that this works differently from pthread_once(): this macro does + * not synchronize code execution, i.e. code that is run conditionalized + * on this macro will run concurrently to all other code conditionalized + * the same way, there's no ordering or completion enforced. */ +#define ONCE __ONCE(UNIQ_T(_once_, UNIQ)) +#define __ONCE(o) \ + ({ \ + static bool (o) = false; \ + __sync_bool_compare_and_swap(&(o), false, true); \ + }) + #undef MAX #define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b)) #define __MAX(aq, a, bq, b) \ diff --git a/src/oom/oomd-util.c b/src/oom/oomd-util.c index 5bf81479c9..0550ac6c74 100644 --- a/src/oom/oomd-util.c +++ b/src/oom/oomd-util.c @@ -333,7 +333,11 @@ int oomd_cgroup_context_acquire(const char *path, OomdCGroupContext **ret) { return log_debug_errno(r, "Error getting memory.low from %s: %m", path); r = cg_get_attribute_as_uint64(SYSTEMD_CGROUP_CONTROLLER, path, "memory.swap.current", &ctx->swap_usage); - if (r < 0) + if (r == -ENODATA) + /* The kernel can be compiled without support for memory.swap.* files, + * or it can be disabled with boot param 'swapaccount=0' */ + log_once(LOG_WARNING, "No kernel support for memory.swap.current from %s (try boot param swapaccount=1), ignoring.", path); + else if (r < 0) return log_debug_errno(r, "Error getting memory.swap.current from %s: %m", path); r = cg_get_keyed_attribute(SYSTEMD_CGROUP_CONTROLLER, path, "memory.stat", STRV_MAKE("pgscan"), &val); diff --git a/src/shared/psi-util.c b/src/shared/psi-util.c index 7a184d53f7..97a4353792 100644 --- a/src/shared/psi-util.c +++ b/src/shared/psi-util.c @@ -4,6 +4,7 @@ #include #include "alloc-util.h" +#include "errno-util.h" #include "extract-word.h" #include "fd-util.h" #include "fileio.h" @@ -107,12 +108,18 @@ int read_resource_pressure(const char *path, PressureType type, ResourcePressure int is_pressure_supported(void) { const char *p; - FOREACH_STRING(p, "/proc/pressure/cpu", "/proc/pressure/io", "/proc/pressure/memory") - if (access(p, F_OK) < 0) { - if (errno == ENOENT) - return 0; - return -errno; - } + /* The pressure files, both under /proc and in cgroups, will exist + * even if the kernel has PSI support disabled; we have to read + * the file to make sure it doesn't return -EOPNOTSUPP */ + FOREACH_STRING(p, "/proc/pressure/cpu", "/proc/pressure/io", "/proc/pressure/memory") { + int r; + + r = read_virtual_file(p, 0, NULL, NULL); + if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r)) + return 0; + if (r < 0) + return r; + } return 1; }