From 8006ab9047e18062b78d57ad9b7b15eb162227f4 Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Mon, 2 Jun 2025 20:04:19 +0200 Subject: [PATCH] core: adding CGroup runtime for io.systemd.Unit.List --- src/core/varlink-cgroup.c | 259 +++++++++++++++++++++++++++ src/core/varlink-cgroup.h | 1 + src/core/varlink-unit.c | 3 +- src/shared/varlink-io.systemd.Unit.c | 67 ++++++- 4 files changed, 328 insertions(+), 2 deletions(-) diff --git a/src/core/varlink-cgroup.c b/src/core/varlink-cgroup.c index 5d9ed103147..ec401054597 100644 --- a/src/core/varlink-cgroup.c +++ b/src/core/varlink-cgroup.c @@ -8,6 +8,7 @@ #include "json-util.h" #include "in-addr-prefix-util.h" #include "ip-protocol-list.h" +#include "path-util.h" #include "set.h" #include "unit.h" #include "varlink-cgroup.h" @@ -351,3 +352,261 @@ int unit_cgroup_context_build_json(sd_json_variant **ret, const char *name, void /* Others */ SD_JSON_BUILD_PAIR_BOOLEAN("CoredumpReceive", c->coredump_receive)); } + +static int memory_accounting_metric_build_json(sd_json_variant **ret, const char *name, void *userdata) { + Unit *u = ASSERT_PTR(userdata); + CGroupMemoryAccountingMetric metric; + uint64_t value; + int r; + + assert(ret); + assert(name); + + metric = cgroup_memory_accounting_metric_from_string(name); + assert(metric >= 0); + + r = unit_get_memory_accounting(u, metric, &value); + if (r == -ENODATA) + goto empty; + if (r < 0) + return log_debug_errno(r, "Failed to get value for '%s': %m", name); + + if (value == UINT64_MAX) + goto empty; + + return sd_json_variant_new_unsigned(ret, value); + +empty: + *ret = NULL; + return 0; +} + +static int memory_available_build_json(sd_json_variant **ret, const char *name, void *userdata) { + Unit *u = ASSERT_PTR(userdata); + uint64_t value; + int r; + + assert(ret); + assert(name); + + r = unit_get_memory_available(u, &value); + if (r == -ENODATA) + goto empty; + if (r < 0) + return log_debug_errno(r, "Failed to get value of available memory: %m"); + + if (value == UINT64_MAX) + goto empty; + + return sd_json_variant_new_unsigned(ret, value); + +empty: + *ret = NULL; + return 0; +} + +static int effective_limit_build_json(sd_json_variant **ret, const char *name, void *userdata) { + Unit *u = ASSERT_PTR(userdata); + CGroupLimitType type; + uint64_t value; + int r; + + assert(ret); + assert(name); + + type = cgroup_effective_limit_type_from_string(name); + assert(type >= 0); + + r = unit_get_effective_limit(u, type, &value); + if (r < 0) + return log_debug_errno(r, "Failed to get value for '%s': %m", name); + + if (value == UINT64_MAX) { + *ret = NULL; + return 0; + } + + return sd_json_variant_new_unsigned(ret, value); +} + +static int cpu_usage_build_json(sd_json_variant **ret, const char *name, void *userdata) { + Unit *u = ASSERT_PTR(userdata); + nsec_t ns; + int r; + + assert(ret); + assert(name); + + r = unit_get_cpu_usage(u, &ns); + if (r == -ENODATA) + goto empty; + if (r < 0) + return log_debug_errno(r, "Failed to get cpu usage: %m"); + + if (ns == NSEC_INFINITY) + goto empty; + + return sd_json_variant_new_unsigned(ret, ns); + +empty: + *ret = NULL; + return 0; +} + +static int effective_cpuset_build_json(sd_json_variant **ret, const char *name, void *userdata, const char *cpuset_name) { + Unit *u = ASSERT_PTR(userdata); + _cleanup_(cpu_set_reset) CPUSet cpus = {}; + int r; + + assert(ret); + assert(name); + assert(cpuset_name); + + r = unit_get_cpuset(u, &cpus, cpuset_name); + if (r == -ENODATA) { + *ret = NULL; + return 0; + } + if (r < 0) + return log_debug_errno(r, "Failed to get cpu set '%s': %m", cpuset_name); + + return cpu_set_build_json(ret, name, &cpus); +} + +static inline int effective_cpus_build_json(sd_json_variant **ret, const char *name, void *userdata) { + return effective_cpuset_build_json(ret, name, userdata, "cpuset.cpus.effective"); +} + +static inline int effective_mems_build_json(sd_json_variant **ret, const char *name, void *userdata) { + return effective_cpuset_build_json(ret, name, userdata, "cpuset.mems.effective"); +} + +static int tasks_current_build_json(sd_json_variant **ret, const char *name, void *userdata) { + Unit *u = ASSERT_PTR(userdata); + uint64_t cn; + int r; + + assert(ret); + assert(name); + + r = unit_get_tasks_current(u, &cn); + if (r == -ENODATA) + goto empty; + if (r < 0) + return log_debug_errno(r, "Failed to get count of current tasks: %m"); + + if (cn == UINT64_MAX) + goto empty; + + return sd_json_variant_new_unsigned(ret, cn); + +empty: + *ret = NULL; + return 0; +} + +static int get_ip_counter_build_json(sd_json_variant **ret, const char *name, void *userdata) { + Unit *u = ASSERT_PTR(userdata); + CGroupIPAccountingMetric metric; + uint64_t value; + int r; + + assert(ret); + assert(name); + + metric = cgroup_ip_accounting_metric_from_string(name); + assert(metric >= 0); + + r = unit_get_ip_accounting(u, metric, &value); + if (r == -ENODATA) + goto empty; + if (r < 0) + return log_debug_errno(r, "Failed to get value for '%s': %m", name); + + if (value == UINT64_MAX) + goto empty; + + return sd_json_variant_new_unsigned(ret, value); + +empty: + *ret = NULL; + return 0; +} + +static int get_io_counter_build_json(sd_json_variant **ret, const char *name, void *userdata) { + Unit *u = ASSERT_PTR(userdata); + CGroupIOAccountingMetric metric; + uint64_t value; + int r; + + assert(ret); + assert(name); + + metric = cgroup_io_accounting_metric_from_string(name); + assert(metric >= 0); + + r = unit_get_io_accounting(u, metric, &value); + if (r == -ENODATA) + goto empty; + if (r < 0) + return log_debug_errno(r, "Failed to get value for '%s': %m", name); + + if (value == UINT64_MAX) + goto empty; + + return sd_json_variant_new_unsigned(ret, value); + +empty: + *ret = NULL; + return 0; +} + +int unit_cgroup_runtime_build_json(sd_json_variant **ret, const char *name, void *userdata) { + Unit *u = ASSERT_PTR(userdata); + + assert(ret); + assert(name); + + CGroupRuntime *crt = unit_get_cgroup_runtime(u); + if (!crt) { + *ret = NULL; + return 0; + } + + return sd_json_buildo( + ret, + + /* ID */ + JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("ID", crt->cgroup_id), + JSON_BUILD_PAIR_STRING_NON_EMPTY("Path", crt->cgroup_path ? empty_to_root(crt->cgroup_path) : NULL), + JSON_BUILD_PAIR_STRING_NON_EMPTY("Slice", unit_slice_name(u)), + + /* Memory */ + JSON_BUILD_PAIR_CALLBACK_NON_NULL("MemoryCurrent", memory_accounting_metric_build_json, u), + JSON_BUILD_PAIR_CALLBACK_NON_NULL("MemoryPeak", memory_accounting_metric_build_json, u), + JSON_BUILD_PAIR_CALLBACK_NON_NULL("MemorySwapCurrent", memory_accounting_metric_build_json, u), + JSON_BUILD_PAIR_CALLBACK_NON_NULL("MemorySwapPeak", memory_accounting_metric_build_json, u), + JSON_BUILD_PAIR_CALLBACK_NON_NULL("MemoryZSwapCurrent", memory_accounting_metric_build_json, u), + JSON_BUILD_PAIR_CALLBACK_NON_NULL("MemoryAvailable", memory_available_build_json, u), + JSON_BUILD_PAIR_CALLBACK_NON_NULL("EffectiveMemoryMax", effective_limit_build_json, u), + JSON_BUILD_PAIR_CALLBACK_NON_NULL("EffectiveMemoryHigh", effective_limit_build_json, u), + JSON_BUILD_PAIR_CALLBACK_NON_NULL("EffectiveMemoryNodes", effective_mems_build_json, u), + + /* CPU */ + JSON_BUILD_PAIR_CALLBACK_NON_NULL("CPUUsageNSec", cpu_usage_build_json, u), + JSON_BUILD_PAIR_CALLBACK_NON_NULL("EffectiveCPUs", effective_cpus_build_json, u), + JSON_BUILD_PAIR_CALLBACK_NON_NULL("TasksCurrent", tasks_current_build_json, u), + JSON_BUILD_PAIR_CALLBACK_NON_NULL("EffectiveTasksMax", effective_limit_build_json, u), + + /* IP */ + JSON_BUILD_PAIR_CALLBACK_NON_NULL("IPIngressBytes", get_ip_counter_build_json, u), + JSON_BUILD_PAIR_CALLBACK_NON_NULL("IPIngressPackets", get_ip_counter_build_json, u), + JSON_BUILD_PAIR_CALLBACK_NON_NULL("IPEgressBytes", get_ip_counter_build_json, u), + JSON_BUILD_PAIR_CALLBACK_NON_NULL("IPEgressPackets", get_ip_counter_build_json, u), + + /* IO */ + JSON_BUILD_PAIR_CALLBACK_NON_NULL("IOReadBytes", get_io_counter_build_json, u), + JSON_BUILD_PAIR_CALLBACK_NON_NULL("IOReadOperations", get_io_counter_build_json, u), + JSON_BUILD_PAIR_CALLBACK_NON_NULL("IOWriteBytes", get_io_counter_build_json, u), + JSON_BUILD_PAIR_CALLBACK_NON_NULL("IOWriteOperations", get_io_counter_build_json, u)); +} diff --git a/src/core/varlink-cgroup.h b/src/core/varlink-cgroup.h index 37fd07fa945..befd1fdde84 100644 --- a/src/core/varlink-cgroup.h +++ b/src/core/varlink-cgroup.h @@ -4,3 +4,4 @@ #include "forward.h" int unit_cgroup_context_build_json(sd_json_variant **ret, const char *name, void *userdata); +int unit_cgroup_runtime_build_json(sd_json_variant **ret, const char *name, void *userdata); diff --git a/src/core/varlink-unit.c b/src/core/varlink-unit.c index faf4ab4d4e6..467f4225a68 100644 --- a/src/core/varlink-unit.c +++ b/src/core/varlink-unit.c @@ -300,7 +300,8 @@ static int unit_runtime_build_json(sd_json_variant **ret, const char *name, void SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(u->invocation_id), "InvocationID", SD_JSON_BUILD_UUID(u->invocation_id)), JSON_BUILD_PAIR_CALLBACK_NON_NULL("Markers", markers_build_json, &u->markers), JSON_BUILD_PAIR_CALLBACK_NON_NULL("ActivationDetails", activation_details_build_json, u->activation_details), - SD_JSON_BUILD_PAIR_BOOLEAN("DebugInvocation", u->debug_invocation)); + SD_JSON_BUILD_PAIR_BOOLEAN("DebugInvocation", u->debug_invocation), + JSON_BUILD_PAIR_CALLBACK_NON_NULL("CGroup", unit_cgroup_runtime_build_json, u)); } static int list_unit_one(sd_varlink *link, Unit *unit, bool more) { diff --git a/src/shared/varlink-io.systemd.Unit.c b/src/shared/varlink-io.systemd.Unit.c index adea53fb26e..955a7345aad 100644 --- a/src/shared/varlink-io.systemd.Unit.c +++ b/src/shared/varlink-io.systemd.Unit.c @@ -393,6 +393,67 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE( SD_VARLINK_FIELD_COMMENT("Trigger unit name"), SD_VARLINK_DEFINE_FIELD(name, SD_VARLINK_STRING, 0)); +static SD_VARLINK_DEFINE_STRUCT_TYPE( + CGroupRuntime, + + /* ID */ + SD_VARLINK_FIELD_COMMENT("ID of the CGroup"), + SD_VARLINK_DEFINE_FIELD(ID, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Path of the CGroup"), + SD_VARLINK_DEFINE_FIELD(Path, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Slice of the CGroup"), + SD_VARLINK_DEFINE_FIELD(Slice, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + + /* Memory */ + SD_VARLINK_FIELD_COMMENT("The current amount of memory used by the cgroup, in bytes"), + SD_VARLINK_DEFINE_FIELD(MemoryCurrent, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The peak amount of memory used by the cgroup since its creation, in bytes"), + SD_VARLINK_DEFINE_FIELD(MemoryPeak, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The current amount of swap space used by the cgroup, in bytes"), + SD_VARLINK_DEFINE_FIELD(MemorySwapCurrent, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The peak amount of swap space used by the cgroup since its creation, in bytes"), + SD_VARLINK_DEFINE_FIELD(MemorySwapPeak, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The current amount of zswap space used by the cgroup, in bytes"), + SD_VARLINK_DEFINE_FIELD(MemoryZSwapCurrent, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The available memory for the cgroup, in bytes."), + SD_VARLINK_DEFINE_FIELD(MemoryAvailable, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The effective maximum amount of memory allowed for the cgroup, in bytes"), + SD_VARLINK_DEFINE_FIELD(EffectiveMemoryMax, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The effective high watermark for memory usage by the cgroup, in bytes"), + SD_VARLINK_DEFINE_FIELD(EffectiveMemoryHigh, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Memory NUMA nodes that the cgroup is allowed to use"), + SD_VARLINK_DEFINE_FIELD(EffectiveMemoryNodes, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE), + + /* CPU */ + SD_VARLINK_FIELD_COMMENT("The total CPU usage time in nanoseconds (ns) for the cgroup"), + SD_VARLINK_DEFINE_FIELD(CPUUsageNSec, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("NUMA nodes that the cgroup is allowed to use"), + SD_VARLINK_DEFINE_FIELD(EffectiveCPUs, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The current number of tasks (i.e., processes or threads) running within the cgroup"), + SD_VARLINK_DEFINE_FIELD(TasksCurrent, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The maximum number of tasks that the cgroup is allowed to run concurrently"), + SD_VARLINK_DEFINE_FIELD(EffectiveTasksMax, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + + /* IP */ + SD_VARLINK_FIELD_COMMENT("The total number of bytes received by the cgroup's IP stack"), + SD_VARLINK_DEFINE_FIELD(IPIngressBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The total number of incoming packets received by the cgroup's IP stack"), + SD_VARLINK_DEFINE_FIELD(IPIngressPackets, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("iThe total number of bytes sent by the cgroup's IP stack"), + SD_VARLINK_DEFINE_FIELD(IPEgressBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The total number of outgoing packets sent by the cgroup's IP stack"), + SD_VARLINK_DEFINE_FIELD(IPEgressPackets, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + + /* IO */ + SD_VARLINK_FIELD_COMMENT("The total number of bytes read from block devices by the cgroup"), + SD_VARLINK_DEFINE_FIELD(IOReadBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The total number of read operations performed on block devices by the cgroup"), + SD_VARLINK_DEFINE_FIELD(IOReadOperations, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The total number of bytes written to block devices by the cgroup"), + SD_VARLINK_DEFINE_FIELD(IOWriteBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The total number of write operations performed on block devices by the cgroup"), + SD_VARLINK_DEFINE_FIELD(IOWriteOperations, SD_VARLINK_INT, SD_VARLINK_NULLABLE)); + static SD_VARLINK_DEFINE_STRUCT_TYPE( UnitRuntime, SD_VARLINK_FIELD_COMMENT("If not empty, the field contains the name of another unit that this unit follows in state"), @@ -450,7 +511,9 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE( SD_VARLINK_FIELD_COMMENT("Provides details about why a unit was activated"), SD_VARLINK_DEFINE_FIELD_BY_TYPE(ActivationDetails, ActivationDetails, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("When true, logs about this unit will be at debug level regardless of other log level settings"), - SD_VARLINK_DEFINE_FIELD(DebugInvocation, SD_VARLINK_BOOL, 0)); + SD_VARLINK_DEFINE_FIELD(DebugInvocation, SD_VARLINK_BOOL, 0), + SD_VARLINK_FIELD_COMMENT("The cgroup runtime of the unit"), + SD_VARLINK_DEFINE_FIELD_BY_TYPE(CGroup, CGroupRuntime, SD_VARLINK_NULLABLE)); static SD_VARLINK_DEFINE_ERROR(NoSuchUnit); @@ -496,5 +559,7 @@ SD_VARLINK_DEFINE_INTERFACE( &vl_type_CGroupDeviceAllow, SD_VARLINK_SYMBOL_COMMENT("CGroup context of a unit"), &vl_type_CGroupContext, + SD_VARLINK_SYMBOL_COMMENT("CGroup runtime of a unit"), + &vl_type_CGroupRuntime, SD_VARLINK_SYMBOL_COMMENT("No matching unit found"), &vl_error_NoSuchUnit); -- 2.47.3