mirror of
https://github.com/systemd/systemd
synced 2025-10-05 03:34:46 +02:00
Compare commits
17 Commits
111e2a106c
...
92716a079f
Author | SHA1 | Date | |
---|---|---|---|
![]() |
92716a079f | ||
![]() |
dda9eeb081 | ||
![]() |
9263c6f57e | ||
![]() |
7bef072ca9 | ||
![]() |
c8082ac09c | ||
![]() |
85e9805903 | ||
![]() |
c4ffd0a020 | ||
![]() |
b089f65676 | ||
![]() |
468b10b188 | ||
![]() |
3f6e7ac27a | ||
![]() |
1edc81aa13 | ||
![]() |
8002f0cf27 | ||
![]() |
62c3f42deb | ||
![]() |
1227795362 | ||
![]() |
8269396078 | ||
![]() |
56631ac8df | ||
![]() |
b0ca060e05 |
@ -10,6 +10,7 @@ static const struct errno_name* lookup_errno(register const char *str,
|
||||
register GPERF_LEN_TYPE len);
|
||||
|
||||
#include "errno-from-name.inc"
|
||||
#include "errno-to-name.inc"
|
||||
|
||||
int errno_from_name(const char *name) {
|
||||
const struct errno_name *sc;
|
||||
@ -24,26 +25,24 @@ int errno_from_name(const char *name) {
|
||||
return sc->id;
|
||||
}
|
||||
|
||||
#if HAVE_STRERRORNAME_NP
|
||||
const char* errno_to_name(int id) {
|
||||
if (id == 0) /* To stay in line with our own impl */
|
||||
return NULL;
|
||||
|
||||
return strerrorname_np(ABS(id));
|
||||
}
|
||||
#else
|
||||
# include "errno-to-name.inc"
|
||||
|
||||
const char* errno_to_name(int id) {
|
||||
if (id < 0)
|
||||
id = -id;
|
||||
|
||||
#if HAVE_STRERRORNAME_NP
|
||||
const char *n = strerrorname_np(id);
|
||||
if (n)
|
||||
return n;
|
||||
#endif
|
||||
|
||||
if ((size_t) id >= ELEMENTSOF(errno_names))
|
||||
return NULL;
|
||||
|
||||
return errno_names[id];
|
||||
}
|
||||
#endif
|
||||
|
||||
const char* errno_name_full(int id, char buf[static ERRNO_NAME_BUF_LEN]) {
|
||||
const char *a = errno_to_name(id);
|
||||
|
@ -212,6 +212,13 @@ static inline bool ERRNO_IS_NEG_DEVICE_ABSENT(intmax_t r) {
|
||||
}
|
||||
_DEFINE_ABS_WRAPPER(DEVICE_ABSENT);
|
||||
|
||||
/* Device is absent or "empty". We get -ENOMEDIUM from CD/DVD devices, also in VMs. */
|
||||
static inline bool ERRNO_IS_NEG_DEVICE_ABSENT_OR_EMPTY(intmax_t r) {
|
||||
return ERRNO_IS_NEG_DEVICE_ABSENT(r) ||
|
||||
r == -ENOMEDIUM;
|
||||
}
|
||||
_DEFINE_ABS_WRAPPER(DEVICE_ABSENT_OR_EMPTY);
|
||||
|
||||
/* Quite often we want to handle cases where the backing FS doesn't support extended attributes at all and
|
||||
* where it simply doesn't have the requested xattr the same way */
|
||||
static inline bool ERRNO_IS_NEG_XATTR_ABSENT(intmax_t r) {
|
||||
|
@ -1931,9 +1931,9 @@ static int socket_coldplug(Unit *u) {
|
||||
SOCKET_RUNNING)) {
|
||||
|
||||
/* Originally, we used to simply reopen all sockets here that we didn't have file descriptors
|
||||
* for. However, this is problematic, as we won't traverse through the SOCKET_START_CHOWN state for
|
||||
* them, and thus the UID/GID wouldn't be right. Hence, instead simply check if we have all fds open,
|
||||
* and if there's a mismatch, warn loudly.
|
||||
* for. However, this is problematic, as we won't traverse through the SOCKET_START_CHOWN
|
||||
* state for them, and thus the UID/GID wouldn't be right. Hence, instead simply check if we
|
||||
* have all fds open, and if there's a mismatch, warn loudly.
|
||||
*
|
||||
* Note that SOCKET_START_OPEN requires no special treatment, as it's only intermediate
|
||||
* between SOCKET_START_PRE and SOCKET_START_CHOWN and shall otherwise not be observed.
|
||||
@ -1942,14 +1942,14 @@ static int socket_coldplug(Unit *u) {
|
||||
r = socket_check_open(s);
|
||||
if (r == SOCKET_OPEN_NONE)
|
||||
log_unit_warning(UNIT(s),
|
||||
"Socket unit configuration has changed while unit has been running, "
|
||||
"no open socket file descriptor left. "
|
||||
"The socket unit is not functional until restarted.");
|
||||
"Unit configuration changed while unit was running, "
|
||||
"and no socket file descriptors are open. "
|
||||
"Unit not functional until restarted.");
|
||||
else if (r == SOCKET_OPEN_SOME)
|
||||
log_unit_warning(UNIT(s),
|
||||
"Socket unit configuration has changed while unit has been running, "
|
||||
"Unit configuration changed while unit was running, "
|
||||
"and some socket file descriptors have not been opened yet. "
|
||||
"The socket unit is not fully functional until restarted.");
|
||||
"Unit not fully functional until restarted.");
|
||||
}
|
||||
|
||||
if (s->deserialized_state == SOCKET_LISTENING) {
|
||||
|
@ -344,7 +344,7 @@ static void test_sd_device_enumerator_filter_subsystem_one(
|
||||
|
||||
ASSERT_OK(sd_device_enumerator_new(&e));
|
||||
ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, subsystem, true));
|
||||
ASSERT_OK(sd_device_enumerator_add_nomatch_sysname(e, "loop*"));
|
||||
exclude_problematic_devices(e);
|
||||
|
||||
FOREACH_DEVICE(e, d) {
|
||||
const char *syspath;
|
||||
@ -613,9 +613,9 @@ TEST(sd_device_enumerator_add_all_parents) {
|
||||
/* STEP 1: enumerate all block devices without all_parents() */
|
||||
ASSERT_OK(sd_device_enumerator_new(&e));
|
||||
ASSERT_OK(sd_device_enumerator_allow_uninitialized(e));
|
||||
exclude_problematic_devices(e);
|
||||
|
||||
/* filter in only a subsystem */
|
||||
ASSERT_OK(sd_device_enumerator_add_nomatch_sysname(e, "loop*"));
|
||||
ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, "block", true));
|
||||
ASSERT_OK(sd_device_enumerator_add_match_property(e, "DEVTYPE", "partition"));
|
||||
|
||||
@ -758,8 +758,9 @@ TEST(sd_device_new_from_path) {
|
||||
|
||||
ASSERT_OK(sd_device_enumerator_new(&e));
|
||||
ASSERT_OK(sd_device_enumerator_allow_uninitialized(e));
|
||||
exclude_problematic_devices(e);
|
||||
|
||||
ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, "block", true));
|
||||
ASSERT_OK(sd_device_enumerator_add_nomatch_sysname(e, "loop*"));
|
||||
ASSERT_OK(sd_device_enumerator_add_match_property(e, "DEVNAME", "*"));
|
||||
|
||||
FOREACH_DEVICE(e, dev) {
|
||||
|
@ -11,23 +11,43 @@
|
||||
#include "oomd-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "set.h"
|
||||
#include "tests.h"
|
||||
#include "time-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
|
||||
static char *cgroup = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(cgroup, freep);
|
||||
|
||||
static int enter_cgroup_root_cached(void) {
|
||||
static int saved_result = 0; /* 0: not entered, 1: success, negative errno: error */
|
||||
int r;
|
||||
|
||||
if (saved_result != 0)
|
||||
return saved_result;
|
||||
|
||||
r = enter_cgroup_root(&cgroup);
|
||||
if (r < 0) {
|
||||
log_tests_skipped_errno(r, "Failed to enter a test cgroup scope");
|
||||
saved_result = r;
|
||||
} else
|
||||
saved_result = 1;
|
||||
|
||||
return saved_result;
|
||||
}
|
||||
|
||||
static int fork_and_sleep(unsigned sleep_min) {
|
||||
usec_t n, timeout, ts;
|
||||
pid_t pid;
|
||||
int r;
|
||||
|
||||
pid_t pid = fork();
|
||||
assert_se(pid >= 0);
|
||||
|
||||
if (pid == 0) {
|
||||
timeout = sleep_min * USEC_PER_MINUTE;
|
||||
ts = now(CLOCK_MONOTONIC);
|
||||
ASSERT_OK(r = safe_fork("(test-oom-child)", /* flags = */ 0, &pid));
|
||||
if (r == 0) {
|
||||
usec_t timeout = usec_add(now(CLOCK_MONOTONIC), sleep_min * USEC_PER_MINUTE);
|
||||
for (;;) {
|
||||
n = now(CLOCK_MONOTONIC);
|
||||
if (ts + timeout < n) {
|
||||
usec_t n = now(CLOCK_MONOTONIC);
|
||||
if (timeout < n) {
|
||||
log_error("Child timed out waiting to be killed");
|
||||
abort();
|
||||
}
|
||||
@ -38,95 +58,92 @@ static int fork_and_sleep(unsigned sleep_min) {
|
||||
return pid;
|
||||
}
|
||||
|
||||
static void test_oomd_cgroup_kill(void) {
|
||||
_cleanup_free_ char *cgroup_root = NULL, *cgroup = NULL;
|
||||
int pid[2];
|
||||
TEST(oomd_cgroup_kill) {
|
||||
_cleanup_free_ char *subcgroup = NULL;
|
||||
int r;
|
||||
|
||||
if (geteuid() != 0)
|
||||
return (void) log_tests_skipped("not root");
|
||||
|
||||
assert_se(cg_pid_get_path(NULL, 0, &cgroup_root) >= 0);
|
||||
if (enter_cgroup_root_cached() < 0)
|
||||
return;
|
||||
|
||||
/* Create another cgroup below this one for the pids we forked off. We need this to be managed
|
||||
* by the test so that pid1 doesn't delete it before we can read the xattrs. */
|
||||
cgroup = path_join(cgroup_root, "oomdkilltest");
|
||||
assert_se(cgroup);
|
||||
assert_se(cg_create(cgroup) >= 0);
|
||||
ASSERT_NOT_NULL(subcgroup = path_join(cgroup, "oomdkilltest"));
|
||||
ASSERT_OK(cg_create(subcgroup));
|
||||
|
||||
/* If we don't have permissions to set xattrs we're likely in a userns or missing capabilities */
|
||||
r = cg_set_xattr(cgroup, "user.oomd_test", "test", 4, 0);
|
||||
r = cg_set_xattr(subcgroup, "user.oomd_test", "test", 4, 0);
|
||||
if (ERRNO_IS_PRIVILEGE(r) || ERRNO_IS_NOT_SUPPORTED(r))
|
||||
return (void) log_tests_skipped("Cannot set user xattrs");
|
||||
|
||||
/* Do this twice to also check the increment behavior on the xattrs */
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
_cleanup_free_ char *v = NULL;
|
||||
pid_t pid[2];
|
||||
|
||||
for (int j = 0; j < 2; j++) {
|
||||
for (size_t j = 0; j < 2; j++) {
|
||||
pid[j] = fork_and_sleep(5);
|
||||
assert_se(cg_attach(cgroup, pid[j]) >= 0);
|
||||
ASSERT_OK(cg_attach(subcgroup, pid[j]));
|
||||
}
|
||||
|
||||
r = oomd_cgroup_kill(cgroup, false /* recurse */, false /* dry run */);
|
||||
if (r <= 0) {
|
||||
log_debug_errno(r, "Failed to kill processes under %s: %m", cgroup);
|
||||
abort();
|
||||
}
|
||||
ASSERT_OK_POSITIVE(oomd_cgroup_kill(subcgroup, false /* recurse */, false /* dry run */));
|
||||
|
||||
ASSERT_OK(cg_get_xattr(cgroup, "user.oomd_ooms", &v, /* ret_size= */ NULL));
|
||||
assert_se(streq(v, i == 0 ? "1" : "2"));
|
||||
ASSERT_OK(cg_get_xattr(subcgroup, "user.oomd_ooms", &v, /* ret_size= */ NULL));
|
||||
ASSERT_STREQ(v, i == 0 ? "1" : "2");
|
||||
v = mfree(v);
|
||||
|
||||
/* Wait a bit since processes may take some time to be cleaned up. */
|
||||
sleep(2);
|
||||
assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, cgroup) == true);
|
||||
bool empty = false;
|
||||
for (size_t t = 0; t < 100; t++) {
|
||||
usleep_safe(100 * USEC_PER_MSEC);
|
||||
ASSERT_OK(r = cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, subcgroup));
|
||||
if (r > 0) {
|
||||
empty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(empty);
|
||||
|
||||
ASSERT_OK(cg_get_xattr(cgroup, "user.oomd_kill", &v, /* ret_size= */ NULL));
|
||||
assert_se(streq(v, i == 0 ? "2" : "4"));
|
||||
ASSERT_OK(cg_get_xattr(subcgroup, "user.oomd_kill", &v, /* ret_size= */ NULL));
|
||||
ASSERT_STREQ(v, i == 0 ? "2" : "4");
|
||||
}
|
||||
}
|
||||
|
||||
static void test_oomd_cgroup_context_acquire_and_insert(void) {
|
||||
TEST(oomd_cgroup_context_acquire_and_insert) {
|
||||
_cleanup_hashmap_free_ Hashmap *h1 = NULL, *h2 = NULL;
|
||||
_cleanup_(oomd_cgroup_context_freep) OomdCGroupContext *ctx = NULL;
|
||||
_cleanup_free_ char *cgroup = NULL;
|
||||
OomdCGroupContext *c1, *c2;
|
||||
CGroupMask mask;
|
||||
|
||||
if (geteuid() != 0)
|
||||
return (void) log_tests_skipped("not root");
|
||||
|
||||
if (!is_pressure_supported())
|
||||
return (void) log_tests_skipped("system does not support pressure");
|
||||
|
||||
assert_se(cg_mask_supported(&mask) >= 0);
|
||||
if (enter_cgroup_root_cached() < 0)
|
||||
return;
|
||||
|
||||
ASSERT_OK(cg_mask_supported(&mask));
|
||||
if (!FLAGS_SET(mask, CGROUP_MASK_MEMORY))
|
||||
return (void) log_tests_skipped("cgroup memory controller is not available");
|
||||
|
||||
assert_se(cg_pid_get_path(NULL, 0, &cgroup) >= 0);
|
||||
assert_se(oomd_cgroup_context_acquire(cgroup, &ctx) == 0);
|
||||
ASSERT_OK(oomd_cgroup_context_acquire(cgroup, &ctx));
|
||||
|
||||
assert_se(streq(ctx->path, cgroup));
|
||||
assert_se(ctx->current_memory_usage > 0);
|
||||
assert_se(ctx->memory_min == 0);
|
||||
assert_se(ctx->memory_low == 0);
|
||||
assert_se(ctx->swap_usage == 0);
|
||||
assert_se(ctx->last_pgscan == 0);
|
||||
assert_se(ctx->pgscan == 0);
|
||||
ctx = oomd_cgroup_context_free(ctx);
|
||||
ASSERT_STREQ(ctx->path, cgroup);
|
||||
ASSERT_GT(ctx->current_memory_usage, 0u);
|
||||
ASSERT_EQ(ctx->memory_min, 0u);
|
||||
ASSERT_EQ(ctx->memory_low, 0u);
|
||||
ASSERT_EQ(ctx->swap_usage, 0u);
|
||||
ASSERT_EQ(ctx->last_pgscan, 0u);
|
||||
ASSERT_EQ(ctx->pgscan, 0u);
|
||||
ASSERT_NULL(ctx = oomd_cgroup_context_free(ctx));
|
||||
|
||||
assert_se(oomd_cgroup_context_acquire("", &ctx) == 0);
|
||||
assert_se(streq(ctx->path, "/"));
|
||||
assert_se(ctx->current_memory_usage > 0);
|
||||
ASSERT_OK(oomd_cgroup_context_acquire("", &ctx));
|
||||
ASSERT_STREQ(ctx->path, "/");
|
||||
ASSERT_GT(ctx->current_memory_usage, 0u);
|
||||
|
||||
/* Test hashmap inserts */
|
||||
assert_se(h1 = hashmap_new(&oomd_cgroup_ctx_hash_ops));
|
||||
assert_se(oomd_insert_cgroup_context(NULL, h1, cgroup) == 0);
|
||||
c1 = hashmap_get(h1, cgroup);
|
||||
assert_se(c1);
|
||||
assert_se(oomd_insert_cgroup_context(NULL, h1, cgroup) == -EEXIST);
|
||||
ASSERT_NOT_NULL(h1 = hashmap_new(&oomd_cgroup_ctx_hash_ops));
|
||||
ASSERT_OK(oomd_insert_cgroup_context(NULL, h1, cgroup));
|
||||
ASSERT_NOT_NULL(c1 = hashmap_get(h1, cgroup));
|
||||
ASSERT_ERROR(oomd_insert_cgroup_context(NULL, h1, cgroup), EEXIST);
|
||||
|
||||
/* make sure certain values from h1 get updated in h2 */
|
||||
c1->pgscan = UINT64_MAX;
|
||||
@ -134,21 +151,19 @@ static void test_oomd_cgroup_context_acquire_and_insert(void) {
|
||||
c1->mem_pressure_limit_hit_start = 42;
|
||||
c1->mem_pressure_duration_usec = 1234;
|
||||
c1->last_had_mem_reclaim = 888;
|
||||
assert_se(h2 = hashmap_new(&oomd_cgroup_ctx_hash_ops));
|
||||
assert_se(oomd_insert_cgroup_context(h1, h2, cgroup) == 0);
|
||||
c1 = hashmap_get(h1, cgroup);
|
||||
c2 = hashmap_get(h2, cgroup);
|
||||
assert_se(c1);
|
||||
assert_se(c2);
|
||||
assert_se(c1 != c2);
|
||||
assert_se(c2->last_pgscan == UINT64_MAX);
|
||||
assert_se(c2->mem_pressure_limit == 6789);
|
||||
assert_se(c2->mem_pressure_limit_hit_start == 42);
|
||||
assert_se(c2->mem_pressure_duration_usec == 1234);
|
||||
assert_se(c2->last_had_mem_reclaim == 888); /* assumes the live pgscan is less than UINT64_MAX */
|
||||
ASSERT_NOT_NULL(h2 = hashmap_new(&oomd_cgroup_ctx_hash_ops));
|
||||
ASSERT_OK(oomd_insert_cgroup_context(h1, h2, cgroup));
|
||||
ASSERT_NOT_NULL(c1 = hashmap_get(h1, cgroup));
|
||||
ASSERT_NOT_NULL(c2 = hashmap_get(h2, cgroup));
|
||||
ASSERT_TRUE(c1 != c2);
|
||||
ASSERT_EQ(c2->last_pgscan, UINT64_MAX);
|
||||
ASSERT_EQ(c2->mem_pressure_limit, 6789u);
|
||||
ASSERT_EQ(c2->mem_pressure_limit_hit_start, 42u);
|
||||
ASSERT_EQ(c2->mem_pressure_duration_usec, 1234u);
|
||||
ASSERT_EQ(c2->last_had_mem_reclaim, 888u); /* assumes the live pgscan is less than UINT64_MAX */
|
||||
}
|
||||
|
||||
static void test_oomd_update_cgroup_contexts_between_hashmaps(void) {
|
||||
TEST(oomd_update_cgroup_contexts_between_hashmaps) {
|
||||
_cleanup_hashmap_free_ Hashmap *h_old = NULL, *h_new = NULL;
|
||||
OomdCGroupContext *c_old, *c_new;
|
||||
char **paths = STRV_MAKE("/0.slice",
|
||||
@ -176,57 +191,55 @@ static void test_oomd_update_cgroup_contexts_between_hashmaps(void) {
|
||||
.pgscan = 101 },
|
||||
};
|
||||
|
||||
assert_se(h_old = hashmap_new(&string_hash_ops));
|
||||
assert_se(hashmap_put(h_old, paths[0], &ctx_old[0]) >= 0);
|
||||
assert_se(hashmap_put(h_old, paths[1], &ctx_old[1]) >= 0);
|
||||
ASSERT_NOT_NULL(h_old = hashmap_new(&string_hash_ops));
|
||||
ASSERT_OK(hashmap_put(h_old, paths[0], &ctx_old[0]));
|
||||
ASSERT_OK(hashmap_put(h_old, paths[1], &ctx_old[1]));
|
||||
|
||||
assert_se(h_new = hashmap_new(&string_hash_ops));
|
||||
assert_se(hashmap_put(h_new, paths[0], &ctx_new[0]) >= 0);
|
||||
assert_se(hashmap_put(h_new, paths[1], &ctx_new[1]) >= 0);
|
||||
ASSERT_NOT_NULL(h_new = hashmap_new(&string_hash_ops));
|
||||
ASSERT_OK(hashmap_put(h_new, paths[0], &ctx_new[0]));
|
||||
ASSERT_OK(hashmap_put(h_new, paths[1], &ctx_new[1]));
|
||||
|
||||
oomd_update_cgroup_contexts_between_hashmaps(h_old, h_new);
|
||||
|
||||
assert_se(c_old = hashmap_get(h_old, "/0.slice"));
|
||||
assert_se(c_new = hashmap_get(h_new, "/0.slice"));
|
||||
assert_se(c_old->pgscan == c_new->last_pgscan);
|
||||
assert_se(c_old->mem_pressure_limit == c_new->mem_pressure_limit);
|
||||
assert_se(c_old->mem_pressure_limit_hit_start == c_new->mem_pressure_limit_hit_start);
|
||||
assert_se(c_old->mem_pressure_duration_usec == c_new->mem_pressure_duration_usec);
|
||||
assert_se(c_old->last_had_mem_reclaim == c_new->last_had_mem_reclaim);
|
||||
ASSERT_NOT_NULL(c_old = hashmap_get(h_old, "/0.slice"));
|
||||
ASSERT_NOT_NULL(c_new = hashmap_get(h_new, "/0.slice"));
|
||||
ASSERT_EQ(c_old->pgscan, c_new->last_pgscan);
|
||||
ASSERT_EQ(c_old->mem_pressure_limit, c_new->mem_pressure_limit);
|
||||
ASSERT_EQ(c_old->mem_pressure_limit_hit_start, c_new->mem_pressure_limit_hit_start);
|
||||
ASSERT_EQ(c_old->mem_pressure_duration_usec, c_new->mem_pressure_duration_usec);
|
||||
ASSERT_EQ(c_old->last_had_mem_reclaim, c_new->last_had_mem_reclaim);
|
||||
|
||||
assert_se(c_old = hashmap_get(h_old, "/1.slice"));
|
||||
assert_se(c_new = hashmap_get(h_new, "/1.slice"));
|
||||
assert_se(c_old->pgscan == c_new->last_pgscan);
|
||||
assert_se(c_old->mem_pressure_limit == c_new->mem_pressure_limit);
|
||||
assert_se(c_old->mem_pressure_limit_hit_start == c_new->mem_pressure_limit_hit_start);
|
||||
assert_se(c_old->mem_pressure_duration_usec == c_new->mem_pressure_duration_usec);
|
||||
assert_se(c_new->last_had_mem_reclaim > c_old->last_had_mem_reclaim);
|
||||
ASSERT_NOT_NULL(c_old = hashmap_get(h_old, "/1.slice"));
|
||||
ASSERT_NOT_NULL(c_new = hashmap_get(h_new, "/1.slice"));
|
||||
ASSERT_EQ(c_old->pgscan, c_new->last_pgscan);
|
||||
ASSERT_EQ(c_old->mem_pressure_limit, c_new->mem_pressure_limit);
|
||||
ASSERT_EQ(c_old->mem_pressure_limit_hit_start, c_new->mem_pressure_limit_hit_start);
|
||||
ASSERT_EQ(c_old->mem_pressure_duration_usec, c_new->mem_pressure_duration_usec);
|
||||
ASSERT_LT(c_old->last_had_mem_reclaim, c_new->last_had_mem_reclaim);
|
||||
}
|
||||
|
||||
static void test_oomd_system_context_acquire(void) {
|
||||
TEST(oomd_system_context_acquire) {
|
||||
_cleanup_(unlink_tempfilep) char path[] = "/tmp/oomdgetsysctxtestXXXXXX";
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
OomdSystemContext ctx;
|
||||
|
||||
if (geteuid() != 0)
|
||||
return (void) log_tests_skipped("not root");
|
||||
ASSERT_OK(fd = mkostemp_safe(path));
|
||||
|
||||
assert_se((fd = mkostemp_safe(path)) >= 0);
|
||||
ASSERT_ERROR(oomd_system_context_acquire("/verylikelynonexistentpath", &ctx), ENOENT);
|
||||
|
||||
assert_se(oomd_system_context_acquire("/verylikelynonexistentpath", &ctx) == -ENOENT);
|
||||
ASSERT_ERROR(oomd_system_context_acquire(path, &ctx), EINVAL);
|
||||
|
||||
assert_se(oomd_system_context_acquire(path, &ctx) == -EINVAL);
|
||||
ASSERT_OK(write_string_file(path, "some\nwords\nacross\nmultiple\nlines", WRITE_STRING_FILE_CREATE));
|
||||
ASSERT_ERROR(oomd_system_context_acquire(path, &ctx), EINVAL);
|
||||
|
||||
assert_se(write_string_file(path, "some\nwords\nacross\nmultiple\nlines", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(oomd_system_context_acquire(path, &ctx) == -EINVAL);
|
||||
|
||||
assert_se(write_string_file(path, "MemTotal: 32495256 kB trailing\n"
|
||||
ASSERT_OK(write_string_file(path, "MemTotal: 32495256 kB trailing\n"
|
||||
"MemFree: 9880512 kB data\n"
|
||||
"SwapTotal: 8388604 kB is\n"
|
||||
"SwapFree: 7604 kB bad\n", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(oomd_system_context_acquire(path, &ctx) == -EINVAL);
|
||||
"SwapFree: 7604 kB bad\n",
|
||||
WRITE_STRING_FILE_CREATE));
|
||||
ASSERT_ERROR(oomd_system_context_acquire(path, &ctx), EINVAL);
|
||||
|
||||
assert_se(write_string_file(path, "MemTotal: 32495256 kB\n"
|
||||
ASSERT_OK(write_string_file(path, "MemTotal: 32495256 kB\n"
|
||||
"MemFree: 9880512 kB\n"
|
||||
"MemAvailable: 21777088 kB\n"
|
||||
"Buffers: 5968 kB\n"
|
||||
@ -234,74 +247,75 @@ static void test_oomd_system_context_acquire(void) {
|
||||
"Unevictable: 740004 kB\n"
|
||||
"Mlocked: 4484 kB\n"
|
||||
"SwapTotal: 8388604 kB\n"
|
||||
"SwapFree: 7604 kB\n", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(oomd_system_context_acquire(path, &ctx) == 0);
|
||||
assert_se(ctx.mem_total == 33275142144);
|
||||
assert_se(ctx.mem_used == 10975404032);
|
||||
assert_se(ctx.swap_total == 8589930496);
|
||||
assert_se(ctx.swap_used == 8582144000);
|
||||
"SwapFree: 7604 kB\n",
|
||||
WRITE_STRING_FILE_CREATE));
|
||||
ASSERT_OK(oomd_system_context_acquire(path, &ctx));
|
||||
ASSERT_EQ(ctx.mem_total, 33275142144u);
|
||||
ASSERT_EQ(ctx.mem_used, 10975404032u);
|
||||
ASSERT_EQ(ctx.swap_total, 8589930496u);
|
||||
ASSERT_EQ(ctx.swap_used, 8582144000u);
|
||||
}
|
||||
|
||||
static void test_oomd_pressure_above(void) {
|
||||
TEST(oomd_pressure_above) {
|
||||
_cleanup_hashmap_free_ Hashmap *h1 = NULL, *h2 = NULL;
|
||||
_cleanup_set_free_ Set *t1 = NULL, *t2 = NULL, *t3 = NULL;
|
||||
OomdCGroupContext ctx[2] = {}, *c;
|
||||
loadavg_t threshold;
|
||||
|
||||
assert_se(store_loadavg_fixed_point(80, 0, &threshold) == 0);
|
||||
ASSERT_OK(store_loadavg_fixed_point(80, 0, &threshold));
|
||||
|
||||
/* /herp.slice */
|
||||
assert_se(store_loadavg_fixed_point(99, 99, &(ctx[0].memory_pressure.avg10)) == 0);
|
||||
assert_se(store_loadavg_fixed_point(99, 99, &(ctx[0].memory_pressure.avg60)) == 0);
|
||||
assert_se(store_loadavg_fixed_point(99, 99, &(ctx[0].memory_pressure.avg300)) == 0);
|
||||
ASSERT_OK(store_loadavg_fixed_point(99, 99, &(ctx[0].memory_pressure.avg10)));
|
||||
ASSERT_OK(store_loadavg_fixed_point(99, 99, &(ctx[0].memory_pressure.avg60)));
|
||||
ASSERT_OK(store_loadavg_fixed_point(99, 99, &(ctx[0].memory_pressure.avg300)));
|
||||
ctx[0].mem_pressure_limit = threshold;
|
||||
/* Set memory pressure duration to 0 since we use the real system monotonic clock
|
||||
* in oomd_pressure_above() and we want to avoid this test depending on timing. */
|
||||
ctx[0].mem_pressure_duration_usec = 0;
|
||||
|
||||
/* /derp.slice */
|
||||
assert_se(store_loadavg_fixed_point(1, 11, &(ctx[1].memory_pressure.avg10)) == 0);
|
||||
assert_se(store_loadavg_fixed_point(1, 11, &(ctx[1].memory_pressure.avg60)) == 0);
|
||||
assert_se(store_loadavg_fixed_point(1, 11, &(ctx[1].memory_pressure.avg300)) == 0);
|
||||
ASSERT_OK(store_loadavg_fixed_point(1, 11, &(ctx[1].memory_pressure.avg10)));
|
||||
ASSERT_OK(store_loadavg_fixed_point(1, 11, &(ctx[1].memory_pressure.avg60)));
|
||||
ASSERT_OK(store_loadavg_fixed_point(1, 11, &(ctx[1].memory_pressure.avg300)));
|
||||
ctx[1].mem_pressure_limit = threshold;
|
||||
ctx[1].mem_pressure_duration_usec = 0;
|
||||
|
||||
/* High memory pressure */
|
||||
assert_se(h1 = hashmap_new(&string_hash_ops));
|
||||
assert_se(hashmap_put(h1, "/herp.slice", &ctx[0]) >= 0);
|
||||
assert_se(oomd_pressure_above(h1, &t1) == 1);
|
||||
assert_se(set_contains(t1, &ctx[0]));
|
||||
assert_se(c = hashmap_get(h1, "/herp.slice"));
|
||||
assert_se(c->mem_pressure_limit_hit_start > 0);
|
||||
ASSERT_NOT_NULL(h1 = hashmap_new(&string_hash_ops));
|
||||
ASSERT_OK(hashmap_put(h1, "/herp.slice", &ctx[0]));
|
||||
ASSERT_OK_POSITIVE(oomd_pressure_above(h1, &t1));
|
||||
ASSERT_TRUE(set_contains(t1, &ctx[0]));
|
||||
ASSERT_NOT_NULL(c = hashmap_get(h1, "/herp.slice"));
|
||||
ASSERT_GT(c->mem_pressure_limit_hit_start, 0u);
|
||||
|
||||
/* Low memory pressure */
|
||||
assert_se(h2 = hashmap_new(&string_hash_ops));
|
||||
assert_se(hashmap_put(h2, "/derp.slice", &ctx[1]) >= 0);
|
||||
assert_se(oomd_pressure_above(h2, &t2) == 0);
|
||||
assert_se(!t2);
|
||||
assert_se(c = hashmap_get(h2, "/derp.slice"));
|
||||
assert_se(c->mem_pressure_limit_hit_start == 0);
|
||||
ASSERT_NOT_NULL(h2 = hashmap_new(&string_hash_ops));
|
||||
ASSERT_OK(hashmap_put(h2, "/derp.slice", &ctx[1]));
|
||||
ASSERT_OK_ZERO(oomd_pressure_above(h2, &t2));
|
||||
ASSERT_NULL(t2);
|
||||
ASSERT_NOT_NULL(c = hashmap_get(h2, "/derp.slice"));
|
||||
ASSERT_EQ(c->mem_pressure_limit_hit_start, 0u);
|
||||
|
||||
/* High memory pressure w/ multiple cgroups */
|
||||
assert_se(hashmap_put(h1, "/derp.slice", &ctx[1]) >= 0);
|
||||
assert_se(oomd_pressure_above(h1, &t3) == 1);
|
||||
assert_se(set_contains(t3, &ctx[0]));
|
||||
assert_se(set_size(t3) == 1);
|
||||
assert_se(c = hashmap_get(h1, "/herp.slice"));
|
||||
assert_se(c->mem_pressure_limit_hit_start > 0);
|
||||
assert_se(c = hashmap_get(h1, "/derp.slice"));
|
||||
assert_se(c->mem_pressure_limit_hit_start == 0);
|
||||
ASSERT_OK(hashmap_put(h1, "/derp.slice", &ctx[1]));
|
||||
ASSERT_OK_POSITIVE(oomd_pressure_above(h1, &t3));
|
||||
ASSERT_TRUE(set_contains(t3, &ctx[0]));
|
||||
ASSERT_EQ(set_size(t3), 1u);
|
||||
ASSERT_NOT_NULL(c = hashmap_get(h1, "/herp.slice"));
|
||||
ASSERT_GT(c->mem_pressure_limit_hit_start, 0u);
|
||||
ASSERT_NOT_NULL(c = hashmap_get(h1, "/derp.slice"));
|
||||
ASSERT_EQ(c->mem_pressure_limit_hit_start, 0u);
|
||||
}
|
||||
|
||||
static void test_oomd_mem_and_swap_free_below(void) {
|
||||
TEST(oomd_mem_and_swap_free_below) {
|
||||
OomdSystemContext ctx = (OomdSystemContext) {
|
||||
.mem_total = UINT64_C(20971512) * 1024U,
|
||||
.mem_used = UINT64_C(3310136) * 1024U,
|
||||
.swap_total = UINT64_C(20971512) * 1024U,
|
||||
.swap_used = UINT64_C(20971440) * 1024U,
|
||||
};
|
||||
assert_se(oomd_mem_available_below(&ctx, 2000) == false);
|
||||
assert_se(oomd_swap_free_below(&ctx, 2000) == true);
|
||||
ASSERT_FALSE(oomd_mem_available_below(&ctx, 2000));
|
||||
ASSERT_TRUE(oomd_swap_free_below(&ctx, 2000));
|
||||
|
||||
ctx = (OomdSystemContext) {
|
||||
.mem_total = UINT64_C(20971512) * 1024U,
|
||||
@ -309,8 +323,8 @@ static void test_oomd_mem_and_swap_free_below(void) {
|
||||
.swap_total = UINT64_C(20971512) * 1024U,
|
||||
.swap_used = UINT64_C(3310136) * 1024U,
|
||||
};
|
||||
assert_se(oomd_mem_available_below(&ctx, 2000) == true);
|
||||
assert_se(oomd_swap_free_below(&ctx, 2000) == false);
|
||||
ASSERT_TRUE(oomd_mem_available_below(&ctx, 2000));
|
||||
ASSERT_FALSE(oomd_swap_free_below(&ctx, 2000));
|
||||
|
||||
ctx = (OomdSystemContext) {
|
||||
.mem_total = 0,
|
||||
@ -318,11 +332,11 @@ static void test_oomd_mem_and_swap_free_below(void) {
|
||||
.swap_total = 0,
|
||||
.swap_used = 0,
|
||||
};
|
||||
assert_se(oomd_mem_available_below(&ctx, 2000) == false);
|
||||
assert_se(oomd_swap_free_below(&ctx, 2000) == false);
|
||||
ASSERT_FALSE(oomd_mem_available_below(&ctx, 2000));
|
||||
ASSERT_FALSE(oomd_swap_free_below(&ctx, 2000));
|
||||
}
|
||||
|
||||
static void test_oomd_sort_cgroups(void) {
|
||||
TEST(oomd_sort_cgroups) {
|
||||
_cleanup_hashmap_free_ Hashmap *h = NULL;
|
||||
_cleanup_free_ OomdCGroupContext **sorted_cgroups = NULL;
|
||||
char **paths = STRV_MAKE("/herp.slice",
|
||||
@ -371,37 +385,37 @@ static void test_oomd_sort_cgroups(void) {
|
||||
.preference = MANAGED_OOM_PREFERENCE_AVOID },
|
||||
};
|
||||
|
||||
assert_se(h = hashmap_new(&string_hash_ops));
|
||||
ASSERT_NOT_NULL(h = hashmap_new(&string_hash_ops));
|
||||
|
||||
assert_se(hashmap_put(h, "/herp.slice", &ctx[0]) >= 0);
|
||||
assert_se(hashmap_put(h, "/herp.slice/derp.scope", &ctx[1]) >= 0);
|
||||
assert_se(hashmap_put(h, "/herp.slice/derp.scope/sheep.service", &ctx[2]) >= 0);
|
||||
assert_se(hashmap_put(h, "/zupa.slice", &ctx[3]) >= 0);
|
||||
assert_se(hashmap_put(h, "/boop.slice", &ctx[4]) >= 0);
|
||||
assert_se(hashmap_put(h, "/omitted.slice", &ctx[5]) >= 0);
|
||||
assert_se(hashmap_put(h, "/avoid.slice", &ctx[6]) >= 0);
|
||||
ASSERT_OK(hashmap_put(h, "/herp.slice", &ctx[0]));
|
||||
ASSERT_OK(hashmap_put(h, "/herp.slice/derp.scope", &ctx[1]));
|
||||
ASSERT_OK(hashmap_put(h, "/herp.slice/derp.scope/sheep.service", &ctx[2]));
|
||||
ASSERT_OK(hashmap_put(h, "/zupa.slice", &ctx[3]));
|
||||
ASSERT_OK(hashmap_put(h, "/boop.slice", &ctx[4]));
|
||||
ASSERT_OK(hashmap_put(h, "/omitted.slice", &ctx[5]));
|
||||
ASSERT_OK(hashmap_put(h, "/avoid.slice", &ctx[6]));
|
||||
|
||||
assert_se(oomd_sort_cgroup_contexts(h, compare_swap_usage, NULL, &sorted_cgroups) == 6);
|
||||
assert_se(sorted_cgroups[0] == &ctx[1]);
|
||||
assert_se(sorted_cgroups[1] == &ctx[2]);
|
||||
assert_se(sorted_cgroups[2] == &ctx[0]);
|
||||
assert_se(sorted_cgroups[3] == &ctx[4]);
|
||||
assert_se(sorted_cgroups[4] == &ctx[3]);
|
||||
assert_se(sorted_cgroups[5] == &ctx[6]);
|
||||
ASSERT_OK_EQ(oomd_sort_cgroup_contexts(h, compare_swap_usage, NULL, &sorted_cgroups), 6);
|
||||
ASSERT_PTR_EQ(sorted_cgroups[0], &ctx[1]);
|
||||
ASSERT_PTR_EQ(sorted_cgroups[1], &ctx[2]);
|
||||
ASSERT_PTR_EQ(sorted_cgroups[2], &ctx[0]);
|
||||
ASSERT_PTR_EQ(sorted_cgroups[3], &ctx[4]);
|
||||
ASSERT_PTR_EQ(sorted_cgroups[4], &ctx[3]);
|
||||
ASSERT_PTR_EQ(sorted_cgroups[5], &ctx[6]);
|
||||
sorted_cgroups = mfree(sorted_cgroups);
|
||||
|
||||
assert_se(oomd_sort_cgroup_contexts(h, compare_pgscan_rate_and_memory_usage, NULL, &sorted_cgroups) == 6);
|
||||
assert_se(sorted_cgroups[0] == &ctx[0]);
|
||||
assert_se(sorted_cgroups[1] == &ctx[2]);
|
||||
assert_se(sorted_cgroups[2] == &ctx[3]);
|
||||
assert_se(sorted_cgroups[3] == &ctx[1]);
|
||||
assert_se(sorted_cgroups[4] == &ctx[4]);
|
||||
assert_se(sorted_cgroups[5] == &ctx[6]);
|
||||
ASSERT_OK_EQ(oomd_sort_cgroup_contexts(h, compare_pgscan_rate_and_memory_usage, NULL, &sorted_cgroups), 6);
|
||||
ASSERT_PTR_EQ(sorted_cgroups[0], &ctx[0]);
|
||||
ASSERT_PTR_EQ(sorted_cgroups[1], &ctx[2]);
|
||||
ASSERT_PTR_EQ(sorted_cgroups[2], &ctx[3]);
|
||||
ASSERT_PTR_EQ(sorted_cgroups[3], &ctx[1]);
|
||||
ASSERT_PTR_EQ(sorted_cgroups[4], &ctx[4]);
|
||||
ASSERT_PTR_EQ(sorted_cgroups[5], &ctx[6]);
|
||||
sorted_cgroups = mfree(sorted_cgroups);
|
||||
|
||||
assert_se(oomd_sort_cgroup_contexts(h, compare_pgscan_rate_and_memory_usage, "/herp.slice/derp.scope", &sorted_cgroups) == 2);
|
||||
assert_se(sorted_cgroups[0] == &ctx[2]);
|
||||
assert_se(sorted_cgroups[1] == &ctx[1]);
|
||||
ASSERT_OK_EQ(oomd_sort_cgroup_contexts(h, compare_pgscan_rate_and_memory_usage, "/herp.slice/derp.scope", &sorted_cgroups), 2);
|
||||
ASSERT_PTR_EQ(sorted_cgroups[0], &ctx[2]);
|
||||
ASSERT_PTR_EQ(sorted_cgroups[1], &ctx[1]);
|
||||
ASSERT_NULL(sorted_cgroups[2]);
|
||||
ASSERT_NULL(sorted_cgroups[3]);
|
||||
ASSERT_NULL(sorted_cgroups[4]);
|
||||
@ -409,27 +423,24 @@ static void test_oomd_sort_cgroups(void) {
|
||||
ASSERT_NULL(sorted_cgroups[6]);
|
||||
}
|
||||
|
||||
static void test_oomd_fetch_cgroup_oom_preference(void) {
|
||||
TEST(oomd_fetch_cgroup_oom_preference) {
|
||||
_cleanup_(oomd_cgroup_context_freep) OomdCGroupContext *ctx = NULL;
|
||||
_cleanup_free_ char *cgroup = NULL;
|
||||
ManagedOOMPreference root_pref;
|
||||
CGroupMask mask;
|
||||
bool test_xattrs;
|
||||
int root_xattrs, r;
|
||||
|
||||
if (geteuid() != 0)
|
||||
return (void) log_tests_skipped("not root");
|
||||
int r;
|
||||
|
||||
if (!is_pressure_supported())
|
||||
return (void) log_tests_skipped("system does not support pressure");
|
||||
|
||||
assert_se(cg_mask_supported(&mask) >= 0);
|
||||
if (enter_cgroup_root_cached() < 0)
|
||||
return;
|
||||
|
||||
ASSERT_OK(cg_mask_supported(&mask));
|
||||
if (!FLAGS_SET(mask, CGROUP_MASK_MEMORY))
|
||||
return (void) log_tests_skipped("cgroup memory controller is not available");
|
||||
|
||||
assert_se(cg_pid_get_path(NULL, 0, &cgroup) >= 0);
|
||||
assert_se(oomd_cgroup_context_acquire(cgroup, &ctx) == 0);
|
||||
ASSERT_OK(oomd_cgroup_context_acquire(cgroup, &ctx));
|
||||
|
||||
/* If we don't have permissions to set xattrs we're likely in a userns or missing capabilities
|
||||
* so skip the xattr portions of the test. */
|
||||
@ -437,77 +448,56 @@ static void test_oomd_fetch_cgroup_oom_preference(void) {
|
||||
test_xattrs = !ERRNO_IS_PRIVILEGE(r) && !ERRNO_IS_NOT_SUPPORTED(r);
|
||||
|
||||
if (test_xattrs) {
|
||||
assert_se(oomd_fetch_cgroup_oom_preference(ctx, NULL) == 0);
|
||||
assert_se(cg_set_xattr(cgroup, "user.oomd_omit", "1", 1, 0) >= 0);
|
||||
assert_se(cg_set_xattr(cgroup, "user.oomd_avoid", "1", 1, 0) >= 0);
|
||||
ASSERT_OK(oomd_fetch_cgroup_oom_preference(ctx, NULL));
|
||||
ASSERT_OK(cg_set_xattr(cgroup, "user.oomd_omit", "1", 1, 0));
|
||||
ASSERT_OK(cg_set_xattr(cgroup, "user.oomd_avoid", "1", 1, 0));
|
||||
|
||||
/* omit takes precedence over avoid when both are set to true */
|
||||
assert_se(oomd_fetch_cgroup_oom_preference(ctx, NULL) == 0);
|
||||
assert_se(ctx->preference == MANAGED_OOM_PREFERENCE_OMIT);
|
||||
ASSERT_OK(oomd_fetch_cgroup_oom_preference(ctx, NULL));
|
||||
ASSERT_EQ(ctx->preference, geteuid() == 0 ? MANAGED_OOM_PREFERENCE_OMIT : MANAGED_OOM_PREFERENCE_NONE);
|
||||
} else {
|
||||
assert_se(oomd_fetch_cgroup_oom_preference(ctx, NULL) < 0);
|
||||
assert_se(ctx->preference == MANAGED_OOM_PREFERENCE_NONE);
|
||||
ASSERT_FAIL(oomd_fetch_cgroup_oom_preference(ctx, NULL));
|
||||
ASSERT_EQ(ctx->preference, MANAGED_OOM_PREFERENCE_NONE);
|
||||
}
|
||||
ctx = oomd_cgroup_context_free(ctx);
|
||||
|
||||
/* also check when only avoid is set to true */
|
||||
if (test_xattrs) {
|
||||
assert_se(cg_set_xattr(cgroup, "user.oomd_omit", "0", 1, 0) >= 0);
|
||||
assert_se(cg_set_xattr(cgroup, "user.oomd_avoid", "1", 1, 0) >= 0);
|
||||
assert_se(oomd_cgroup_context_acquire(cgroup, &ctx) == 0);
|
||||
assert_se(oomd_fetch_cgroup_oom_preference(ctx, NULL) == 0);
|
||||
assert_se(ctx->preference == MANAGED_OOM_PREFERENCE_AVOID);
|
||||
ASSERT_OK(cg_set_xattr(cgroup, "user.oomd_omit", "0", 1, 0));
|
||||
ASSERT_OK(cg_set_xattr(cgroup, "user.oomd_avoid", "1", 1, 0));
|
||||
ASSERT_OK(oomd_cgroup_context_acquire(cgroup, &ctx));
|
||||
ASSERT_OK(oomd_fetch_cgroup_oom_preference(ctx, NULL));
|
||||
ASSERT_EQ(ctx->preference, geteuid() == 0 ? MANAGED_OOM_PREFERENCE_AVOID : MANAGED_OOM_PREFERENCE_NONE);
|
||||
ctx = oomd_cgroup_context_free(ctx);
|
||||
}
|
||||
|
||||
/* Test the root cgroup */
|
||||
/* Root cgroup is live and not made on demand like the cgroup the test runs in. It can have varying
|
||||
* xattrs set already so let's read in the booleans first to get the final preference value. */
|
||||
assert_se(oomd_cgroup_context_acquire("", &ctx) == 0);
|
||||
root_xattrs = cg_get_xattr_bool("", "user.oomd_omit");
|
||||
root_pref = root_xattrs > 0 ? MANAGED_OOM_PREFERENCE_OMIT : MANAGED_OOM_PREFERENCE_NONE;
|
||||
root_xattrs = cg_get_xattr_bool("", "user.oomd_avoid");
|
||||
root_pref = root_xattrs > 0 ? MANAGED_OOM_PREFERENCE_AVOID : MANAGED_OOM_PREFERENCE_NONE;
|
||||
assert_se(oomd_fetch_cgroup_oom_preference(ctx, NULL) == 0);
|
||||
assert_se(ctx->preference == root_pref);
|
||||
ASSERT_OK(oomd_cgroup_context_acquire("", &ctx));
|
||||
root_pref = MANAGED_OOM_PREFERENCE_NONE;
|
||||
if (cg_get_xattr_bool("", "user.oomd_avoid") > 0)
|
||||
root_pref = MANAGED_OOM_PREFERENCE_AVOID;
|
||||
if (cg_get_xattr_bool("", "user.oomd_omit") > 0)
|
||||
root_pref = MANAGED_OOM_PREFERENCE_OMIT;
|
||||
ASSERT_OK(oomd_fetch_cgroup_oom_preference(ctx, NULL));
|
||||
ASSERT_EQ(ctx->preference, root_pref);
|
||||
|
||||
assert_se(oomd_fetch_cgroup_oom_preference(ctx, "/herp.slice/derp.scope") == -EINVAL);
|
||||
ASSERT_ERROR(oomd_fetch_cgroup_oom_preference(ctx, "/herp.slice/derp.scope"), EINVAL);
|
||||
|
||||
/* Assert that avoid/omit are not set if the cgroup and prefix are not
|
||||
* owned by the same user. */
|
||||
if (test_xattrs && !empty_or_root(cgroup)) {
|
||||
if (test_xattrs && !empty_or_root(cgroup) && geteuid() == 0) {
|
||||
ctx = oomd_cgroup_context_free(ctx);
|
||||
assert_se(cg_set_access(cgroup, 61183, 0) >= 0);
|
||||
assert_se(oomd_cgroup_context_acquire(cgroup, &ctx) == 0);
|
||||
ASSERT_OK(cg_set_access(cgroup, 61183, 0));
|
||||
ASSERT_OK(oomd_cgroup_context_acquire(cgroup, &ctx));
|
||||
|
||||
assert_se(oomd_fetch_cgroup_oom_preference(ctx, NULL) == 0);
|
||||
assert_se(ctx->preference == MANAGED_OOM_PREFERENCE_NONE);
|
||||
ASSERT_OK(oomd_fetch_cgroup_oom_preference(ctx, NULL));
|
||||
ASSERT_EQ(ctx->preference, MANAGED_OOM_PREFERENCE_NONE);
|
||||
|
||||
assert_se(oomd_fetch_cgroup_oom_preference(ctx, ctx->path) == 0);
|
||||
assert_se(ctx->preference == MANAGED_OOM_PREFERENCE_AVOID);
|
||||
ASSERT_OK(oomd_fetch_cgroup_oom_preference(ctx, ctx->path));
|
||||
ASSERT_EQ(ctx->preference, MANAGED_OOM_PREFERENCE_AVOID);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int r;
|
||||
|
||||
test_setup_logging(LOG_DEBUG);
|
||||
|
||||
test_oomd_update_cgroup_contexts_between_hashmaps();
|
||||
test_oomd_system_context_acquire();
|
||||
test_oomd_pressure_above();
|
||||
test_oomd_mem_and_swap_free_below();
|
||||
test_oomd_sort_cgroups();
|
||||
|
||||
/* The following tests operate on live cgroups */
|
||||
|
||||
r = enter_cgroup_root(NULL);
|
||||
if (r < 0)
|
||||
return log_tests_skipped_errno(r, "failed to enter a test cgroup scope");
|
||||
|
||||
test_oomd_cgroup_kill();
|
||||
test_oomd_cgroup_context_acquire_and_insert();
|
||||
test_oomd_fetch_cgroup_oom_preference();
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "log.h"
|
||||
#include "namespace-util.h"
|
||||
#include "path-util.h"
|
||||
#include "pidref.h"
|
||||
#include "process-util.h"
|
||||
#include "random-util.h"
|
||||
#include "rlimit-util.h"
|
||||
@ -38,13 +39,11 @@
|
||||
#include "uid-range.h"
|
||||
|
||||
char* setup_fake_runtime_dir(void) {
|
||||
char t[] = "/tmp/fake-xdg-runtime-XXXXXX", *p;
|
||||
char *t;
|
||||
|
||||
assert_se(mkdtemp(t));
|
||||
assert_se(setenv("XDG_RUNTIME_DIR", t, 1) >= 0);
|
||||
assert_se(p = strdup(t));
|
||||
|
||||
return p;
|
||||
ASSERT_OK(mkdtemp_malloc("/tmp/fake-xdg-runtime-XXXXXX", &t));
|
||||
ASSERT_OK(setenv("XDG_RUNTIME_DIR", t, /* overwrite = */ true));
|
||||
return t;
|
||||
}
|
||||
|
||||
static void load_testdata_env(void) {
|
||||
@ -57,18 +56,18 @@ static void load_testdata_env(void) {
|
||||
return;
|
||||
called = true;
|
||||
|
||||
assert_se(readlink_and_make_absolute("/proc/self/exe", &s) >= 0);
|
||||
assert_se(path_extract_directory(s, &d) >= 0);
|
||||
assert_se(envpath = path_join(d, "systemd-runtest.env"));
|
||||
ASSERT_OK(readlink_and_make_absolute("/proc/self/exe", &s));
|
||||
ASSERT_OK(path_extract_directory(s, &d));
|
||||
ASSERT_NOT_NULL(envpath = path_join(d, "systemd-runtest.env"));
|
||||
|
||||
r = load_env_file_pairs(NULL, envpath, &pairs);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Reading %s failed: %m", envpath);
|
||||
log_debug_errno(r, "Reading %s failed, ignoring: %m", envpath);
|
||||
return;
|
||||
}
|
||||
|
||||
STRV_FOREACH_PAIR(k, v, pairs)
|
||||
assert_se(setenv(*k, *v, 0) >= 0);
|
||||
ASSERT_OK(setenv(*k, *v, /* overwrite = */ false));
|
||||
}
|
||||
|
||||
int get_testdata_dir(const char *suffix, char **ret) {
|
||||
@ -144,16 +143,14 @@ int write_tmpfile(char *pattern, const char *contents) {
|
||||
}
|
||||
|
||||
bool have_namespaces(void) {
|
||||
siginfo_t si = {};
|
||||
pid_t pid;
|
||||
_cleanup_(pidref_done) PidRef pid = PIDREF_NULL;
|
||||
int r;
|
||||
|
||||
/* Checks whether namespaces are available. In some cases they aren't. We do this by calling unshare(), and we
|
||||
* do so in a child process in order not to affect our own process. */
|
||||
|
||||
pid = fork();
|
||||
assert_se(pid >= 0);
|
||||
|
||||
if (pid == 0) {
|
||||
ASSERT_OK(r = pidref_safe_fork("(have_namespace)", /* flags = */ 0, &pid));
|
||||
if (r == 0) {
|
||||
/* child */
|
||||
if (detach_mount_namespace() < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
@ -161,13 +158,11 @@ bool have_namespaces(void) {
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
assert_se(waitid(P_PID, pid, &si, WEXITED) >= 0);
|
||||
assert_se(si.si_code == CLD_EXITED);
|
||||
|
||||
if (si.si_status == EXIT_SUCCESS)
|
||||
ASSERT_OK(r = pidref_wait_for_terminate_and_check("(have_namespace)", &pid, /* flags = */ 0));
|
||||
if (r == EXIT_SUCCESS)
|
||||
return true;
|
||||
|
||||
if (si.si_status == EXIT_FAILURE)
|
||||
if (r == EXIT_FAILURE)
|
||||
return false;
|
||||
|
||||
assert_not_reached();
|
||||
@ -203,9 +198,9 @@ bool can_memlock(void) {
|
||||
|
||||
bool b = mlock(p, CAN_MEMLOCK_SIZE) >= 0;
|
||||
if (b)
|
||||
assert_se(munlock(p, CAN_MEMLOCK_SIZE) >= 0);
|
||||
ASSERT_OK(munlock(p, CAN_MEMLOCK_SIZE));
|
||||
|
||||
assert_se(munmap(p, CAN_MEMLOCK_SIZE) >= 0);
|
||||
ASSERT_OK(munmap(p, CAN_MEMLOCK_SIZE));
|
||||
return b;
|
||||
}
|
||||
|
||||
@ -301,16 +296,14 @@ static int enter_cgroup(char **ret_cgroup, bool enter_subroot) {
|
||||
r = cg_pid_get_path(NULL, 0, &cgroup_root);
|
||||
if (IN_SET(r, -ENOMEDIUM, -ENOENT))
|
||||
return log_warning_errno(r, "cg_pid_get_path(NULL, 0, ...) failed: %m");
|
||||
assert(r >= 0);
|
||||
ASSERT_OK(r);
|
||||
|
||||
if (enter_subroot)
|
||||
assert_se(asprintf(&cgroup_subroot, "%s/%" PRIx64, cgroup_root, random_u64()) >= 0);
|
||||
else {
|
||||
cgroup_subroot = strdup(cgroup_root);
|
||||
assert_se(cgroup_subroot != NULL);
|
||||
}
|
||||
ASSERT_OK(asprintf(&cgroup_subroot, "%s/%" PRIx64, cgroup_root, random_u64()));
|
||||
else
|
||||
ASSERT_NOT_NULL(cgroup_subroot = strdup(cgroup_root));
|
||||
|
||||
assert_se(cg_mask_supported(&supported) >= 0);
|
||||
ASSERT_OK(cg_mask_supported(&supported));
|
||||
|
||||
/* If this fails, then we don't mind as the later cgroup operations will fail too, and it's fine if
|
||||
* we handle any errors at that point. */
|
||||
|
@ -82,7 +82,7 @@ int define_hex_ptr_internal(const char *hex, void **name, size_t *name_len);
|
||||
#define DEFINE_HEX_PTR(name, hex) \
|
||||
_cleanup_free_ void *name = NULL; \
|
||||
size_t name##_len = 0; \
|
||||
assert_se(define_hex_ptr_internal(hex, &name, &name##_len) >= 0)
|
||||
ASSERT_OK(define_hex_ptr_internal(hex, &name, &name##_len))
|
||||
|
||||
/* Provide a convenient way to check if we're running in CI. */
|
||||
const char* ci_environment(void);
|
||||
|
@ -697,7 +697,7 @@ int resource_resolve_path(
|
||||
|
||||
} else if (RESOURCE_IS_FILESYSTEM(rr->type)) {
|
||||
_cleanup_free_ char *resolved = NULL, *relative_to = NULL;
|
||||
ChaseFlags chase_flags = CHASE_PREFIX_ROOT;
|
||||
ChaseFlags chase_flags = CHASE_NONEXISTENT | CHASE_PREFIX_ROOT;
|
||||
|
||||
if (rr->path_relative_to == PATH_RELATIVE_TO_EXPLICIT) {
|
||||
assert(relative_to_directory);
|
||||
|
@ -413,7 +413,7 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
fd = open(ASSERT_PTR(arg_device), O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
|
||||
if (fd < 0) {
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT(errno);
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(errno);
|
||||
log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno,
|
||||
"Failed to open device node '%s'%s: %m",
|
||||
arg_device, ignore ? ", ignoring" : "");
|
||||
|
@ -75,7 +75,7 @@ static int run(int argc, char** argv) {
|
||||
|
||||
mtd_fd = open(argv[1], O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||
if (mtd_fd < 0) {
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT(errno);
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(errno);
|
||||
log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno,
|
||||
"Failed to open device node '%s'%s: %m",
|
||||
argv[1], ignore ? ", ignoring" : "");
|
||||
|
@ -499,7 +499,7 @@ static int builtin_blkid(UdevEvent *event, int argc, char *argv[]) {
|
||||
|
||||
fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
|
||||
if (fd < 0) {
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(fd);
|
||||
log_device_debug_errno(dev, fd, "Failed to open block device %s%s: %m",
|
||||
devnode, ignore ? ", ignoring" : "");
|
||||
return ignore ? 0 : fd;
|
||||
|
@ -35,7 +35,7 @@ static int builtin_btrfs(UdevEvent *event, int argc, char *argv[]) {
|
||||
|
||||
_cleanup_close_ int fd = open("/dev/btrfs-control", O_RDWR|O_CLOEXEC|O_NOCTTY);
|
||||
if (fd < 0) {
|
||||
if (ERRNO_IS_DEVICE_ABSENT(errno)) {
|
||||
if (ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(errno)) {
|
||||
/* Driver not installed? Then we aren't ready. This is useful in initrds that lack
|
||||
* btrfs.ko. After the host transition (where btrfs.ko will hopefully become
|
||||
* available) the device can be retriggered and will then be considered ready. */
|
||||
|
@ -201,7 +201,7 @@ static int builtin_keyboard(UdevEvent *event, int argc, char *argv[]) {
|
||||
if (fd < 0) {
|
||||
fd = sd_device_open(dev, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
|
||||
if (fd < 0) {
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(fd);
|
||||
log_device_full_errno(dev, ignore ? LOG_DEBUG : LOG_WARNING, fd,
|
||||
"Failed to open device '%s'%s: %m",
|
||||
node, ignore ? ", ignoring" : "");
|
||||
@ -223,7 +223,7 @@ static int builtin_keyboard(UdevEvent *event, int argc, char *argv[]) {
|
||||
if (fd < 0) {
|
||||
fd = sd_device_open(dev, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
|
||||
if (fd < 0) {
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(fd);
|
||||
log_device_full_errno(dev, ignore ? LOG_DEBUG : LOG_WARNING, fd,
|
||||
"Failed to open device '%s'%s: %m",
|
||||
node, ignore ? ", ignoring" : "");
|
||||
|
@ -122,7 +122,7 @@ static int builtin_uaccess(UdevEvent *event, int argc, char *argv[]) {
|
||||
|
||||
_cleanup_close_ int fd = sd_device_open(dev, O_CLOEXEC|O_PATH);
|
||||
if (fd < 0) {
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(fd);
|
||||
log_device_full_errno(dev, ignore ? LOG_DEBUG : LOG_WARNING, fd,
|
||||
"Failed to open device node%s: %m",
|
||||
ignore ? ", ignoring" : "");
|
||||
|
@ -431,7 +431,7 @@ static int link_update(sd_device *dev, const char *slink, bool add) {
|
||||
return r;
|
||||
|
||||
r = node_get_current(slink, dirfd, ¤t_id, add ? ¤t_prio : NULL);
|
||||
if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r))
|
||||
if (r < 0 && !ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(r))
|
||||
return log_device_debug_errno(dev, r, "Failed to get the current device node priority for '%s': %m", slink);
|
||||
|
||||
r = stack_directory_update(dev, dirfd, add);
|
||||
@ -702,7 +702,7 @@ int udev_node_apply_permissions(
|
||||
|
||||
node_fd = sd_device_open(dev, O_PATH|O_CLOEXEC);
|
||||
if (node_fd < 0) {
|
||||
if (ERRNO_IS_DEVICE_ABSENT(node_fd)) {
|
||||
if (ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(node_fd)) {
|
||||
log_device_debug_errno(dev, node_fd, "Device node %s is missing, skipping handling.", devnode);
|
||||
return 0; /* This is necessarily racey, so ignore missing the device */
|
||||
}
|
||||
@ -735,7 +735,7 @@ int static_node_apply_permissions(
|
||||
|
||||
node_fd = open(devnode, O_PATH|O_CLOEXEC);
|
||||
if (node_fd < 0) {
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT(errno);
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(errno);
|
||||
log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno,
|
||||
"Failed to open device node '%s'%s: %m",
|
||||
devnode, ignore ? ", ignoring" : "");
|
||||
|
@ -112,7 +112,7 @@ static int worker_lock_whole_disk(UdevWorker *worker, sd_device *dev, int *ret_f
|
||||
|
||||
fd = sd_device_open(dev_whole_disk, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
|
||||
if (fd < 0) {
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(fd);
|
||||
|
||||
log_device_debug_errno(dev, fd, "Failed to open '%s'%s: %m", whole_disk, ignore ? ", ignoring" : "");
|
||||
if (!ignore)
|
||||
@ -178,7 +178,7 @@ static int worker_mark_block_device_read_only(sd_device *dev) {
|
||||
|
||||
_cleanup_close_ int fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
|
||||
if (fd < 0) {
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(fd);
|
||||
log_device_full_errno(dev, ignore ? LOG_DEBUG : LOG_WARNING, fd,
|
||||
"Failed to open device node '%s'%s: %m",
|
||||
node, ignore ? ", ignoring" : "");
|
||||
|
@ -61,7 +61,7 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
fd = open(arg_device, O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||
if (fd < 0) {
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT(errno);
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(errno);
|
||||
log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno,
|
||||
"Failed to open device node '%s'%s: %m",
|
||||
arg_device, ignore ? ", ignoring" : "");
|
||||
|
@ -133,6 +133,10 @@ that make use of `run_testcases`.
|
||||
|
||||
`TEST_SKIP_TESTCASE=testcase`: takes a space separated list of testcases to skip.
|
||||
|
||||
`TEST_SAVE_JOURNAL=0|1|fail`: When `0`, journal file will be removed on exit.
|
||||
When `1`, journal file will be saved at `$BUILD_DIR/test/journal`. When `fail`,
|
||||
journal file will be saved only when the test is failed. Defaults to `fail`.
|
||||
|
||||
`TEST_JOURNAL_USE_TMP=1`: Write test journal to `/tmp` while the test is in
|
||||
progress and only move the journal to its final location in the build directory
|
||||
(`$BUILD_DIR/test/journal`) when the test is finished.
|
||||
|
8
test/integration-tests/TEST-10-MOUNT/meson.build
Normal file
8
test/integration-tests/TEST-10-MOUNT/meson.build
Normal file
@ -0,0 +1,8 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
integration_tests += [
|
||||
integration_test_template + {
|
||||
'name' : fs.name(meson.current_source_dir()),
|
||||
'vm' : true,
|
||||
},
|
||||
]
|
@ -660,7 +660,12 @@ def main() -> None:
|
||||
journal_file = Path(shutil.move(journal_file, dst))
|
||||
|
||||
if shell or (result.returncode in (args.exit_code, 77) and not coredumps and not sanitizer):
|
||||
exit(0 if shell or result.returncode == args.exit_code else 77)
|
||||
exit_code = 0 if shell or result.returncode == args.exit_code else 77
|
||||
exit_str = 'succeeded' if exit_code == 0 else 'skipped'
|
||||
else:
|
||||
# 0 also means we failed so translate that to a non-zero exit code to mark the test as failed.
|
||||
exit_code = result.returncode or 1
|
||||
exit_str = 'failed'
|
||||
|
||||
if journal_file.exists():
|
||||
ops = []
|
||||
@ -678,10 +683,11 @@ def main() -> None:
|
||||
|
||||
ops += [f'journalctl --file {journal_file} --no-hostname -o short-monotonic -u {args.unit} -p info']
|
||||
|
||||
print(f'Test failed, relevant logs can be viewed with: \n\n{(" && ".join(ops))}\n', file=sys.stderr)
|
||||
print(
|
||||
f'Test {exit_str}, relevant logs can be viewed with: \n\n{(" && ".join(ops))}\n', file=sys.stderr
|
||||
)
|
||||
|
||||
# 0 also means we failed so translate that to a non-zero exit code to mark the test as failed.
|
||||
exit(result.returncode or 1)
|
||||
exit(exit_code)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -43,6 +43,7 @@ foreach dirname : [
|
||||
'TEST-07-PID1',
|
||||
'TEST-08-INITRD',
|
||||
'TEST-09-REBOOT',
|
||||
'TEST-10-MOUNT',
|
||||
'TEST-13-NSPAWN',
|
||||
'TEST-15-DROPIN',
|
||||
'TEST-16-EXTEND-TIMEOUT',
|
||||
|
@ -32,6 +32,11 @@ if ! systemd-detect-virt -qc; then
|
||||
sysctl net.ipv4.ping_group_range="0 2147483647"
|
||||
fi
|
||||
|
||||
# Disable firewalld and friends to make them not disturb test-firewall-util
|
||||
systemctl disable --now firewalld.service || :
|
||||
systemctl disable --now iptables.service || :
|
||||
systemctl disable --now ip6tables.service || :
|
||||
|
||||
# Check & report test results
|
||||
# Arguments:
|
||||
# $1: test path
|
||||
|
169
test/units/TEST-10-MOUNT.sh
Executable file
169
test/units/TEST-10-MOUNT.sh
Executable file
@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# shellcheck source=test/units/test-control.sh
|
||||
. "$(dirname "$0")"/test-control.sh
|
||||
# shellcheck source=test/units/util.sh
|
||||
. "$(dirname "$0")"/util.sh
|
||||
|
||||
teardown_test_dependencies() (
|
||||
set +eux
|
||||
|
||||
if mountpoint /tmp/deptest; then
|
||||
umount /tmp/deptest
|
||||
fi
|
||||
|
||||
if [[ -n "${LOOP}" ]]; then
|
||||
losetup -d "${LOOP}" || :
|
||||
fi
|
||||
if [[ -n "${LOOP_0}" ]]; then
|
||||
losetup -d "${LOOP_0}" || :
|
||||
fi
|
||||
if [[ -n "${LOOP_1}" ]]; then
|
||||
losetup -d "${LOOP_1}" || :
|
||||
fi
|
||||
|
||||
rm -f /tmp/TEST-60-MOUNT-RATELIMIT-dependencies-0.img
|
||||
rm -f /tmp/TEST-60-MOUNT-RATELIMIT-dependencies-1.img
|
||||
|
||||
rm -f /run/systemd/system/tmp-deptest.mount
|
||||
systemctl daemon-reload
|
||||
|
||||
return 0
|
||||
)
|
||||
|
||||
setup_loop() {
|
||||
truncate -s 30m "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img"
|
||||
sfdisk --wipe=always "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img" <<EOF
|
||||
label:gpt
|
||||
|
||||
name="loop${1?}-part1"
|
||||
EOF
|
||||
LOOP=$(losetup -P --show -f "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img")
|
||||
udevadm wait --settle --timeout=30 "${LOOP}"
|
||||
udevadm lock --timeout=30 --device="${LOOP}" mkfs.ext4 -L "partname${1?}-1" "${LOOP}p1"
|
||||
}
|
||||
|
||||
check_dependencies() {
|
||||
local escaped_0 escaped_1 after
|
||||
|
||||
escaped_0=$(systemd-escape -p "${LOOP_0}p1")
|
||||
escaped_1=$(systemd-escape -p "${LOOP_1}p1")
|
||||
|
||||
if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
|
||||
after=$(systemctl show --property=After --value tmp-deptest.mount)
|
||||
assert_not_in "local-fs-pre.target" "$after"
|
||||
assert_in "remote-fs-pre.target" "$after"
|
||||
assert_in "network.target" "$after"
|
||||
fi
|
||||
|
||||
# mount LOOP_0
|
||||
mount -t ext4 "${LOOP_0}p1" /tmp/deptest
|
||||
timeout 10 bash -c 'until systemctl -q is-active tmp-deptest.mount; do sleep .1; done'
|
||||
after=$(systemctl show --property=After --value tmp-deptest.mount)
|
||||
assert_in "local-fs-pre.target" "$after"
|
||||
assert_not_in "remote-fs-pre.target" "$after"
|
||||
assert_not_in "network.target" "$after"
|
||||
assert_in "${escaped_0}.device" "$after"
|
||||
assert_in "blockdev@${escaped_0}.target" "$after"
|
||||
assert_not_in "${escaped_1}.device" "$after"
|
||||
assert_not_in "blockdev@${escaped_1}.target" "$after"
|
||||
systemctl stop tmp-deptest.mount
|
||||
|
||||
if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
|
||||
after=$(systemctl show --property=After --value tmp-deptest.mount)
|
||||
assert_not_in "local-fs-pre.target" "$after"
|
||||
assert_in "remote-fs-pre.target" "$after"
|
||||
assert_in "network.target" "$after"
|
||||
fi
|
||||
|
||||
# mount LOOP_1 (using fake _netdev option)
|
||||
mount -t ext4 -o _netdev "${LOOP_1}p1" /tmp/deptest
|
||||
timeout 10 bash -c 'until systemctl -q is-active tmp-deptest.mount; do sleep .1; done'
|
||||
# When a device is mounted with userspace options such as _netdev, even when the mount event source is
|
||||
# triggered, only /proc/self/mountinfo may be updated, and /run/mount/utab may not be updated yet.
|
||||
# Hence, the mount unit may be created/updated without the userspace options. In that case, the mount
|
||||
# event source will be retriggered when /run/mount/utab is updated, and the mount unit will be updated
|
||||
# again with the userspace options. Typically, the window between the two calls is very short, but when
|
||||
# the mount event source is ratelimited after the first event, processing the second event may be delayed
|
||||
# about 1 secound. Hence, here we need to wait for a while.
|
||||
timeout 10 bash -c 'until systemctl show --property=After --value tmp-deptest.mount | grep -q -F remote-fs-pre.target; do sleep .1; done'
|
||||
after=$(systemctl show --property=After --value tmp-deptest.mount)
|
||||
assert_not_in "local-fs-pre.target" "$after"
|
||||
assert_in "remote-fs-pre.target" "$after"
|
||||
assert_in "network.target" "$after"
|
||||
assert_not_in "${escaped_0}.device" "$after"
|
||||
assert_not_in "blockdev@${escaped_0}.target" "$after"
|
||||
assert_in "${escaped_1}.device" "$after"
|
||||
assert_in "blockdev@${escaped_1}.target" "$after"
|
||||
systemctl stop tmp-deptest.mount
|
||||
|
||||
if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
|
||||
after=$(systemctl show --property=After --value tmp-deptest.mount)
|
||||
assert_not_in "local-fs-pre.target" "$after"
|
||||
assert_in "remote-fs-pre.target" "$after"
|
||||
assert_in "network.target" "$after"
|
||||
fi
|
||||
|
||||
# mount tmpfs
|
||||
mount -t tmpfs tmpfs /tmp/deptest
|
||||
timeout 10 bash -c 'until systemctl -q is-active tmp-deptest.mount; do sleep .1; done'
|
||||
after=$(systemctl show --property=After --value tmp-deptest.mount)
|
||||
assert_in "local-fs-pre.target" "$after"
|
||||
assert_not_in "remote-fs-pre.target" "$after"
|
||||
assert_not_in "network.target" "$after"
|
||||
assert_not_in "${escaped_0}.device" "$after"
|
||||
assert_not_in "blockdev@${escaped_0}.target" "$after"
|
||||
assert_not_in "${escaped_1}.device" "$after"
|
||||
assert_not_in "blockdev@${escaped_1}.target" "$after"
|
||||
systemctl stop tmp-deptest.mount
|
||||
|
||||
if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
|
||||
after=$(systemctl show --property=After --value tmp-deptest.mount)
|
||||
assert_not_in "local-fs-pre.target" "$after"
|
||||
assert_in "remote-fs-pre.target" "$after"
|
||||
assert_in "network.target" "$after"
|
||||
fi
|
||||
}
|
||||
|
||||
testcase_dependencies() {
|
||||
# test for issue #19983 and #23552.
|
||||
|
||||
if systemd-detect-virt --quiet --container; then
|
||||
echo "Skipping test_dependencies in container"
|
||||
return
|
||||
fi
|
||||
|
||||
trap teardown_test_dependencies RETURN EXIT ERR INT TERM
|
||||
|
||||
setup_loop 0
|
||||
LOOP_0="${LOOP}"
|
||||
LOOP=
|
||||
setup_loop 1
|
||||
LOOP_1="${LOOP}"
|
||||
LOOP=
|
||||
|
||||
mkdir -p /tmp/deptest
|
||||
|
||||
# without .mount file
|
||||
check_dependencies
|
||||
|
||||
# create .mount file
|
||||
mkdir -p /run/systemd/system
|
||||
cat >/run/systemd/system/tmp-deptest.mount <<EOF
|
||||
[Mount]
|
||||
Where=/tmp/deptest
|
||||
What=192.168.0.1:/tmp/mnt
|
||||
Type=nfs
|
||||
EOF
|
||||
systemctl daemon-reload
|
||||
|
||||
# with .mount file
|
||||
check_dependencies
|
||||
}
|
||||
|
||||
run_testcases
|
||||
|
||||
touch /testok
|
@ -8,154 +8,6 @@ set -o pipefail
|
||||
# shellcheck source=test/units/util.sh
|
||||
. "$(dirname "$0")"/util.sh
|
||||
|
||||
teardown_test_dependencies() (
|
||||
set +eux
|
||||
|
||||
if mountpoint /tmp/deptest; then
|
||||
umount /tmp/deptest
|
||||
fi
|
||||
|
||||
if [[ -n "${LOOP}" ]]; then
|
||||
losetup -d "${LOOP}" || :
|
||||
fi
|
||||
if [[ -n "${LOOP_0}" ]]; then
|
||||
losetup -d "${LOOP_0}" || :
|
||||
fi
|
||||
if [[ -n "${LOOP_1}" ]]; then
|
||||
losetup -d "${LOOP_1}" || :
|
||||
fi
|
||||
|
||||
rm -f /tmp/TEST-60-MOUNT-RATELIMIT-dependencies-0.img
|
||||
rm -f /tmp/TEST-60-MOUNT-RATELIMIT-dependencies-1.img
|
||||
|
||||
rm -f /run/systemd/system/tmp-deptest.mount
|
||||
systemctl daemon-reload
|
||||
|
||||
return 0
|
||||
)
|
||||
|
||||
setup_loop() {
|
||||
truncate -s 30m "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img"
|
||||
sfdisk --wipe=always "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img" <<EOF
|
||||
label:gpt
|
||||
|
||||
name="loop${1?}-part1"
|
||||
EOF
|
||||
LOOP=$(losetup -P --show -f "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img")
|
||||
udevadm wait --settle --timeout=30 "${LOOP}"
|
||||
udevadm lock --timeout=30 --device="${LOOP}" mkfs.ext4 -L "partname${1?}-1" "${LOOP}p1"
|
||||
}
|
||||
|
||||
check_dependencies() {
|
||||
local escaped_0 escaped_1 after
|
||||
|
||||
escaped_0=$(systemd-escape -p "${LOOP_0}p1")
|
||||
escaped_1=$(systemd-escape -p "${LOOP_1}p1")
|
||||
|
||||
if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
|
||||
after=$(systemctl show --property=After --value tmp-deptest.mount)
|
||||
assert_not_in "local-fs-pre.target" "$after"
|
||||
assert_in "remote-fs-pre.target" "$after"
|
||||
assert_in "network.target" "$after"
|
||||
fi
|
||||
|
||||
# mount LOOP_0
|
||||
mount -t ext4 "${LOOP_0}p1" /tmp/deptest
|
||||
timeout 10 bash -c 'until systemctl -q is-active tmp-deptest.mount; do sleep .1; done'
|
||||
after=$(systemctl show --property=After --value tmp-deptest.mount)
|
||||
assert_in "local-fs-pre.target" "$after"
|
||||
assert_not_in "remote-fs-pre.target" "$after"
|
||||
assert_not_in "network.target" "$after"
|
||||
assert_in "${escaped_0}.device" "$after"
|
||||
assert_in "blockdev@${escaped_0}.target" "$after"
|
||||
assert_not_in "${escaped_1}.device" "$after"
|
||||
assert_not_in "blockdev@${escaped_1}.target" "$after"
|
||||
systemctl stop tmp-deptest.mount
|
||||
|
||||
if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
|
||||
after=$(systemctl show --property=After --value tmp-deptest.mount)
|
||||
assert_not_in "local-fs-pre.target" "$after"
|
||||
assert_in "remote-fs-pre.target" "$after"
|
||||
assert_in "network.target" "$after"
|
||||
fi
|
||||
|
||||
# mount LOOP_1 (using fake _netdev option)
|
||||
mount -t ext4 -o _netdev "${LOOP_1}p1" /tmp/deptest
|
||||
timeout 10 bash -c 'until systemctl -q is-active tmp-deptest.mount; do sleep .1; done'
|
||||
after=$(systemctl show --property=After --value tmp-deptest.mount)
|
||||
assert_not_in "local-fs-pre.target" "$after"
|
||||
assert_in "remote-fs-pre.target" "$after"
|
||||
assert_in "network.target" "$after"
|
||||
assert_not_in "${escaped_0}.device" "$after"
|
||||
assert_not_in "blockdev@${escaped_0}.target" "$after"
|
||||
assert_in "${escaped_1}.device" "$after"
|
||||
assert_in "blockdev@${escaped_1}.target" "$after"
|
||||
systemctl stop tmp-deptest.mount
|
||||
|
||||
if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
|
||||
after=$(systemctl show --property=After --value tmp-deptest.mount)
|
||||
assert_not_in "local-fs-pre.target" "$after"
|
||||
assert_in "remote-fs-pre.target" "$after"
|
||||
assert_in "network.target" "$after"
|
||||
fi
|
||||
|
||||
# mount tmpfs
|
||||
mount -t tmpfs tmpfs /tmp/deptest
|
||||
timeout 10 bash -c 'until systemctl -q is-active tmp-deptest.mount; do sleep .1; done'
|
||||
after=$(systemctl show --property=After --value tmp-deptest.mount)
|
||||
assert_in "local-fs-pre.target" "$after"
|
||||
assert_not_in "remote-fs-pre.target" "$after"
|
||||
assert_not_in "network.target" "$after"
|
||||
assert_not_in "${escaped_0}.device" "$after"
|
||||
assert_not_in "blockdev@${escaped_0}.target" "$after"
|
||||
assert_not_in "${escaped_1}.device" "$after"
|
||||
assert_not_in "blockdev@${escaped_1}.target" "$after"
|
||||
systemctl stop tmp-deptest.mount
|
||||
|
||||
if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
|
||||
after=$(systemctl show --property=After --value tmp-deptest.mount)
|
||||
assert_not_in "local-fs-pre.target" "$after"
|
||||
assert_in "remote-fs-pre.target" "$after"
|
||||
assert_in "network.target" "$after"
|
||||
fi
|
||||
}
|
||||
|
||||
testcase_dependencies() {
|
||||
# test for issue #19983 and #23552.
|
||||
|
||||
if systemd-detect-virt --quiet --container; then
|
||||
echo "Skipping test_dependencies in container"
|
||||
return
|
||||
fi
|
||||
|
||||
trap teardown_test_dependencies RETURN
|
||||
|
||||
setup_loop 0
|
||||
LOOP_0="${LOOP}"
|
||||
LOOP=
|
||||
setup_loop 1
|
||||
LOOP_1="${LOOP}"
|
||||
LOOP=
|
||||
|
||||
mkdir -p /tmp/deptest
|
||||
|
||||
# without .mount file
|
||||
check_dependencies
|
||||
|
||||
# create .mount file
|
||||
mkdir -p /run/systemd/system
|
||||
cat >/run/systemd/system/tmp-deptest.mount <<EOF
|
||||
[Mount]
|
||||
Where=/tmp/deptest
|
||||
What=192.168.0.1:/tmp/mnt
|
||||
Type=nfs
|
||||
EOF
|
||||
systemctl daemon-reload
|
||||
|
||||
# with .mount file
|
||||
check_dependencies
|
||||
}
|
||||
|
||||
testcase_issue_20329() {
|
||||
# test that handling of mount start jobs is delayed when /proc/self/mouninfo monitor is rate limited
|
||||
|
||||
@ -324,7 +176,7 @@ cat >/run/systemd/journald.conf.d/99-ratelimit.conf <<EOF
|
||||
[Journal]
|
||||
RateLimitBurst=0
|
||||
EOF
|
||||
systemctl restart systemd-journald.service
|
||||
systemctl reload systemd-journald.service
|
||||
|
||||
run_testcases
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user