core: do BindMount/MountImage operations in async control process

These operations might require slow I/O, and thus might block PID1's main
loop for an undeterminated amount of time. Instead of performing them
inline, fork a worker process and stash away the D-Bus message, and reply
once we get a SIGCHILD indicating they have completed. That way we don't
break compatibility and callers can continue to rely on the fact that when
they get the method reply the operation either succeeded or failed.

To keep backward compatibility, unlike reload control processes, these
are ran inside init.scope and not the target cgroup. Unlike ExecReload,
this is under our control and is not defined by the unit. This is necessary
because previously the operation also wasn't ran from the target cgroup,
so suddenly forking a copy-on-write copy of pid1 into the target cgroup
will make memory usage spike, and if there is a MemoryMax= or MemoryHigh=
set and the cgroup is already close to the limit, it will cause an OOM
kill, where previously it would have worked fine.
This commit is contained in:
Luca Boccassi
2024-08-29 12:48:55 +01:00
committed by Luca Boccassi
parent 5121f7c45b
commit 5162829ec8
25 changed files with 480 additions and 146 deletions
+9 -3
View File
@@ -24,6 +24,7 @@ typedef struct WaitForItem {
char *active_state;
uint32_t job_id;
char *clean_result;
char *live_mount_result;
} WaitForItem;
typedef struct BusWaitForUnits {
@@ -67,6 +68,7 @@ static WaitForItem *wait_for_item_free(WaitForItem *item) {
free(item->bus_path);
free(item->active_state);
free(item->clean_result);
free(item->live_mount_result);
return mfree(item);
}
@@ -178,6 +180,9 @@ static void wait_for_item_check_ready(WaitForItem *item) {
if (item->clean_result && !streq(item->clean_result, "success"))
d->has_failed = true;
if (item->live_mount_result && !streq(item->live_mount_result, "success"))
d->has_failed = true;
if (!item->active_state || streq(item->active_state, "maintenance"))
return;
}
@@ -214,9 +219,10 @@ static int property_map_job_id(
static int wait_for_item_parse_properties(WaitForItem *item, sd_bus_message *m) {
static const struct bus_properties_map map[] = {
{ "ActiveState", "s", NULL, offsetof(WaitForItem, active_state) },
{ "Job", "(uo)", property_map_job_id, offsetof(WaitForItem, job_id) },
{ "CleanResult", "s", NULL, offsetof(WaitForItem, clean_result) },
{ "ActiveState", "s", NULL, offsetof(WaitForItem, active_state) },
{ "Job", "(uo)", property_map_job_id, offsetof(WaitForItem, job_id) },
{ "CleanResult", "s", NULL, offsetof(WaitForItem, clean_result) },
{ "LiveMountResult", "s", NULL, offsetof(WaitForItem, live_mount_result) },
{}
};
+26 -38
View File
@@ -854,11 +854,9 @@ static int mount_in_namespace_legacy(
int pidns_fd,
int mntns_fd,
int root_fd,
bool read_only,
bool make_file_or_directory,
MountInNamespaceFlags flags,
const MountOptions *options,
const ImagePolicy *image_policy,
bool is_image) {
const ImagePolicy *image_policy) {
_cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR;
char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
@@ -877,7 +875,7 @@ static int mount_in_namespace_legacy(
assert(pidns_fd >= 0);
assert(mntns_fd >= 0);
assert(root_fd >= 0);
assert(!options || is_image);
assert(!options || (flags & MOUNT_IN_NAMESPACE_IS_IMAGE));
p = strjoina(propagate_path, "/");
r = laccess(p, F_OK);
@@ -910,7 +908,7 @@ static int mount_in_namespace_legacy(
/* Second, we mount the source file or directory to a directory inside of our MS_SLAVE playground. */
mount_tmp = strjoina(mount_slave, "/mount");
if (is_image)
if (flags & MOUNT_IN_NAMESPACE_IS_IMAGE)
r = mkdir_p(mount_tmp, 0700);
else
r = make_mount_point_inode_from_stat(chased_src_st, mount_tmp, 0700);
@@ -921,7 +919,7 @@ static int mount_in_namespace_legacy(
mount_tmp_created = true;
if (is_image)
if (flags & MOUNT_IN_NAMESPACE_IS_IMAGE)
r = verity_dissect_and_mount(
chased_src_fd,
chased_src_path,
@@ -943,7 +941,7 @@ static int mount_in_namespace_legacy(
mount_tmp_mounted = true;
/* Third, we remount the new bind mount read-only if requested. */
if (read_only) {
if (flags & MOUNT_IN_NAMESPACE_READ_ONLY) {
r = mount_nofollow_verbose(LOG_DEBUG, NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL);
if (r < 0)
goto finish;
@@ -953,7 +951,7 @@ static int mount_in_namespace_legacy(
* right-away. */
mount_outside = strjoina(propagate_path, "/XXXXXX");
if (is_image || S_ISDIR(chased_src_st->st_mode))
if ((flags & MOUNT_IN_NAMESPACE_IS_IMAGE) || S_ISDIR(chased_src_st->st_mode))
r = mkdtemp(mount_outside) ? 0 : -errno;
else {
r = mkostemp_safe(mount_outside);
@@ -973,7 +971,7 @@ static int mount_in_namespace_legacy(
mount_outside_mounted = true;
mount_tmp_mounted = false;
if (is_image || S_ISDIR(chased_src_st->st_mode))
if ((flags & MOUNT_IN_NAMESPACE_IS_IMAGE) || S_ISDIR(chased_src_st->st_mode))
(void) rmdir(mount_tmp);
else
(void) unlink(mount_tmp);
@@ -999,8 +997,8 @@ static int mount_in_namespace_legacy(
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
if (make_file_or_directory) {
if (!is_image) {
if (flags & MOUNT_IN_NAMESPACE_MAKE_FILE_OR_DIRECTORY) {
if (!(flags & MOUNT_IN_NAMESPACE_IS_IMAGE)) {
(void) mkdir_parents(dest, 0755);
(void) make_mount_point_inode_from_stat(chased_src_st, dest, 0700);
} else
@@ -1052,7 +1050,7 @@ finish:
if (mount_outside_mounted)
(void) umount_verbose(LOG_DEBUG, mount_outside, UMOUNT_NOFOLLOW);
if (mount_outside_created) {
if (is_image || S_ISDIR(chased_src_st->st_mode))
if ((flags & MOUNT_IN_NAMESPACE_IS_IMAGE) || S_ISDIR(chased_src_st->st_mode))
(void) rmdir(mount_outside);
else
(void) unlink(mount_outside);
@@ -1061,7 +1059,7 @@ finish:
if (mount_tmp_mounted)
(void) umount_verbose(LOG_DEBUG, mount_tmp, UMOUNT_NOFOLLOW);
if (mount_tmp_created) {
if (is_image || S_ISDIR(chased_src_st->st_mode))
if ((flags & MOUNT_IN_NAMESPACE_IS_IMAGE) || S_ISDIR(chased_src_st->st_mode))
(void) rmdir(mount_tmp);
else
(void) unlink(mount_tmp);
@@ -1081,9 +1079,7 @@ static int mount_in_namespace(
const char *incoming_path,
const char *src,
const char *dest,
bool read_only,
bool make_file_or_directory,
bool is_image,
MountInNamespaceFlags flags,
const MountOptions *options,
const ImagePolicy *image_policy) {
@@ -1096,7 +1092,7 @@ static int mount_in_namespace(
assert(incoming_path);
assert(src);
assert(dest);
assert(is_image || (!options && !image_policy));
assert((flags & MOUNT_IN_NAMESPACE_IS_IMAGE) || (!options && !image_policy));
if (!pidref_is_set(target))
return -ESRCH;
@@ -1133,18 +1129,16 @@ static int mount_in_namespace(
pidns_fd,
mntns_fd,
root_fd,
read_only,
make_file_or_directory,
flags,
options,
image_policy,
is_image);
image_policy);
_cleanup_(dissected_image_unrefp) DissectedImage *img = NULL;
_cleanup_close_ int new_mount_fd = -EBADF;
_cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR;
pid_t child;
if (is_image) {
if (flags & MOUNT_IN_NAMESPACE_IS_IMAGE) {
r = verity_dissect_and_mount(
chased_src_fd,
chased_src_path,
@@ -1173,7 +1167,7 @@ static int mount_in_namespace(
"Failed to open mount source '%s': %m",
chased_src_path);
if (read_only && mount_setattr(new_mount_fd, "", AT_EMPTY_PATH,
if ((flags & MOUNT_IN_NAMESPACE_READ_ONLY) && mount_setattr(new_mount_fd, "", AT_EMPTY_PATH,
&(struct mount_attr) {
.attr_set = MOUNT_ATTR_RDONLY,
}, MOUNT_ATTR_SIZE_VER0) < 0)
@@ -1201,7 +1195,7 @@ static int mount_in_namespace(
if (r == 0) {
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
if (make_file_or_directory)
if (flags & MOUNT_IN_NAMESPACE_MAKE_FILE_OR_DIRECTORY)
(void) mkdir_parents(dest, 0755);
if (img) {
@@ -1209,10 +1203,10 @@ static int mount_in_namespace(
DISSECT_IMAGE_TRY_ATOMIC_MOUNT_EXCHANGE |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
if (make_file_or_directory)
if (flags & MOUNT_IN_NAMESPACE_MAKE_FILE_OR_DIRECTORY)
f |= DISSECT_IMAGE_MKDIR;
if (read_only)
if (flags & MOUNT_IN_NAMESPACE_READ_ONLY)
f |= DISSECT_IMAGE_READ_ONLY;
r = dissected_image_mount(
@@ -1223,7 +1217,7 @@ static int mount_in_namespace(
/* userns_fd= */ -EBADF,
f);
} else {
if (make_file_or_directory)
if (flags & MOUNT_IN_NAMESPACE_MAKE_FILE_OR_DIRECTORY)
(void) make_mount_point_inode_from_stat(&st, dest, 0700);
r = mount_exchange_graceful(new_mount_fd, dest, /* mount_beneath= */ true);
@@ -1259,17 +1253,14 @@ int bind_mount_in_namespace(
const char *incoming_path,
const char *src,
const char *dest,
bool read_only,
bool make_file_or_directory) {
MountInNamespaceFlags flags) {
return mount_in_namespace(target,
propagate_path,
incoming_path,
src,
dest,
read_only,
make_file_or_directory,
/* is_image = */ false,
flags & ~MOUNT_IN_NAMESPACE_IS_IMAGE,
/* options = */ NULL,
/* image_policy = */ NULL);
}
@@ -1280,8 +1271,7 @@ int mount_image_in_namespace(
const char *incoming_path,
const char *src,
const char *dest,
bool read_only,
bool make_file_or_directory,
MountInNamespaceFlags flags,
const MountOptions *options,
const ImagePolicy *image_policy) {
@@ -1290,9 +1280,7 @@ int mount_image_in_namespace(
incoming_path,
src,
dest,
read_only,
make_file_or_directory,
/* is_image = */ true,
flags | MOUNT_IN_NAMESPACE_IS_IMAGE,
options,
image_policy);
}
+8 -4
View File
@@ -106,22 +106,26 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char*, umount_and_unlink_and_free);
int mount_exchange_graceful(int fsmount_fd, const char *dest, bool mount_beneath);
typedef enum MountInNamespaceFlags {
MOUNT_IN_NAMESPACE_READ_ONLY = 1 << 0,
MOUNT_IN_NAMESPACE_MAKE_FILE_OR_DIRECTORY = 1 << 1,
MOUNT_IN_NAMESPACE_IS_IMAGE = 1 << 2,
} MountInNamespaceFlags;
int bind_mount_in_namespace(
const PidRef *target,
const char *propagate_path,
const char *incoming_path,
const char *src,
const char *dest,
bool read_only,
bool make_file_or_directory);
MountInNamespaceFlags flags);
int mount_image_in_namespace(
const PidRef *target,
const char *propagate_path,
const char *incoming_path,
const char *src,
const char *dest,
bool read_only,
bool make_file_or_directory,
MountInNamespaceFlags flags,
const MountOptions *options,
const ImagePolicy *image_policy);