diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index 708793d7d1..7a65bfa809 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -3421,6 +3421,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly b MemoryKSM = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s MemoryTHP = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s UserNamespacePath = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s NetworkNamespacePath = '...'; @@ -4036,6 +4038,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + @@ -4784,6 +4788,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + @@ -5684,6 +5690,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly b MemoryKSM = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s MemoryTHP = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s UserNamespacePath = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s NetworkNamespacePath = '...'; @@ -6317,6 +6325,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { + + @@ -7041,6 +7051,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { + + @@ -7765,6 +7777,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly b MemoryKSM = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s MemoryTHP = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s UserNamespacePath = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s NetworkNamespacePath = '...'; @@ -8322,6 +8336,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { + + @@ -8954,6 +8970,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { + + @@ -9811,6 +9829,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly b MemoryKSM = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s MemoryTHP = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s UserNamespacePath = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s NetworkNamespacePath = '...'; @@ -10350,6 +10370,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { + + @@ -10964,6 +10986,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { + + @@ -12598,7 +12622,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \ ManagedOOMKills, ExecReloadPost, and ExecReloadPostEx were added in version 259. - BindNetworkInterface was added in version 260. + BindNetworkInterface, and + MemoryTHP were added in version 260. Socket Unit Objects @@ -12667,7 +12692,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \ UserNamespacePath, OOMKills, and ManagedOOMKills were added in 259. - BindNetworkInterface was added in version 260. + BindNetworkInterface, and + MemoryTHP were added in version 260. Mount Unit Objects @@ -12731,7 +12757,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \ UserNamespacePath, OOMKills, and ManagedOOMKills were added in 259. - BindNetworkInterface was added in version 260. + BindNetworkInterface, and + MemoryTHP were added in version 260. Swap Unit Objects @@ -12793,7 +12820,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \ UserNamespacePath, OOMKills, and ManagedOOMKills were added in 259. - BindNetworkInterface was added in version 260. + BindNetworkInterface, and + MemoryTHP were added in version 260. Slice Unit Objects diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 927fa3e0c4..ae24b9dc14 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -2134,6 +2134,41 @@ BindReadOnlyPaths=/var/lib/systemd + + MemoryTHP= + + Transparent Hugepages (THPs) is a Linux kernel feature that manages memory + using larger pages (2MB on x86, compared to the default 4KB). The main goal is to improve memory management + efficiency and system performance, especially for memory-intensive applications. + However, it can cause drawbacks in some scenarios, such as memory regression and latency spikes. + THP policy is governed for the entire system via /sys/kernel/mm/transparent_hugepage/enabled. + However, it can be overridden for individual workloads via + prctl2. + MemoryTHP= may be used to disable THPs at process invocation time to stop providing + THPs for workloads where the drawbacks outweigh the advantages. + When MemoryTHP= is set to inherit or not set at all, systemd + inherits THP settings from the process that starts it and no + prctl2 + PR_SET_THP_DISABLE call is made. + When set to disable, MemoryTHP= disables THPs completely for the process, + irrespecitive of global THP controls. + When set to madvise, MemoryTHP= disables THPs for the process except when + specifically requested via madvise2 + by the process with MADV_HUGEPAGE or MADV_COLLAPSE. + When set to system, MemoryTHP= resets the THP policy to system wide policy. + This can be used when the process that starts systemd has already disabled THPs via + PR_SET_THP_DISABLE, and we want to restore the system default THP setting at + process invokation time. For details, see + Transparent Hugepage Support + in the kernel documentation. + Note that this functionality might not be available, for example if THP is disabled in the + kernel, or the kernel does not support controlling THP at the process level through + prctl2. + + + + + PrivatePIDs= diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 658fc1ee06..19d454820e 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -58,6 +58,7 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_private_bpf, private_bpf, Priva static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_protect_home, protect_home, ProtectHome); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_protect_system, protect_system, ProtectSystem); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_personality, personality, unsigned long); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_memory_thp, memory_thp, MemoryTHP); static BUS_DEFINE_PROPERTY_GET(property_get_ioprio, "i", ExecContext, exec_context_get_effective_ioprio); static BUS_DEFINE_PROPERTY_GET(property_get_mount_apivfs, "b", ExecContext, exec_context_get_effective_mount_apivfs); static BUS_DEFINE_PROPERTY_GET(property_get_bind_log_sockets, "b", ExecContext, exec_context_get_effective_bind_log_sockets); @@ -1407,6 +1408,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("BPFDelegatePrograms", "s", property_get_bpf_delegate_programs, offsetof(ExecContext, bpf_delegate_programs), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("BPFDelegateAttachments", "s", property_get_bpf_delegate_attachments, offsetof(ExecContext, bpf_delegate_attachments), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MemoryKSM", "b", bus_property_get_tristate, offsetof(ExecContext, memory_ksm), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("MemoryTHP", "s", property_get_memory_thp, offsetof(ExecContext, memory_thp), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("UserNamespacePath", "s", NULL, offsetof(ExecContext, user_namespace_path), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("NetworkNamespacePath", "s", NULL, offsetof(ExecContext, network_namespace_path), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("IPCNamespacePath", "s", NULL, offsetof(ExecContext, ipc_namespace_path), SD_BUS_VTABLE_PROPERTY_CONST), @@ -1845,6 +1847,7 @@ static BUS_DEFINE_SET_TRANSIENT_PARSE(keyring_mode, ExecKeyringMode, exec_keyrin static BUS_DEFINE_SET_TRANSIENT_PARSE(protect_proc, ProtectProc, protect_proc_from_string); static BUS_DEFINE_SET_TRANSIENT_PARSE(proc_subset, ProcSubset, proc_subset_from_string); static BUS_DEFINE_SET_TRANSIENT_PARSE(private_bpf, PrivateBPF, private_bpf_from_string); +static BUS_DEFINE_SET_TRANSIENT_PARSE(memory_thp, MemoryTHP, memory_thp_from_string); static BUS_DEFINE_SET_TRANSIENT_PARSE_PTR(bpf_delegate_commands, uint64_t, bpf_delegate_commands_from_string); static BUS_DEFINE_SET_TRANSIENT_PARSE_PTR(bpf_delegate_maps, uint64_t, bpf_delegate_maps_from_string); static BUS_DEFINE_SET_TRANSIENT_PARSE_PTR(bpf_delegate_programs, uint64_t, bpf_delegate_programs_from_string); @@ -2333,6 +2336,9 @@ int bus_exec_context_set_transient_property( if (streq(name, "MemoryKSM")) return bus_set_transient_tristate(u, name, &c->memory_ksm, message, flags, reterr_error); + if (streq(name, "MemoryTHP")) + return bus_set_transient_memory_thp(u, name, &c->memory_thp, message, flags, reterr_error); + if (streq(name, "UtmpIdentifier")) return bus_set_transient_string(u, name, &c->utmp_id, message, flags, reterr_error); diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c index ef4c272bf7..d87392d538 100644 --- a/src/core/exec-invoke.c +++ b/src/core/exec-invoke.c @@ -4866,6 +4866,32 @@ static int exec_fd_mark_hot( return 1; } +static int set_memory_thp(MemoryTHP thp) { + switch (thp) { + + case MEMORY_THP_INHERIT: + return 0; + + case MEMORY_THP_DISABLE: + if (prctl(PR_SET_THP_DISABLE, 1, 0, 0, 0) < 0) + return errno == EINVAL ? -EOPNOTSUPP : -errno; + return 0; + + case MEMORY_THP_MADVISE: + if (prctl(PR_SET_THP_DISABLE, 1, PR_THP_DISABLE_EXCEPT_ADVISED, 0, 0) < 0) + return errno == EINVAL ? -EOPNOTSUPP : -errno; + return 0; + + case MEMORY_THP_SYSTEM: + if (prctl(PR_SET_THP_DISABLE, 0, 0, 0, 0) < 0) + return errno == EINVAL ? -EOPNOTSUPP : -errno; + return 0; + + default: + assert_not_reached(); + } +} + static int send_handoff_timestamp( const ExecContext *c, ExecParameters *p, @@ -5550,6 +5576,16 @@ int exec_invoke( } } + r = set_memory_thp(context->memory_thp); + if (r == -EOPNOTSUPP) + log_debug_errno(r, "Setting MemoryTHP=%s is not supported, ignoring: %m", + memory_thp_to_string(context->memory_thp)); + else if (r < 0) { + *exit_status = EXIT_MEMORY_THP; + return log_error_errno(r, "Failed to set MemoryTHP=%s: %m", + memory_thp_to_string(context->memory_thp)); + } + #if ENABLE_UTMP if (context->utmp_id) { _cleanup_free_ char *username_alloc = NULL; diff --git a/src/core/execute-serialize.c b/src/core/execute-serialize.c index ba28e913ae..538ce72397 100644 --- a/src/core/execute-serialize.c +++ b/src/core/execute-serialize.c @@ -1664,6 +1664,10 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) { if (r < 0) return r; + r = serialize_item(f, "exec-context-memory-thp", memory_thp_to_string(c->memory_thp)); + if (r < 0) + return r; + r = serialize_item(f, "exec-context-private-tmp", private_tmp_to_string(c->private_tmp)); if (r < 0) return r; @@ -2649,6 +2653,10 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) { r = safe_atoi(val, &c->memory_ksm); if (r < 0) return r; + } else if ((val = startswith(l, "exec-context-memory-thp="))) { + c->memory_thp = memory_thp_from_string(val); + if (c->memory_thp < 0) + return c->memory_thp; } else if ((val = startswith(l, "exec-context-private-tmp="))) { c->private_tmp = private_tmp_from_string(val); if (c->private_tmp < 0) diff --git a/src/core/execute.c b/src/core/execute.c index a5cb664d61..92c09947db 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -1117,7 +1117,8 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { "%sProtectHostname: %s%s%s\n" "%sProtectProc: %s\n" "%sProcSubset: %s\n" - "%sPrivateBPF: %s\n", + "%sPrivateBPF: %s\n" + "%sMemoryTHP: %s\n", prefix, c->umask, prefix, empty_to_root(c->working_directory), prefix, empty_to_root(c->root_directory), @@ -1145,7 +1146,8 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { prefix, protect_hostname_to_string(c->protect_hostname), c->private_hostname ? ":" : "", strempty(c->private_hostname), prefix, protect_proc_to_string(c->protect_proc), prefix, proc_subset_to_string(c->proc_subset), - prefix, private_bpf_to_string(c->private_bpf)); + prefix, private_bpf_to_string(c->private_bpf), + prefix, memory_thp_to_string(c->memory_thp)); if (c->private_bpf == PRIVATE_BPF_YES) { _cleanup_free_ char diff --git a/src/core/execute.h b/src/core/execute.h index ae26be6781..29a23f021f 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -311,6 +311,7 @@ typedef struct ExecContext { int mount_apivfs; int bind_log_sockets; int memory_ksm; + MemoryTHP memory_thp; PrivateTmp private_tmp; PrivateTmp private_var_tmp; /* This is not an independent parameter, but calculated from other * parameters in unit_patch_contexts(). */ diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in index 69b92d03cb..69e39ed0dc 100644 --- a/src/core/load-fragment-gperf.gperf.in +++ b/src/core/load-fragment-gperf.gperf.in @@ -195,6 +195,7 @@ {% endif %} {{type}}.ProtectHostname, config_parse_protect_hostname, 0, offsetof({{type}}, exec_context) {{type}}.MemoryKSM, config_parse_tristate, 0, offsetof({{type}}, exec_context.memory_ksm) +{{type}}.MemoryTHP, config_parse_memory_thp, 0, offsetof({{type}}, exec_context.memory_thp) {%- endmacro -%} {%- macro KILL_CONTEXT_CONFIG_ITEMS(type) -%} diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 6a59d33af2..7a5845ab7d 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -163,6 +163,7 @@ DEFINE_CONFIG_PARSE_PTR(config_parse_bpf_delegate_commands, bpf_delegate_command DEFINE_CONFIG_PARSE_PTR(config_parse_bpf_delegate_maps, bpf_delegate_maps_from_string, uint64_t); DEFINE_CONFIG_PARSE_PTR(config_parse_bpf_delegate_programs, bpf_delegate_programs_from_string, uint64_t); DEFINE_CONFIG_PARSE_PTR(config_parse_bpf_delegate_attachments, bpf_delegate_attachments_from_string, uint64_t); +DEFINE_CONFIG_PARSE_ENUM(config_parse_memory_thp, memory_thp, MemoryTHP); bool contains_instance_specifier_superset(const char *s) { const char *p, *q; diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 336ba250bf..3b0ee5fb30 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -168,6 +168,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_cgroup_nft_set); CONFIG_PARSER_PROTOTYPE(config_parse_mount_node); CONFIG_PARSER_PROTOTYPE(config_parse_concurrency_max); CONFIG_PARSER_PROTOTYPE(config_parse_bind_network_interface); +CONFIG_PARSER_PROTOTYPE(config_parse_memory_thp); /* gperf prototypes */ const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length); diff --git a/src/core/namespace.c b/src/core/namespace.c index d75d70f1bc..8893daa230 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -4047,6 +4047,15 @@ DEFINE_STRING_TABLE_LOOKUP(bpf_delegate_map_type, uint64_t); DEFINE_STRING_TABLE_LOOKUP(bpf_delegate_prog_type, uint64_t); DEFINE_STRING_TABLE_LOOKUP(bpf_delegate_attach_type, uint64_t); +static const char* const memory_thp_table[_MEMORY_THP_MAX] = { + [MEMORY_THP_INHERIT] = "inherit", + [MEMORY_THP_DISABLE] = "disable", + [MEMORY_THP_MADVISE] = "madvise", + [MEMORY_THP_SYSTEM] = "system", +}; + +DEFINE_STRING_TABLE_LOOKUP(memory_thp, MemoryTHP); + char* bpf_delegate_to_string(uint64_t u, const char * (*parser)(uint64_t) _const_ ) { assert(parser); diff --git a/src/core/namespace.h b/src/core/namespace.h index aae43db389..d5a2198f1e 100644 --- a/src/core/namespace.h +++ b/src/core/namespace.h @@ -91,6 +91,24 @@ typedef enum PrivatePIDs { _PRIVATE_PIDS_INVALID = -EINVAL, } PrivatePIDs; +typedef enum MemoryTHP { + /* + * Inherit default from process that starts systemd, i.e. do not make + * any PR_SET_THP_DISABLE call. + */ + MEMORY_THP_INHERIT, + MEMORY_THP_DISABLE, /* Disable THPs completely for the prcess */ + MEMORY_THP_MADVISE, /* Disable THPs for the process except when madvised */ + /* + * Use system default THP setting. this can be used when the process that + * starts systemd has already disabled THPs via PR_SET_THP_DISABLE, and we + * want to restore the system default THP setting at process invokation time. + */ + MEMORY_THP_SYSTEM, + _MEMORY_THP_MAX, + _MEMORY_THP_INVALID = -EINVAL, +} MemoryTHP; + typedef struct BindMount { char *source; char *destination; @@ -233,6 +251,8 @@ DECLARE_STRING_TABLE_LOOKUP(proc_subset, ProcSubset); DECLARE_STRING_TABLE_LOOKUP(private_bpf, PrivateBPF); +DECLARE_STRING_TABLE_LOOKUP(memory_thp, MemoryTHP); + DECLARE_STRING_TABLE_LOOKUP(bpf_delegate_cmd, uint64_t); DECLARE_STRING_TABLE_LOOKUP(bpf_delegate_map_type, uint64_t); diff --git a/src/core/varlink-execute.c b/src/core/varlink-execute.c index 70e43386c4..e3d736ad5f 100644 --- a/src/core/varlink-execute.c +++ b/src/core/varlink-execute.c @@ -908,6 +908,7 @@ int unit_exec_context_build_json(sd_json_variant **ret, const char *name, void * SD_JSON_BUILD_PAIR_BOOLEAN("RemoveIPC", c->remove_ipc), JSON_BUILD_PAIR_TRISTATE_NON_NULL("PrivateMounts", c->private_mounts), JSON_BUILD_PAIR_STRING_NON_EMPTY("MountFlags", mount_propagation_flag_to_string(c->mount_propagation_flag)), + SD_JSON_BUILD_PAIR_STRING("MemoryTHP", memory_thp_to_string(c->memory_thp)), /* System Call Filtering */ JSON_BUILD_PAIR_CALLBACK_NON_NULL("SystemCallFilter", syscall_filter_build_json, c), diff --git a/src/include/musl/sys/prctl.h b/src/include/musl/sys/prctl.h index 0473a706cc..cc1d4ac983 100644 --- a/src/include/musl/sys/prctl.h +++ b/src/include/musl/sys/prctl.h @@ -17,3 +17,7 @@ #ifndef PR_SET_MEMORY_MERGE #define PR_SET_MEMORY_MERGE 67 #endif + +#ifndef PR_THP_DISABLE_EXCEPT_ADVISED +#define PR_THP_DISABLE_EXCEPT_ADVISED (1 << 1) +#endif diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 0b9e6ba073..048e02e527 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -2494,6 +2494,7 @@ static const BusProperty execute_properties[] = { { "LogRateLimitBurst", bus_append_safe_atou }, { "TTYRows", bus_append_safe_atou }, { "TTYColumns", bus_append_safe_atou }, + { "MemoryTHP", bus_append_string }, { "MountFlags", bus_append_mount_propagation_flag_from_string }, { "Environment", bus_append_strv_cunescape }, { "UnsetEnvironment", bus_append_strv_cunescape }, diff --git a/src/shared/exit-status.c b/src/shared/exit-status.c index d7567dc026..b40664d204 100644 --- a/src/shared/exit-status.c +++ b/src/shared/exit-status.c @@ -71,6 +71,7 @@ const ExitStatusMapping exit_status_mappings[256] = { [EXIT_CREDENTIALS] = { "CREDENTIALS", EXIT_STATUS_SYSTEMD }, [EXIT_BPF] = { "BPF", EXIT_STATUS_SYSTEMD }, [EXIT_KSM] = { "KSM", EXIT_STATUS_SYSTEMD }, + [EXIT_MEMORY_THP] = { "MEMORY_THP", EXIT_STATUS_SYSTEMD }, [EXIT_EXCEPTION] = { "EXCEPTION", EXIT_STATUS_SYSTEMD }, diff --git a/src/shared/exit-status.h b/src/shared/exit-status.h index 4c656c109f..28730ff64d 100644 --- a/src/shared/exit-status.h +++ b/src/shared/exit-status.h @@ -70,6 +70,7 @@ enum { EXIT_CREDENTIALS, EXIT_BPF, EXIT_KSM, + EXIT_MEMORY_THP, EXIT_EXCEPTION = 255, /* Whenever we want to propagate an abnormal/signal exit, in line with bash */ }; diff --git a/src/shared/varlink-io.systemd.Unit.c b/src/shared/varlink-io.systemd.Unit.c index 390e1b414b..9670951c47 100644 --- a/src/shared/varlink-io.systemd.Unit.c +++ b/src/shared/varlink-io.systemd.Unit.c @@ -626,6 +626,8 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE( SD_VARLINK_DEFINE_FIELD(PrivateMounts, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man"PROJECT_VERSION_STR"systemd.exec.html#MountFlags="), SD_VARLINK_DEFINE_FIELD(MountFlags, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man"PROJECT_VERSION_STR"systemd.exec.html#MemoryTHP="), + SD_VARLINK_DEFINE_FIELD(MemoryTHP, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), /* System Call Filtering * https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#System%20Call%20Filtering */