+52
-2
@@ -128,6 +128,9 @@ void cgroup_context_init(CGroupContext *c) {
|
||||
.startup_blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID,
|
||||
|
||||
.tasks_max = TASKS_MAX_UNSET,
|
||||
|
||||
.moom_swap = MANAGED_OOM_AUTO,
|
||||
.moom_mem_pressure = MANAGED_OOM_AUTO,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -411,7 +414,10 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
|
||||
"%sTasksMax: %" PRIu64 "\n"
|
||||
"%sDevicePolicy: %s\n"
|
||||
"%sDisableControllers: %s\n"
|
||||
"%sDelegate: %s\n",
|
||||
"%sDelegate: %s\n"
|
||||
"%sManagedOOMSwap: %s\n"
|
||||
"%sManagedOOMMemoryPressure: %s\n"
|
||||
"%sManagedOOMMemoryPressureLimitPercent: %d%%\n",
|
||||
prefix, yes_no(c->cpu_accounting),
|
||||
prefix, yes_no(c->io_accounting),
|
||||
prefix, yes_no(c->blockio_accounting),
|
||||
@@ -441,7 +447,10 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
|
||||
prefix, tasks_max_resolve(&c->tasks_max),
|
||||
prefix, cgroup_device_policy_to_string(c->device_policy),
|
||||
prefix, strempty(disable_controllers_str),
|
||||
prefix, yes_no(c->delegate));
|
||||
prefix, yes_no(c->delegate),
|
||||
prefix, managed_oom_mode_to_string(c->moom_swap),
|
||||
prefix, managed_oom_mode_to_string(c->moom_mem_pressure),
|
||||
prefix, c->moom_mem_pressure_limit);
|
||||
|
||||
if (c->delegate) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
@@ -2672,6 +2681,47 @@ static void unit_remove_from_cgroup_empty_queue(Unit *u) {
|
||||
u->in_cgroup_empty_queue = false;
|
||||
}
|
||||
|
||||
int unit_check_oomd_kill(Unit *u) {
|
||||
_cleanup_free_ char *value = NULL;
|
||||
bool increased;
|
||||
uint64_t n = 0;
|
||||
int r;
|
||||
|
||||
if (!u->cgroup_path)
|
||||
return 0;
|
||||
|
||||
r = cg_all_unified();
|
||||
if (r < 0)
|
||||
return log_unit_debug_errno(u, r, "Couldn't determine whether we are in all unified mode: %m");
|
||||
else if (r == 0)
|
||||
return 0;
|
||||
|
||||
r = cg_get_xattr_malloc(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "user.systemd_oomd_kill", &value);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
return r;
|
||||
|
||||
if (!isempty(value)) {
|
||||
r = safe_atou64(value, &n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
increased = n > u->managed_oom_kill_last;
|
||||
u->managed_oom_kill_last = n;
|
||||
|
||||
if (!increased)
|
||||
return 0;
|
||||
|
||||
if (n > 0)
|
||||
log_struct(LOG_NOTICE,
|
||||
"MESSAGE_ID=" SD_MESSAGE_UNIT_OOMD_KILL_STR,
|
||||
LOG_UNIT_ID(u),
|
||||
LOG_UNIT_INVOCATION_ID(u),
|
||||
LOG_UNIT_MESSAGE(u, "systemd-oomd killed %"PRIu64" process(es) in this unit.", n));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int unit_check_oom(Unit *u) {
|
||||
_cleanup_free_ char *oom_kill = NULL;
|
||||
bool increased;
|
||||
|
||||
@@ -159,6 +159,11 @@ struct CGroupContext {
|
||||
|
||||
/* Common */
|
||||
TasksMax tasks_max;
|
||||
|
||||
/* Settings for systemd-oomd */
|
||||
ManagedOOMMode moom_swap;
|
||||
ManagedOOMMode moom_mem_pressure;
|
||||
int moom_mem_pressure_limit;
|
||||
};
|
||||
|
||||
/* Used when querying IP accounting data */
|
||||
@@ -224,6 +229,7 @@ int unit_watch_cgroup(Unit *u);
|
||||
int unit_watch_cgroup_memory(Unit *u);
|
||||
|
||||
void unit_add_to_cgroup_empty_queue(Unit *u);
|
||||
int unit_check_oomd_kill(Unit *u);
|
||||
int unit_check_oom(Unit *u);
|
||||
|
||||
int unit_attach_pids_to_cgroup(Unit *u, Set *pids, const char *suffix_path);
|
||||
|
||||
+177
-1
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "core-varlink.h"
|
||||
#include "mkdir.h"
|
||||
#include "strv.h"
|
||||
#include "user-util.h"
|
||||
#include "varlink.h"
|
||||
|
||||
@@ -15,6 +16,11 @@ typedef struct LookupParameters {
|
||||
const char *service;
|
||||
} LookupParameters;
|
||||
|
||||
static const char* const managed_oom_mode_properties[] = {
|
||||
"ManagedOOMSwap",
|
||||
"ManagedOOMMemoryPressure",
|
||||
};
|
||||
|
||||
static int build_user_json(const char *user_name, uid_t uid, JsonVariant **ret) {
|
||||
assert(user_name);
|
||||
assert(uid_is_valid(uid));
|
||||
@@ -45,6 +51,150 @@ static bool user_match_lookup_parameters(LookupParameters *p, const char *name,
|
||||
return true;
|
||||
}
|
||||
|
||||
static int build_managed_oom_json_array_element(Unit *u, const char *property, JsonVariant **ret_v) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
||||
CGroupContext *c;
|
||||
const char *mode;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(property);
|
||||
assert(ret_v);
|
||||
|
||||
if (!UNIT_VTABLE(u)->can_set_managed_oom)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
c = unit_get_cgroup_context(u);
|
||||
if (!c)
|
||||
return -EINVAL;
|
||||
|
||||
if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)))
|
||||
/* systemd-oomd should always treat inactive units as though they didn't enable any action since they
|
||||
* should not have a valid cgroup */
|
||||
mode = managed_oom_mode_to_string(MANAGED_OOM_AUTO);
|
||||
else if (streq(property, "ManagedOOMSwap"))
|
||||
mode = managed_oom_mode_to_string(c->moom_swap);
|
||||
else if (streq(property, "ManagedOOMMemoryPressure"))
|
||||
mode = managed_oom_mode_to_string(c->moom_mem_pressure);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
r = json_build(&v, JSON_BUILD_OBJECT(
|
||||
JSON_BUILD_PAIR("mode", JSON_BUILD_STRING(mode)),
|
||||
JSON_BUILD_PAIR("path", JSON_BUILD_STRING(u->cgroup_path)),
|
||||
JSON_BUILD_PAIR("property", JSON_BUILD_STRING(property)),
|
||||
JSON_BUILD_PAIR("limit", JSON_BUILD_UNSIGNED(c->moom_mem_pressure_limit))));
|
||||
|
||||
*ret_v = TAKE_PTR(v);
|
||||
return r;
|
||||
}
|
||||
|
||||
int manager_varlink_send_managed_oom_update(Unit *u) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *arr = NULL, *v = NULL;
|
||||
CGroupContext *c;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (!UNIT_VTABLE(u)->can_set_managed_oom || !u->manager || !u->manager->managed_oom_varlink_request || !u->cgroup_path)
|
||||
return 0;
|
||||
|
||||
c = unit_get_cgroup_context(u);
|
||||
if (!c)
|
||||
return 0;
|
||||
|
||||
r = json_build(&arr, JSON_BUILD_EMPTY_ARRAY);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(managed_oom_mode_properties); i++) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *e = NULL;
|
||||
|
||||
r = build_managed_oom_json_array_element(u, managed_oom_mode_properties[i], &e);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = json_variant_append_array(&arr, e);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = json_build(&v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("cgroups", JSON_BUILD_VARIANT(arr))));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return varlink_notify(u->manager->managed_oom_varlink_request, v);
|
||||
}
|
||||
|
||||
static int vl_method_subscribe_managed_oom_cgroups(
|
||||
Varlink *link,
|
||||
JsonVariant *parameters,
|
||||
VarlinkMethodFlags flags,
|
||||
void *userdata) {
|
||||
static const UnitType supported_unit_types[] = { UNIT_SLICE, UNIT_SERVICE, UNIT_SCOPE };
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *arr = NULL;
|
||||
Manager *m = userdata;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(m);
|
||||
|
||||
if (json_variant_elements(parameters) > 0)
|
||||
return varlink_error_invalid_parameter(link, parameters);
|
||||
|
||||
/* We only take one subscriber for this method so return an error if there's already an existing one.
|
||||
* This shouldn't happen since systemd-oomd is the only client of this method. */
|
||||
if (FLAGS_SET(flags, VARLINK_METHOD_MORE) && m->managed_oom_varlink_request)
|
||||
return varlink_error(m->managed_oom_varlink_request, VARLINK_ERROR_SUBSCRIPTION_TAKEN, NULL);
|
||||
|
||||
r = json_build(&arr, JSON_BUILD_EMPTY_ARRAY);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(supported_unit_types); i++) {
|
||||
Unit *u;
|
||||
|
||||
LIST_FOREACH(units_by_type, u, m->units_by_type[supported_unit_types[i]]) {
|
||||
CGroupContext *c;
|
||||
|
||||
if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)))
|
||||
continue;
|
||||
|
||||
c = unit_get_cgroup_context(u);
|
||||
if (!c)
|
||||
continue;
|
||||
|
||||
for (size_t j = 0; j < ELEMENTSOF(managed_oom_mode_properties); j++) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *e = NULL;
|
||||
|
||||
/* For the initial varlink call we only care about units that enabled (i.e. mode is not
|
||||
* set to "auto") oomd properties. */
|
||||
if (!(streq(managed_oom_mode_properties[j], "ManagedOOMSwap") && c->moom_swap == MANAGED_OOM_KILL) &&
|
||||
!(streq(managed_oom_mode_properties[j], "ManagedOOMMemoryPressure") && c->moom_mem_pressure == MANAGED_OOM_KILL))
|
||||
continue;
|
||||
|
||||
r = build_managed_oom_json_array_element(u, managed_oom_mode_properties[j], &e);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = json_variant_append_array(&arr, e);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r = json_build(&v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("cgroups", JSON_BUILD_VARIANT(arr))));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!FLAGS_SET(flags, VARLINK_METHOD_MORE))
|
||||
return varlink_reply(link, v);
|
||||
|
||||
m->managed_oom_varlink_request = varlink_ref(link);
|
||||
return varlink_notify(m->managed_oom_varlink_request, v);
|
||||
}
|
||||
|
||||
static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
|
||||
|
||||
static const JsonDispatch dispatch_table[] = {
|
||||
@@ -262,6 +412,17 @@ static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, Var
|
||||
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
||||
}
|
||||
|
||||
static void vl_disconnect(VarlinkServer *s, Varlink *link, void *userdata) {
|
||||
Manager *m = userdata;
|
||||
|
||||
assert(m);
|
||||
assert(s);
|
||||
assert(link);
|
||||
|
||||
if (link == m->managed_oom_varlink_request)
|
||||
m->managed_oom_varlink_request = varlink_unref(link);
|
||||
}
|
||||
|
||||
int manager_varlink_init(Manager *m) {
|
||||
_cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
|
||||
int r;
|
||||
@@ -284,16 +445,25 @@ int manager_varlink_init(Manager *m) {
|
||||
s,
|
||||
"io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record,
|
||||
"io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record,
|
||||
"io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships);
|
||||
"io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships,
|
||||
"io.systemd.ManagedOOM.SubscribeManagedOOMCGroups", vl_method_subscribe_managed_oom_cgroups);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to register varlink methods: %m");
|
||||
|
||||
r = varlink_server_bind_disconnect(s, vl_disconnect);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to register varlink disconnect handler: %m");
|
||||
|
||||
if (!MANAGER_IS_TEST_RUN(m)) {
|
||||
(void) mkdir_p_label("/run/systemd/userdb", 0755);
|
||||
|
||||
r = varlink_server_listen_address(s, "/run/systemd/userdb/io.systemd.DynamicUser", 0666);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to bind to varlink socket: %m");
|
||||
|
||||
r = varlink_server_listen_address(s, VARLINK_ADDR_PATH_MANAGED_OOM, 0666);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to bind to varlink socket: %m");
|
||||
}
|
||||
|
||||
r = varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL);
|
||||
@@ -307,5 +477,11 @@ int manager_varlink_init(Manager *m) {
|
||||
void manager_varlink_done(Manager *m) {
|
||||
assert(m);
|
||||
|
||||
/* Send the final message if we still have a subscribe request open. */
|
||||
if (m->managed_oom_varlink_request) {
|
||||
(void) varlink_error(m->managed_oom_varlink_request, VARLINK_ERROR_DISCONNECTED, NULL);
|
||||
m->managed_oom_varlink_request = varlink_unref(m->managed_oom_varlink_request);
|
||||
}
|
||||
|
||||
m->varlink_server = varlink_server_unref(m->varlink_server);
|
||||
}
|
||||
|
||||
@@ -5,3 +5,8 @@
|
||||
|
||||
int manager_varlink_init(Manager *m);
|
||||
void manager_varlink_done(Manager *m);
|
||||
|
||||
/* The manager is expected to send an update to systemd-oomd if one of the following occurs:
|
||||
* - The value of ManagedOOM*= properties change
|
||||
* - A unit with ManagedOOM*= properties changes unit active state */
|
||||
int manager_varlink_send_managed_oom_update(Unit *u);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "bus-get-properties.h"
|
||||
#include "cgroup-util.h"
|
||||
#include "cgroup.h"
|
||||
#include "core-varlink.h"
|
||||
#include "dbus-cgroup.h"
|
||||
#include "dbus-util.h"
|
||||
#include "errno-util.h"
|
||||
@@ -19,6 +20,7 @@
|
||||
BUS_DEFINE_PROPERTY_GET(bus_property_get_tasks_max, "t", TasksMax, tasks_max_resolve);
|
||||
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_cgroup_device_policy, cgroup_device_policy, CGroupDevicePolicy);
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_managed_oom_mode, managed_oom_mode, ManagedOOMMode);
|
||||
|
||||
static int property_get_cgroup_mask(
|
||||
sd_bus *bus,
|
||||
@@ -391,6 +393,9 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
|
||||
SD_BUS_PROPERTY("IPIngressFilterPath", "as", NULL, offsetof(CGroupContext, ip_filters_ingress), 0),
|
||||
SD_BUS_PROPERTY("IPEgressFilterPath", "as", NULL, offsetof(CGroupContext, ip_filters_egress), 0),
|
||||
SD_BUS_PROPERTY("DisableControllers", "as", property_get_cgroup_mask, offsetof(CGroupContext, disable_controllers), 0),
|
||||
SD_BUS_PROPERTY("ManagedOOMSwap", "s", property_get_managed_oom_mode, offsetof(CGroupContext, moom_swap), 0),
|
||||
SD_BUS_PROPERTY("ManagedOOMMemoryPressure", "s", property_get_managed_oom_mode, offsetof(CGroupContext, moom_mem_pressure), 0),
|
||||
SD_BUS_PROPERTY("ManagedOOMMemoryPressureLimitPercent", "s", bus_property_get_percent, offsetof(CGroupContext, moom_mem_pressure_limit), 0),
|
||||
SD_BUS_VTABLE_END
|
||||
};
|
||||
|
||||
@@ -1667,6 +1672,45 @@ int bus_cgroup_set_property(
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (STR_IN_SET(name, "ManagedOOMSwap", "ManagedOOMMemoryPressure")) {
|
||||
ManagedOOMMode *cgroup_mode = streq(name, "ManagedOOMSwap") ? &c->moom_swap : &c->moom_mem_pressure;
|
||||
ManagedOOMMode m;
|
||||
const char *mode;
|
||||
|
||||
if (!UNIT_VTABLE(u)->can_set_managed_oom)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot set %s for this unit type", name);
|
||||
|
||||
r = sd_bus_message_read(message, "s", &mode);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
m = managed_oom_mode_from_string(mode);
|
||||
if (m < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
||||
*cgroup_mode = m;
|
||||
unit_write_settingf(u, flags, name, "%s=%s", name, mode);
|
||||
}
|
||||
|
||||
(void) manager_varlink_send_managed_oom_update(u);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (streq(name, "ManagedOOMMemoryPressureLimitPercent")) {
|
||||
if (!UNIT_VTABLE(u)->can_set_managed_oom)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot set %s for this unit type", name);
|
||||
|
||||
r = bus_set_transient_percent(u, name, &c->moom_mem_pressure_limit, message, flags, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (c->moom_mem_pressure == MANAGED_OOM_KILL)
|
||||
(void) manager_varlink_send_managed_oom_update(u);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (streq(name, "DisableControllers") || (u->transient && u->load_state == UNIT_STUB))
|
||||
return bus_cgroup_set_transient_property(u, c, name, message, flags, error);
|
||||
|
||||
|
||||
@@ -91,6 +91,35 @@ int bus_set_transient_bool(
|
||||
return 1;
|
||||
}
|
||||
|
||||
int bus_set_transient_percent(
|
||||
Unit *u,
|
||||
const char *name,
|
||||
int *p,
|
||||
sd_bus_message *message,
|
||||
UnitWriteFlags flags,
|
||||
sd_bus_error *error) {
|
||||
|
||||
const char *v;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
|
||||
r = sd_bus_message_read(message, "s", &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = parse_percent(v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
||||
*p = r;
|
||||
unit_write_settingf(u, flags, name, "%s=%d%%", name, r);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int bus_set_transient_usec_internal(
|
||||
Unit *u,
|
||||
const char *name,
|
||||
|
||||
@@ -240,6 +240,7 @@ int bus_set_transient_user_relaxed(Unit *u, const char *name, char **p, sd_bus_m
|
||||
int bus_set_transient_path(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
|
||||
int bus_set_transient_string(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
|
||||
int bus_set_transient_bool(Unit *u, const char *name, bool *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
|
||||
int bus_set_transient_percent(Unit *u, const char *name, int *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
|
||||
int bus_set_transient_usec_internal(Unit *u, const char *name, usec_t *p, bool fix_0, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
|
||||
static inline int bus_set_transient_usec(Unit *u, const char *name, usec_t *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error) {
|
||||
return bus_set_transient_usec_internal(u, name, p, false, message, flags, error);
|
||||
|
||||
@@ -224,6 +224,9 @@ $1.IPAddressAllow, config_parse_ip_address_access, 0,
|
||||
$1.IPAddressDeny, config_parse_ip_address_access, 0, offsetof($1, cgroup_context.ip_address_deny)
|
||||
$1.IPIngressFilterPath, config_parse_ip_filter_bpf_progs, 0, offsetof($1, cgroup_context.ip_filters_ingress)
|
||||
$1.IPEgressFilterPath, config_parse_ip_filter_bpf_progs, 0, offsetof($1, cgroup_context.ip_filters_egress)
|
||||
$1.ManagedOOMSwap, config_parse_managed_oom_mode, 0, offsetof($1, cgroup_context.moom_swap)
|
||||
$1.ManagedOOMMemoryPressure, config_parse_managed_oom_mode, 0, offsetof($1, cgroup_context.moom_mem_pressure)
|
||||
$1.ManagedOOMMemoryPressureLimitPercent,config_parse_managed_oom_mem_pressure_limit,0, offsetof($1, cgroup_context)
|
||||
$1.NetClass, config_parse_warn_compat, DISABLED_LEGACY, 0'
|
||||
)m4_dnl
|
||||
Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description)
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "capability-util.h"
|
||||
#include "cgroup-setup.h"
|
||||
#include "conf-parser.h"
|
||||
#include "core-varlink.h"
|
||||
#include "cpu-set-util.h"
|
||||
#include "env-util.h"
|
||||
#include "errno-list.h"
|
||||
@@ -3812,6 +3813,86 @@ int config_parse_delegate(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_managed_oom_mode(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
Unit *u = userdata;
|
||||
ManagedOOMMode *mode = data, m;
|
||||
UnitType t;
|
||||
|
||||
t = unit_name_to_type(unit);
|
||||
assert(t != _UNIT_TYPE_INVALID);
|
||||
|
||||
if (!unit_vtable[t]->can_set_managed_oom)
|
||||
return log_syntax(unit, LOG_WARNING, filename, line, 0, "%s= is not supported for this unit type, ignoring.", lvalue);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
*mode = MANAGED_OOM_AUTO;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
m = managed_oom_mode_from_string(rvalue);
|
||||
if (m < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid syntax, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
*mode = m;
|
||||
|
||||
finish:
|
||||
(void) manager_varlink_send_managed_oom_update(u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_managed_oom_mem_pressure_limit(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
Unit *u = userdata;
|
||||
CGroupContext *c = data;
|
||||
UnitType t;
|
||||
int r;
|
||||
|
||||
t = unit_name_to_type(unit);
|
||||
assert(t != _UNIT_TYPE_INVALID);
|
||||
|
||||
if (!unit_vtable[t]->can_set_managed_oom)
|
||||
return log_syntax(unit, LOG_WARNING, filename, line, 0, "%s= is not supported for this unit type, ignoring.", lvalue);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
c->moom_mem_pressure_limit = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = parse_percent(rvalue);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse limit percent value, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
c->moom_mem_pressure_limit = r;
|
||||
|
||||
finish:
|
||||
/* Only update the limit if memory pressure detection is enabled because the information is irrelevant otherwise */
|
||||
if (c->moom_mem_pressure == MANAGED_OOM_KILL)
|
||||
(void) manager_varlink_send_managed_oom_update(u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_device_allow(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
|
||||
@@ -76,6 +76,8 @@ CONFIG_PARSER_PROTOTYPE(config_parse_cpu_shares);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_memory_limit);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_tasks_max);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_delegate);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_managed_oom_mode);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_managed_oom_mem_pressure_limit);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_device_policy);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_device_allow);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_io_device_latency);
|
||||
|
||||
@@ -2578,6 +2578,11 @@ static int manager_dispatch_sigchld(sd_event_source *source, void *userdata) {
|
||||
* We only do this for the cgroup the PID belonged to. */
|
||||
(void) unit_check_oom(u1);
|
||||
|
||||
/* This only logs for now. In the future when the interface for kills/notifications
|
||||
* is more stable we can extend service results table similar to how kernel oom kills
|
||||
* are managed. */
|
||||
(void) unit_check_oomd_kill(u1);
|
||||
|
||||
manager_invoke_sigchld_event(m, u1, &si);
|
||||
}
|
||||
if (u2)
|
||||
|
||||
@@ -434,6 +434,8 @@ struct Manager {
|
||||
bool honor_device_enumeration;
|
||||
|
||||
VarlinkServer *varlink_server;
|
||||
/* Only systemd-oomd should be using this to subscribe to changes in ManagedOOM settings */
|
||||
Varlink *managed_oom_varlink_request;
|
||||
};
|
||||
|
||||
static inline usec_t manager_default_timeout_abort_usec(Manager *m) {
|
||||
|
||||
@@ -621,6 +621,7 @@ const UnitVTable scope_vtable = {
|
||||
.can_delegate = true,
|
||||
.can_fail = true,
|
||||
.once_only = true,
|
||||
.can_set_managed_oom = true,
|
||||
|
||||
.init = scope_init,
|
||||
.load = scope_load,
|
||||
|
||||
@@ -4533,6 +4533,7 @@ const UnitVTable service_vtable = {
|
||||
.can_transient = true,
|
||||
.can_delegate = true,
|
||||
.can_fail = true,
|
||||
.can_set_managed_oom = true,
|
||||
|
||||
.init = service_init,
|
||||
.done = service_done,
|
||||
|
||||
@@ -435,6 +435,7 @@ const UnitVTable slice_vtable = {
|
||||
.private_section = "Slice",
|
||||
|
||||
.can_transient = true,
|
||||
.can_set_managed_oom = true,
|
||||
|
||||
.init = slice_init,
|
||||
.load = slice_load,
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "bus-util.h"
|
||||
#include "cgroup-setup.h"
|
||||
#include "cgroup-util.h"
|
||||
#include "core-varlink.h"
|
||||
#include "dbus-unit.h"
|
||||
#include "dbus.h"
|
||||
#include "dropin.h"
|
||||
@@ -1573,6 +1574,31 @@ static int unit_add_mount_dependencies(Unit *u) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unit_add_oomd_dependencies(Unit *u) {
|
||||
CGroupContext *c;
|
||||
bool wants_oomd;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (!u->default_dependencies)
|
||||
return 0;
|
||||
|
||||
c = unit_get_cgroup_context(u);
|
||||
if (!c)
|
||||
return 0;
|
||||
|
||||
wants_oomd = (c->moom_swap == MANAGED_OOM_KILL || c->moom_mem_pressure == MANAGED_OOM_KILL);
|
||||
if (!wants_oomd)
|
||||
return 0;
|
||||
|
||||
r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, "systemd-oomd.service", true, UNIT_DEPENDENCY_FILE);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unit_add_startup_units(Unit *u) {
|
||||
CGroupContext *c;
|
||||
|
||||
@@ -1633,6 +1659,10 @@ int unit_load(Unit *u) {
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = unit_add_oomd_dependencies(u);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = unit_add_startup_units(u);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
@@ -2592,6 +2622,18 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
|
||||
* the bus queue, so that any job change signal queued will force out the unit change signal first. */
|
||||
unit_add_to_dbus_queue(u);
|
||||
|
||||
/* Update systemd-oomd on the property/state change */
|
||||
if (os != ns) {
|
||||
/* Always send an update if the unit is going into an inactive state so systemd-oomd knows to stop
|
||||
* monitoring.
|
||||
* Also send an update whenever the unit goes active; this is to handle a case where an override file
|
||||
* sets one of the ManagedOOM*= properties to "kill", then later removes it. systemd-oomd needs to
|
||||
* know to stop monitoring when the unit changes from "kill" -> "auto" on daemon-reload, but we don't
|
||||
* have the information on the property. Thus, indiscriminately send an update. */
|
||||
if (UNIT_IS_INACTIVE_OR_FAILED(ns) || ns == UNIT_ACTIVE)
|
||||
(void) manager_varlink_send_managed_oom_update(u);
|
||||
}
|
||||
|
||||
/* Update timestamps for state changes */
|
||||
if (!MANAGER_IS_RELOADING(m)) {
|
||||
dual_timestamp_get(&u->state_change_timestamp);
|
||||
@@ -3558,6 +3600,9 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
|
||||
if (u->cpu_usage_last != NSEC_INFINITY)
|
||||
(void) serialize_item_format(f, "cpu-usage-last", "%" PRIu64, u->cpu_usage_last);
|
||||
|
||||
if (u->managed_oom_kill_last > 0)
|
||||
(void) serialize_item_format(f, "managed-oom-kill-last", "%" PRIu64, u->managed_oom_kill_last);
|
||||
|
||||
if (u->oom_kill_last > 0)
|
||||
(void) serialize_item_format(f, "oom-kill-last", "%" PRIu64, u->oom_kill_last);
|
||||
|
||||
@@ -3803,6 +3848,14 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
|
||||
|
||||
continue;
|
||||
|
||||
} else if (streq(l, "managed-oom-kill-last")) {
|
||||
|
||||
r = safe_atou64(v, &u->managed_oom_kill_last);
|
||||
if (r < 0)
|
||||
log_unit_debug(u, "Failed to read managed OOM kill last %s, ignoring.", v);
|
||||
|
||||
continue;
|
||||
|
||||
} else if (streq(l, "oom-kill-last")) {
|
||||
|
||||
r = safe_atou64(v, &u->oom_kill_last);
|
||||
|
||||
+7
-1
@@ -260,7 +260,10 @@ typedef struct Unit {
|
||||
nsec_t cpu_usage_base;
|
||||
nsec_t cpu_usage_last; /* the most recently read value */
|
||||
|
||||
/* The current counter of the oom_kill field in the memory.events cgroup attribute */
|
||||
/* The current counter of processes sent SIGKILL by systemd-oomd */
|
||||
uint64_t managed_oom_kill_last;
|
||||
|
||||
/* The current counter of the oom_kill field in the memory.events cgroup attribute */
|
||||
uint64_t oom_kill_last;
|
||||
|
||||
/* Where the io.stat data was at the time the unit was started */
|
||||
@@ -625,6 +628,9 @@ typedef struct UnitVTable {
|
||||
|
||||
/* True if queued jobs of this type should be GC'ed if no other job needs them anymore */
|
||||
bool gc_jobs:1;
|
||||
|
||||
/* True if systemd-oomd can monitor and act on this unit's recursive children's cgroup(s) */
|
||||
bool can_set_managed_oom:1;
|
||||
} UnitVTable;
|
||||
|
||||
extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX];
|
||||
|
||||
Reference in New Issue
Block a user