core: generalize memory accounting attribute handling
Follow-up for #29941 Also, support for MemoryCurrent in cgroup v1 is removed, as we're going to remove that completely anyway. Fixes #30000
This commit is contained in:
+38
-46
@@ -4017,6 +4017,8 @@ int unit_get_memory_available(Unit *u, uint64_t *ret) {
|
||||
int unit_get_memory_current(Unit *u, uint64_t *ret) {
|
||||
int r;
|
||||
|
||||
// FIXME: Merge this into unit_get_memory_accounting after support for cgroup v1 is dropped
|
||||
|
||||
assert(u);
|
||||
assert(ret);
|
||||
|
||||
@@ -4040,12 +4042,21 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) {
|
||||
return cg_get_attribute_as_uint64("memory", u->cgroup_path, r > 0 ? "memory.current" : "memory.usage_in_bytes", ret);
|
||||
}
|
||||
|
||||
static int unit_get_memory_attr_raw(Unit *u, const char* mem_attribute, uint64_t *ret) {
|
||||
int unit_get_memory_accounting(Unit *u, CGroupMemoryAccountingMetric metric, uint64_t *ret) {
|
||||
|
||||
static const char* const attributes_table[_CGROUP_MEMORY_ACCOUNTING_METRIC_MAX] = {
|
||||
[CGROUP_MEMORY_PEAK] = "memory.peak",
|
||||
[CGROUP_MEMORY_SWAP_CURRENT] = "memory.swap.current",
|
||||
[CGROUP_MEMORY_SWAP_PEAK] = "memory.swap.peak",
|
||||
[CGROUP_MEMORY_ZSWAP_CURRENT] = "memory.zswap.current",
|
||||
};
|
||||
|
||||
uint64_t bytes;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(mem_attribute);
|
||||
assert(ret);
|
||||
assert(metric >= 0);
|
||||
assert(metric < _CGROUP_MEMORY_ACCOUNTING_METRIC_MAX);
|
||||
|
||||
if (!UNIT_CGROUP_BOOL(u, memory_accounting))
|
||||
return -ENODATA;
|
||||
@@ -4057,64 +4068,36 @@ static int unit_get_memory_attr_raw(Unit *u, const char* mem_attribute, uint64_t
|
||||
if (unit_has_host_root_cgroup(u))
|
||||
return -ENODATA;
|
||||
|
||||
if ((u->cgroup_realized_mask & CGROUP_MASK_MEMORY) == 0)
|
||||
if (!FLAGS_SET(u->cgroup_realized_mask, CGROUP_MASK_MEMORY))
|
||||
return -ENODATA;
|
||||
|
||||
r = cg_all_unified();
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!r)
|
||||
if (r == 0)
|
||||
return -ENODATA;
|
||||
|
||||
return cg_get_attribute_as_uint64("memory", u->cgroup_path, mem_attribute, ret);
|
||||
}
|
||||
|
||||
static int unit_get_memory_attr_cached(Unit *u, const char* mem_attribute, uint64_t* last, uint64_t *ret) {
|
||||
uint64_t bytes;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(mem_attribute);
|
||||
assert(last);
|
||||
|
||||
if (!UNIT_CGROUP_BOOL(u, memory_accounting))
|
||||
return -ENODATA;
|
||||
|
||||
r = unit_get_memory_attr_raw(u, mem_attribute, &bytes);
|
||||
if (r == -ENODATA && *last != UINT64_MAX) {
|
||||
/* If we can't get the memory peak anymore (because the cgroup was already removed, for example),
|
||||
* use our cached value. */
|
||||
|
||||
if (ret)
|
||||
*ret = *last;
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
r = cg_get_attribute_as_uint64("memory", u->cgroup_path, attributes_table[metric], &bytes);
|
||||
if (r < 0 && (r != -ENODATA || metric > _CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST))
|
||||
return r;
|
||||
|
||||
*last = bytes;
|
||||
if (metric <= _CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST) {
|
||||
uint64_t *last = &u->memory_accounting_last[metric];
|
||||
|
||||
if (r >= 0)
|
||||
*last = bytes;
|
||||
else if (*last != UINT64_MAX)
|
||||
bytes = *last;
|
||||
else
|
||||
return r;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
*ret = bytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unit_get_memory_peak(Unit *u, uint64_t *ret) {
|
||||
return unit_get_memory_attr_cached(u, "memory.peak", &u->memory_peak_last, ret);
|
||||
}
|
||||
|
||||
int unit_get_memory_swap_current(Unit *u, uint64_t *ret) {
|
||||
return unit_get_memory_attr_raw(u, "memory.swap.current", ret);
|
||||
}
|
||||
|
||||
int unit_get_memory_swap_peak(Unit *u, uint64_t *ret) {
|
||||
return unit_get_memory_attr_cached(u, "memory.swap.peak", &u->memory_swap_peak_last, ret);
|
||||
}
|
||||
|
||||
int unit_get_memory_zswap_current(Unit *u, uint64_t *ret) {
|
||||
return unit_get_memory_attr_raw(u, "memory.zswap.current", ret);
|
||||
}
|
||||
|
||||
int unit_get_tasks_current(Unit *u, uint64_t *ret) {
|
||||
assert(u);
|
||||
assert(ret);
|
||||
@@ -4650,3 +4633,12 @@ static const char* const cgroup_io_accounting_metric_table[_CGROUP_IO_ACCOUNTING
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(cgroup_io_accounting_metric, CGroupIOAccountingMetric);
|
||||
|
||||
static const char* const cgroup_memory_accounting_metric_table[_CGROUP_MEMORY_ACCOUNTING_METRIC_MAX] = {
|
||||
[CGROUP_MEMORY_PEAK] = "MemoryPeak",
|
||||
[CGROUP_MEMORY_SWAP_CURRENT] = "MemorySwapCurrent",
|
||||
[CGROUP_MEMORY_SWAP_PEAK] = "MemorySwapPeak",
|
||||
[CGROUP_MEMORY_ZSWAP_CURRENT] = "MemoryZSwapCurrent",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(cgroup_memory_accounting_metric, CGroupMemoryAccountingMetric);
|
||||
|
||||
+20
-5
@@ -261,6 +261,21 @@ typedef enum CGroupIOAccountingMetric {
|
||||
_CGROUP_IO_ACCOUNTING_METRIC_INVALID = -EINVAL,
|
||||
} CGroupIOAccountingMetric;
|
||||
|
||||
typedef enum CGroupMemoryAccountingMetric {
|
||||
CGROUP_MEMORY_PEAK,
|
||||
CGROUP_MEMORY_SWAP_PEAK,
|
||||
/* We cache the above attributes, so that they can be fetched even after the cgroup is gone, e.g.
|
||||
* when systemd-run exits. */
|
||||
_CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST = CGROUP_MEMORY_SWAP_PEAK,
|
||||
|
||||
/* These attributes are transient, so no need for caching. */
|
||||
CGROUP_MEMORY_SWAP_CURRENT,
|
||||
CGROUP_MEMORY_ZSWAP_CURRENT,
|
||||
|
||||
_CGROUP_MEMORY_ACCOUNTING_METRIC_MAX,
|
||||
_CGROUP_MEMORY_ACCOUNTING_METRIC_INVALID = -EINVAL,
|
||||
} CGroupMemoryAccountingMetric;
|
||||
|
||||
typedef struct Unit Unit;
|
||||
typedef struct Manager Manager;
|
||||
typedef enum ManagerState ManagerState;
|
||||
@@ -352,12 +367,9 @@ int unit_watch_all_pids(Unit *u);
|
||||
|
||||
int unit_synthesize_cgroup_empty_event(Unit *u);
|
||||
|
||||
int unit_get_memory_current(Unit *u, uint64_t *ret);
|
||||
int unit_get_memory_peak(Unit *u, uint64_t *ret);
|
||||
int unit_get_memory_swap_current(Unit *u, uint64_t *ret);
|
||||
int unit_get_memory_swap_peak(Unit *u, uint64_t *ret);
|
||||
int unit_get_memory_zswap_current(Unit *u, uint64_t *ret);
|
||||
int unit_get_memory_available(Unit *u, uint64_t *ret);
|
||||
int unit_get_memory_current(Unit *u, uint64_t *ret);
|
||||
int unit_get_memory_accounting(Unit *u, CGroupMemoryAccountingMetric metric, uint64_t *ret);
|
||||
int unit_get_tasks_current(Unit *u, uint64_t *ret);
|
||||
int unit_get_cpu_usage(Unit *u, nsec_t *ret);
|
||||
int unit_get_io_accounting(Unit *u, CGroupIOAccountingMetric metric, bool allow_cache, uint64_t *ret);
|
||||
@@ -410,3 +422,6 @@ CGroupIPAccountingMetric cgroup_ip_accounting_metric_from_string(const char *s)
|
||||
|
||||
const char* cgroup_io_accounting_metric_to_string(CGroupIOAccountingMetric m) _const_;
|
||||
CGroupIOAccountingMetric cgroup_io_accounting_metric_from_string(const char *s) _pure_;
|
||||
|
||||
const char* cgroup_memory_accounting_metric_to_string(CGroupMemoryAccountingMetric m) _const_;
|
||||
CGroupMemoryAccountingMetric cgroup_memory_accounting_metric_from_string(const char *s) _pure_;
|
||||
|
||||
+25
-96
@@ -1080,98 +1080,6 @@ static int property_get_current_memory(
|
||||
return sd_bus_message_append(reply, "t", sz);
|
||||
}
|
||||
|
||||
static int property_get_peak_memory(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
uint64_t sz = UINT64_MAX;
|
||||
Unit *u = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(reply);
|
||||
|
||||
r = unit_get_memory_peak(u, &sz);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
log_unit_warning_errno(u, r, "Failed to get memory.peak attribute: %m");
|
||||
|
||||
return sd_bus_message_append(reply, "t", sz);
|
||||
}
|
||||
|
||||
static int property_get_current_swap_memory(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
uint64_t sz = UINT64_MAX;
|
||||
Unit *u = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(reply);
|
||||
|
||||
r = unit_get_memory_swap_current(u, &sz);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
log_unit_warning_errno(u, r, "Failed to get memory.swap.current attribute: %m");
|
||||
|
||||
return sd_bus_message_append(reply, "t", sz);
|
||||
}
|
||||
|
||||
static int property_get_peak_swap_memory(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
uint64_t sz = UINT64_MAX;
|
||||
Unit *u = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(reply);
|
||||
|
||||
r = unit_get_memory_swap_peak(u, &sz);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
log_unit_warning_errno(u, r, "Failed to get memory.swap.peak attribute: %m");
|
||||
|
||||
return sd_bus_message_append(reply, "t", sz);
|
||||
}
|
||||
|
||||
static int property_get_current_zswap_memory(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
uint64_t sz = UINT64_MAX;
|
||||
Unit *u = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(reply);
|
||||
|
||||
r = unit_get_memory_swap_current(u, &sz);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
log_unit_warning_errno(u, r, "Failed to get memory.zswap.current attribute: %m");
|
||||
|
||||
return sd_bus_message_append(reply, "t", sz);
|
||||
}
|
||||
|
||||
static int property_get_available_memory(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
@@ -1195,6 +1103,27 @@ static int property_get_available_memory(
|
||||
return sd_bus_message_append(reply, "t", sz);
|
||||
}
|
||||
|
||||
static int property_get_memory_accounting(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
Unit *u = ASSERT_PTR(userdata);
|
||||
CGroupMemoryAccountingMetric metric;
|
||||
uint64_t sz = UINT64_MAX;
|
||||
|
||||
assert(bus);
|
||||
assert(reply);
|
||||
|
||||
assert_se((metric = cgroup_memory_accounting_metric_from_string(property)) >= 0);
|
||||
(void) unit_get_memory_accounting(u, metric, &sz);
|
||||
return sd_bus_message_append(reply, "t", sz);
|
||||
}
|
||||
|
||||
static int property_get_current_tasks(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
@@ -1628,10 +1557,10 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = {
|
||||
SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0),
|
||||
SD_BUS_PROPERTY("ControlGroupId", "t", NULL, offsetof(Unit, cgroup_id), 0),
|
||||
SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0),
|
||||
SD_BUS_PROPERTY("MemoryPeak", "t", property_get_peak_memory, 0, 0),
|
||||
SD_BUS_PROPERTY("MemorySwapCurrent", "t", property_get_current_swap_memory, 0, 0),
|
||||
SD_BUS_PROPERTY("MemorySwapPeak", "t", property_get_peak_swap_memory, 0, 0),
|
||||
SD_BUS_PROPERTY("MemoryZSwapCurrent", "t", property_get_current_zswap_memory, 0, 0),
|
||||
SD_BUS_PROPERTY("MemoryPeak", "t", property_get_memory_accounting, 0, 0),
|
||||
SD_BUS_PROPERTY("MemorySwapCurrent", "t", property_get_memory_accounting, 0, 0),
|
||||
SD_BUS_PROPERTY("MemorySwapPeak", "t", property_get_memory_accounting, 0, 0),
|
||||
SD_BUS_PROPERTY("MemoryZSwapCurrent", "t", property_get_memory_accounting, 0, 0),
|
||||
SD_BUS_PROPERTY("MemoryAvailable", "t", property_get_available_memory, 0, 0),
|
||||
SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0),
|
||||
SD_BUS_PROPERTY("EffectiveCPUs", "ay", property_get_cpuset_cpus, 0, 0),
|
||||
|
||||
@@ -96,6 +96,13 @@ static const char* const io_accounting_metric_field_last_table[_CGROUP_IO_ACCOUN
|
||||
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(io_accounting_metric_field_last, CGroupIOAccountingMetric);
|
||||
|
||||
static const char* const memory_accounting_metric_field_last_table[_CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST + 1] = {
|
||||
[CGROUP_MEMORY_PEAK] = "memory-accounting-peak",
|
||||
[CGROUP_MEMORY_SWAP_PEAK] = "memory-accounting-swap-peak",
|
||||
};
|
||||
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(memory_accounting_metric_field_last, CGroupMemoryAccountingMetric);
|
||||
|
||||
int unit_serialize_state(Unit *u, FILE *f, FDSet *fds, bool switching_root) {
|
||||
int r;
|
||||
|
||||
@@ -165,6 +172,14 @@ int unit_serialize_state(Unit *u, FILE *f, FDSet *fds, bool switching_root) {
|
||||
(void) serialize_item_format(f, io_accounting_metric_field_last_to_string(im), "%" PRIu64, u->io_accounting_last[im]);
|
||||
}
|
||||
|
||||
for (CGroupMemoryAccountingMetric metric = 0; metric <= _CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST; metric++) {
|
||||
uint64_t v;
|
||||
|
||||
r = unit_get_memory_accounting(u, metric, &v);
|
||||
if (r >= 0)
|
||||
(void) serialize_item_format(f, memory_accounting_metric_field_last_to_string(metric), "%" PRIu64, v);
|
||||
}
|
||||
|
||||
if (u->cgroup_path)
|
||||
(void) serialize_item(f, "cgroup", u->cgroup_path);
|
||||
|
||||
@@ -474,6 +489,18 @@ int unit_deserialize_state(Unit *u, FILE *f, FDSet *fds) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m = memory_accounting_metric_field_last_from_string(l);
|
||||
if (m >= 0) {
|
||||
uint64_t c;
|
||||
|
||||
r = safe_atou64(v, &c);
|
||||
if (r < 0)
|
||||
log_unit_debug(u, "Failed to parse memory accounting last value %s, ignoring.", v);
|
||||
else
|
||||
u->memory_accounting_last[m] = c;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check if this is an IP accounting metric serialization field */
|
||||
m = ip_accounting_metric_field_from_string(l);
|
||||
if (m >= 0) {
|
||||
|
||||
+6
-4
@@ -114,8 +114,10 @@ Unit* unit_new(Manager *m, size_t size) {
|
||||
u->ref_uid = UID_INVALID;
|
||||
u->ref_gid = GID_INVALID;
|
||||
u->cpu_usage_last = NSEC_INFINITY;
|
||||
u->memory_peak_last = UINT64_MAX;
|
||||
u->memory_swap_peak_last = UINT64_MAX;
|
||||
|
||||
FOREACH_ARRAY(i, u->memory_accounting_last, ELEMENTSOF(u->memory_accounting_last))
|
||||
*i = UINT64_MAX;
|
||||
|
||||
u->cgroup_invalidated_mask |= CGROUP_MASK_BPF_FIREWALL;
|
||||
u->failure_action_exit_status = u->success_action_exit_status = -1;
|
||||
|
||||
@@ -2372,7 +2374,7 @@ static int unit_log_resources(Unit *u) {
|
||||
nsec > NOTICEWORTHY_CPU_NSEC);
|
||||
}
|
||||
|
||||
(void) unit_get_memory_peak(u, &memory_peak);
|
||||
(void) unit_get_memory_accounting(u, CGROUP_MEMORY_PEAK, &memory_peak);
|
||||
if (memory_peak != UINT64_MAX) {
|
||||
/* Format peak memory for inclusion in the structured log message */
|
||||
if (asprintf(&t, "MEMORY_PEAK=%" PRIu64, memory_peak) < 0) {
|
||||
@@ -2390,7 +2392,7 @@ static int unit_log_resources(Unit *u) {
|
||||
message_parts[n_message_parts++] = t;
|
||||
}
|
||||
|
||||
(void) unit_get_memory_swap_peak(u, &memory_swap_peak);
|
||||
(void) unit_get_memory_accounting(u, CGROUP_MEMORY_SWAP_PEAK, &memory_swap_peak);
|
||||
if (memory_swap_peak != UINT64_MAX) {
|
||||
/* Format peak swap memory for inclusion in the structured log message */
|
||||
if (asprintf(&t, "MEMORY_SWAP_PEAK=%" PRIu64, memory_swap_peak) < 0) {
|
||||
|
||||
+2
-5
@@ -365,11 +365,8 @@ typedef struct Unit {
|
||||
nsec_t cpu_usage_base;
|
||||
nsec_t cpu_usage_last; /* the most recently read value */
|
||||
|
||||
/* Most recently read value of memory.peak */
|
||||
uint64_t memory_peak_last;
|
||||
|
||||
/* Most recently read value of memory.swap.peak */
|
||||
uint64_t memory_swap_peak_last;
|
||||
/* Most recently read value of memory accounting metrics */
|
||||
uint64_t memory_accounting_last[_CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST + 1];
|
||||
|
||||
/* The current counter of OOM kills initiated by systemd-oomd */
|
||||
uint64_t managed_oom_kill_last;
|
||||
|
||||
Reference in New Issue
Block a user