From 0bbff7d6382a489cc753ef2e85122521945d8fdf Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Mar 2019 10:41:32 +0100 Subject: cgroup: get rid of a local variable --- src/core/cgroup.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 6a477adb71..88acb593cd 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -3179,20 +3179,18 @@ int unit_get_ip_accounting( } int unit_reset_cpu_accounting(Unit *u) { - nsec_t ns; int r; assert(u); u->cpu_usage_last = NSEC_INFINITY; - r = unit_get_cpu_usage_raw(u, &ns); + r = unit_get_cpu_usage_raw(u, &u->cpu_usage_base); if (r < 0) { u->cpu_usage_base = 0; return r; } - u->cpu_usage_base = ns; return 0; } -- cgit v1.2.1 From cc6625212f4a2cf41e2fe2cbe64ea71152a377b8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Mar 2019 10:42:19 +0100 Subject: core: no need to initialize ip_accounting twice --- src/core/unit.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/unit.c b/src/core/unit.c index 608a33530c..9dbfdb43d5 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -159,7 +159,6 @@ static void unit_init(Unit *u) { cc->cpu_accounting = u->manager->default_cpu_accounting; cc->io_accounting = u->manager->default_io_accounting; - cc->ip_accounting = u->manager->default_ip_accounting; cc->blockio_accounting = u->manager->default_blockio_accounting; cc->memory_accounting = u->manager->default_memory_accounting; cc->tasks_accounting = u->manager->default_tasks_accounting; -- cgit v1.2.1 From 9b2559a13e5b8ccdf3b429f3c7e82159e2a733b5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Mar 2019 11:25:49 +0100 Subject: core: add new call unit_reset_accounting() It's a simple wrapper for resetting both IP and CPU accounting in one go. This will become particularly useful when we also needs this to reset IO accounting (to be added in a later commit). --- src/core/cgroup.c | 11 +++++++++++ src/core/cgroup.h | 1 + src/core/scope.c | 3 +-- src/core/slice.c | 3 +-- src/core/unit.c | 3 +-- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 88acb593cd..5bf6224e31 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -3210,6 +3210,17 @@ int unit_reset_ip_accounting(Unit *u) { return r < 0 ? r : q; } +int unit_reset_accounting(Unit *u) { + int r, q; + + assert(u); + + r = unit_reset_cpu_accounting(u); + q = unit_reset_ip_accounting(u); + + return r < 0 ? r : q; +} + void unit_invalidate_cgroup(Unit *u, CGroupMask m) { assert(u); diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 050b963579..bfb2fa0a53 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -204,6 +204,7 @@ int unit_get_ip_accounting(Unit *u, CGroupIPAccountingMetric metric, uint64_t *r int unit_reset_cpu_accounting(Unit *u); int unit_reset_ip_accounting(Unit *u); +int unit_reset_accounting(Unit *u); #define UNIT_CGROUP_BOOL(u, name) \ ({ \ diff --git a/src/core/scope.c b/src/core/scope.c index 7f83052258..bb1e60dd11 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -330,8 +330,7 @@ static int scope_start(Unit *u) { return r; (void) unit_realize_cgroup(u); - (void) unit_reset_cpu_accounting(u); - (void) unit_reset_ip_accounting(u); + (void) unit_reset_accounting(u); unit_export_state_files(u); diff --git a/src/core/slice.c b/src/core/slice.c index 15b18bcad3..489d5ace6a 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -230,8 +230,7 @@ static int slice_start(Unit *u) { return r; (void) unit_realize_cgroup(u); - (void) unit_reset_cpu_accounting(u); - (void) unit_reset_ip_accounting(u); + (void) unit_reset_accounting(u); slice_set_state(t, SLICE_ACTIVE); return 1; diff --git a/src/core/unit.c b/src/core/unit.c index 9dbfdb43d5..99b7acbef1 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -5393,8 +5393,7 @@ int unit_prepare_exec(Unit *u) { (void) unit_realize_cgroup(u); if (u->reset_accounting) { - (void) unit_reset_cpu_accounting(u); - (void) unit_reset_ip_accounting(u); + (void) unit_reset_accounting(u); u->reset_accounting = false; } -- cgit v1.2.1 From 83f18c91d095a437392a0b78f1c7631dcbc0ba5d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Mar 2019 11:47:29 +0100 Subject: core: use string_table_lookup() at more places --- src/core/dbus-unit.c | 24 +++++++++++------------- src/core/unit.c | 8 +++----- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 6f1a74d6b5..d34833f4ff 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -19,6 +19,7 @@ #include "selinux-access.h" #include "signal-util.h" #include "special.h" +#include "string-table.h" #include "string-util.h" #include "strv.h" #include "user-util.h" @@ -1029,26 +1030,23 @@ static int property_get_ip_counter( void *userdata, sd_bus_error *error) { - CGroupIPAccountingMetric metric; - uint64_t value = (uint64_t) -1; + static const char *const table[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = { + [CGROUP_IP_INGRESS_BYTES] = "IPIngressBytes", + [CGROUP_IP_EGRESS_BYTES] = "IPEgressBytes", + [CGROUP_IP_INGRESS_PACKETS] = "IPIngressPackets", + [CGROUP_IP_EGRESS_PACKETS] = "IPEgressPackets", + }; + + uint64_t value = UINT64_MAX; Unit *u = userdata; + ssize_t metric; assert(bus); assert(reply); assert(property); assert(u); - if (streq(property, "IPIngressBytes")) - metric = CGROUP_IP_INGRESS_BYTES; - else if (streq(property, "IPIngressPackets")) - metric = CGROUP_IP_INGRESS_PACKETS; - else if (streq(property, "IPEgressBytes")) - metric = CGROUP_IP_EGRESS_BYTES; - else { - assert(streq(property, "IPEgressPackets")); - metric = CGROUP_IP_EGRESS_PACKETS; - } - + assert_se((metric = string_table_lookup(table, ELEMENTSOF(table), property)) >= 0); (void) unit_get_ip_accounting(u, metric, &value); return sd_bus_message_append(reply, "t", value); } diff --git a/src/core/unit.c b/src/core/unit.c index 99b7acbef1..bbc27243a9 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -3323,8 +3323,8 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { for (;;) { _cleanup_free_ char *line = NULL; - CGroupIPAccountingMetric m; char *l, *v; + ssize_t m; size_t k; r = read_line(f, LONG_LINE_MAX, &line); @@ -3576,10 +3576,8 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { } /* Check if this is an IP accounting metric serialization field */ - for (m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) - if (streq(l, ip_accounting_metric_field[m])) - break; - if (m < _CGROUP_IP_ACCOUNTING_METRIC_MAX) { + m = string_table_lookup(ip_accounting_metric_field, ELEMENTSOF(ip_accounting_metric_field), l); + if (m >= 0) { uint64_t c; r = safe_atou64(v, &c); -- cgit v1.2.1 From fbe14fc9a78e03ea4759aa00779d6799609decb6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Mar 2019 12:16:03 +0100 Subject: croup: expose IO accounting data per unit This was the last kind of accounting still not exposed on for each unit. Let's fix that. Note that this is a relatively simplistic approach: we don't expose per-device stats, but sum them all up, much like cgtop does. This kind of metric is probably the most interesting for most usecases, and covers the "systemctl status" output best. If we want per-device stats one day we can of course always add that eventually. --- src/core/cgroup.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/core/cgroup.h | 12 +++++ src/core/dbus-unit.c | 34 ++++++++++++ src/core/unit.c | 48 +++++++++++++++++ src/core/unit.h | 4 ++ 5 files changed, 240 insertions(+), 3 deletions(-) diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 5bf6224e31..2bfa8df0cb 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -3178,6 +3178,127 @@ int unit_get_ip_accounting( return r; } +static int unit_get_io_accounting_raw(Unit *u, uint64_t ret[static _CGROUP_IO_ACCOUNTING_METRIC_MAX]) { + static const char *const field_names[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = { + [CGROUP_IO_READ_BYTES] = "rbytes=", + [CGROUP_IO_WRITE_BYTES] = "wbytes=", + [CGROUP_IO_READ_OPERATIONS] = "rios=", + [CGROUP_IO_WRITE_OPERATIONS] = "wios=", + }; + uint64_t acc[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {}; + _cleanup_free_ char *path = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(u); + + if (!u->cgroup_path) + return -ENODATA; + + if (unit_has_host_root_cgroup(u)) + return -ENODATA; /* TODO: return useful data for the top-level cgroup */ + + r = cg_all_unified(); + if (r < 0) + return r; + if (r == 0) /* TODO: support cgroupv1 */ + return -ENODATA; + + if (!FLAGS_SET(u->cgroup_realized_mask, CGROUP_MASK_IO)) + return -ENODATA; + + r = cg_get_path("io", u->cgroup_path, "io.stat", &path); + if (r < 0) + return r; + + f = fopen(path, "re"); + if (!f) + return -errno; + + for (;;) { + _cleanup_free_ char *line = NULL; + const char *p; + + r = read_line(f, LONG_LINE_MAX, &line); + if (r < 0) + return r; + if (r == 0) + break; + + p = line; + p += strcspn(p, WHITESPACE); /* Skip over device major/minor */ + p += strspn(p, WHITESPACE); /* Skip over following whitespace */ + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_RETAIN_ESCAPE); + if (r < 0) + return r; + if (r == 0) + break; + + for (CGroupIOAccountingMetric i = 0; i < _CGROUP_IO_ACCOUNTING_METRIC_MAX; i++) { + const char *x; + + x = startswith(word, field_names[i]); + if (x) { + uint64_t w; + + r = safe_atou64(x, &w); + if (r < 0) + return r; + + /* Sum up the stats of all devices */ + acc[i] += w; + break; + } + } + } + } + + memcpy(ret, acc, sizeof(acc)); + return 0; +} + +int unit_get_io_accounting( + Unit *u, + CGroupIOAccountingMetric metric, + bool allow_cache, + uint64_t *ret) { + + uint64_t raw[_CGROUP_IO_ACCOUNTING_METRIC_MAX]; + int r; + + /* Retrieve an IO account parameter. This will subtract the counter when the unit was started. */ + + if (!UNIT_CGROUP_BOOL(u, io_accounting)) + return -ENODATA; + + if (allow_cache && u->io_accounting_last[metric] != UINT64_MAX) + goto done; + + r = unit_get_io_accounting_raw(u, raw); + if (r == -ENODATA && u->io_accounting_last[metric] != UINT64_MAX) + goto done; + if (r < 0) + return r; + + for (CGroupIOAccountingMetric i = 0; i < _CGROUP_IO_ACCOUNTING_METRIC_MAX; i++) { + /* Saturated subtraction */ + if (raw[i] > u->io_accounting_base[i]) + u->io_accounting_last[i] = raw[i] - u->io_accounting_base[i]; + else + u->io_accounting_last[i] = 0; + } + +done: + if (ret) + *ret = u->io_accounting_last[metric]; + + return 0; +} + int unit_reset_cpu_accounting(Unit *u) { int r; @@ -3210,15 +3331,33 @@ int unit_reset_ip_accounting(Unit *u) { return r < 0 ? r : q; } +int unit_reset_io_accounting(Unit *u) { + int r; + + assert(u); + + for (CGroupIOAccountingMetric i = 0; i < _CGROUP_IO_ACCOUNTING_METRIC_MAX; i++) + u->io_accounting_last[i] = UINT64_MAX; + + r = unit_get_io_accounting_raw(u, u->io_accounting_base); + if (r < 0) { + zero(u->io_accounting_base); + return r; + } + + return 0; +} + int unit_reset_accounting(Unit *u) { - int r, q; + int r, q, v; assert(u); r = unit_reset_cpu_accounting(u); - q = unit_reset_ip_accounting(u); + q = unit_reset_io_accounting(u); + v = unit_reset_ip_accounting(u); - return r < 0 ? r : q; + return r < 0 ? r : q < 0 ? q : v; } void unit_invalidate_cgroup(Unit *u, CGroupMask m) { diff --git a/src/core/cgroup.h b/src/core/cgroup.h index bfb2fa0a53..ad04be30e9 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -135,6 +135,16 @@ typedef enum CGroupIPAccountingMetric { _CGROUP_IP_ACCOUNTING_METRIC_INVALID = -1, } CGroupIPAccountingMetric; +/* Used when querying IO accounting data */ +typedef enum CGroupIOAccountingMetric { + CGROUP_IO_READ_BYTES, + CGROUP_IO_WRITE_BYTES, + CGROUP_IO_READ_OPERATIONS, + CGROUP_IO_WRITE_OPERATIONS, + _CGROUP_IO_ACCOUNTING_METRIC_MAX, + _CGROUP_IO_ACCOUNTING_METRIC_INVALID = -1, +} CGroupIOAccountingMetric; + typedef struct Unit Unit; typedef struct Manager Manager; @@ -200,10 +210,12 @@ int unit_synthesize_cgroup_empty_event(Unit *u); int unit_get_memory_current(Unit *u, 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); int unit_get_ip_accounting(Unit *u, CGroupIPAccountingMetric metric, uint64_t *ret); int unit_reset_cpu_accounting(Unit *u); int unit_reset_ip_accounting(Unit *u); +int unit_reset_io_accounting(Unit *u); int unit_reset_accounting(Unit *u); #define UNIT_CGROUP_BOOL(u, name) \ diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index d34833f4ff..1d73e0d46d 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -1051,6 +1051,36 @@ static int property_get_ip_counter( return sd_bus_message_append(reply, "t", value); } +static int property_get_io_counter( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + static const char *const table[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = { + [CGROUP_IO_READ_BYTES] = "IOReadBytes", + [CGROUP_IO_WRITE_BYTES] = "IOWriteBytes", + [CGROUP_IO_READ_OPERATIONS] = "IOReadOperations", + [CGROUP_IO_WRITE_OPERATIONS] = "IOWriteOperations", + }; + + uint64_t value = UINT64_MAX; + Unit *u = userdata; + ssize_t metric; + + assert(bus); + assert(reply); + assert(property); + assert(u); + + assert_se((metric = string_table_lookup(table, ELEMENTSOF(table), property)) >= 0); + (void) unit_get_io_accounting(u, metric, false, &value); + return sd_bus_message_append(reply, "t", value); +} + int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; @@ -1174,6 +1204,10 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = { SD_BUS_PROPERTY("IPIngressPackets", "t", property_get_ip_counter, 0, 0), SD_BUS_PROPERTY("IPEgressBytes", "t", property_get_ip_counter, 0, 0), SD_BUS_PROPERTY("IPEgressPackets", "t", property_get_ip_counter, 0, 0), + SD_BUS_PROPERTY("IOReadBytes", "t", property_get_io_counter, 0, 0), + SD_BUS_PROPERTY("IOReadOperations", "t", property_get_io_counter, 0, 0), + SD_BUS_PROPERTY("IOWriteBytes", "t", property_get_io_counter, 0, 0), + SD_BUS_PROPERTY("IOWriteOperations", "t", property_get_io_counter, 0, 0), SD_BUS_METHOD("GetProcesses", NULL, "a(sus)", bus_unit_method_get_processes, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("AttachProcesses", "sau", NULL, bus_unit_method_attach_processes, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END diff --git a/src/core/unit.c b/src/core/unit.c index bbc27243a9..3225ad68e8 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -113,6 +113,9 @@ Unit *unit_new(Manager *m, size_t size) { RATELIMIT_INIT(u->start_limit, m->default_start_limit_interval, m->default_start_limit_burst); RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16); + for (CGroupIOAccountingMetric i = 0; i < _CGROUP_IO_ACCOUNTING_METRIC_MAX; i++) + u->io_accounting_last[i] = UINT64_MAX; + return u; } @@ -3202,6 +3205,20 @@ static const char *const ip_accounting_metric_field[_CGROUP_IP_ACCOUNTING_METRIC [CGROUP_IP_EGRESS_PACKETS] = "ip-accounting-egress-packets", }; +static const char *const io_accounting_metric_field_base[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = { + [CGROUP_IO_READ_BYTES] = "io-accounting-read-bytes-base", + [CGROUP_IO_WRITE_BYTES] = "io-accounting-write-bytes-base", + [CGROUP_IO_READ_OPERATIONS] = "io-accounting-read-operations-base", + [CGROUP_IO_WRITE_OPERATIONS] = "io-accounting-write-operations-base", +}; + +static const char *const io_accounting_metric_field_last[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = { + [CGROUP_IO_READ_BYTES] = "io-accounting-read-bytes-last", + [CGROUP_IO_WRITE_BYTES] = "io-accounting-write-bytes-last", + [CGROUP_IO_READ_OPERATIONS] = "io-accounting-read-operations-last", + [CGROUP_IO_WRITE_OPERATIONS] = "io-accounting-write-operations-last", +}; + int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { CGroupIPAccountingMetric m; int r; @@ -3248,6 +3265,13 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { if (u->oom_kill_last > 0) (void) serialize_item_format(f, "oom-kill-last", "%" PRIu64, u->oom_kill_last); + for (CGroupIOAccountingMetric im = 0; im < _CGROUP_IO_ACCOUNTING_METRIC_MAX; im++) { + (void) serialize_item_format(f, io_accounting_metric_field_base[im], "%" PRIu64, u->io_accounting_base[im]); + + if (u->io_accounting_last[im] != UINT64_MAX) + (void) serialize_item_format(f, io_accounting_metric_field_last[im], "%" PRIu64, u->io_accounting_last[im]); + } + if (u->cgroup_path) (void) serialize_item(f, "cgroup", u->cgroup_path); @@ -3588,6 +3612,30 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { continue; } + m = string_table_lookup(io_accounting_metric_field_base, ELEMENTSOF(io_accounting_metric_field_base), l); + if (m >= 0) { + uint64_t c; + + r = safe_atou64(v, &c); + if (r < 0) + log_unit_debug(u, "Failed to parse IO accounting base value %s, ignoring.", v); + else + u->io_accounting_base[m] = c; + continue; + } + + m = string_table_lookup(io_accounting_metric_field_last, ELEMENTSOF(io_accounting_metric_field_last), l); + if (m >= 0) { + uint64_t c; + + r = safe_atou64(v, &c); + if (r < 0) + log_unit_debug(u, "Failed to parse IO accounting last value %s, ignoring.", v); + else + u->io_accounting_last[m] = c; + continue; + } + if (unit_can_serialize(u)) { r = exec_runtime_deserialize_compat(u, l, v, fds); if (r < 0) { diff --git a/src/core/unit.h b/src/core/unit.h index f730f6eee4..150ca7be72 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -252,6 +252,10 @@ typedef struct Unit { /* 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 */ + uint64_t io_accounting_base[_CGROUP_IO_ACCOUNTING_METRIC_MAX]; + uint64_t io_accounting_last[_CGROUP_IO_ACCOUNTING_METRIC_MAX]; /* the most recently read value */ + /* Counterparts in the cgroup filesystem */ char *cgroup_path; CGroupMask cgroup_realized_mask; /* In which hierarchies does this unit's cgroup exist? (only relevant on cgroup v1) */ -- cgit v1.2.1 From bc40a20ebefc051ac4e41e3f107c2c6d5615e14c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Mar 2019 12:18:00 +0100 Subject: core: include IO data in per-unit resource log msg --- src/core/unit.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 4 deletions(-) diff --git a/src/core/unit.c b/src/core/unit.c index 3225ad68e8..35c4c12575 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -2124,11 +2124,11 @@ void unit_trigger_notify(Unit *u) { } static int unit_log_resources(Unit *u) { - struct iovec iovec[1 + _CGROUP_IP_ACCOUNTING_METRIC_MAX + 4]; - bool any_traffic = false, have_ip_accounting = false; - _cleanup_free_ char *igress = NULL, *egress = NULL; + struct iovec iovec[1 + _CGROUP_IP_ACCOUNTING_METRIC_MAX + _CGROUP_IO_ACCOUNTING_METRIC_MAX + 4]; + bool any_traffic = false, have_ip_accounting = false, any_io = false, have_io_accounting = false; + _cleanup_free_ char *igress = NULL, *egress = NULL, *rr = NULL, *wr = NULL; size_t n_message_parts = 0, n_iovec = 0; - char* message_parts[3 + 1], *t; + char* message_parts[1 + 2 + 2 + 1], *t; nsec_t nsec = NSEC_INFINITY; CGroupIPAccountingMetric m; size_t i; @@ -2139,6 +2139,12 @@ static int unit_log_resources(Unit *u) { [CGROUP_IP_EGRESS_BYTES] = "IP_METRIC_EGRESS_BYTES", [CGROUP_IP_EGRESS_PACKETS] = "IP_METRIC_EGRESS_PACKETS", }; + const char* const io_fields[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = { + [CGROUP_IO_READ_BYTES] = "IO_METRIC_READ_BYTES", + [CGROUP_IO_WRITE_BYTES] = "IO_METRIC_WRITE_BYTES", + [CGROUP_IO_READ_OPERATIONS] = "IO_METRIC_READ_OPERATIONS", + [CGROUP_IO_WRITE_OPERATIONS] = "IO_METRIC_WRITE_OPERATIONS", + }; assert(u); @@ -2168,6 +2174,66 @@ static int unit_log_resources(Unit *u) { message_parts[n_message_parts++] = t; } + for (CGroupIOAccountingMetric k = 0; k < _CGROUP_IO_ACCOUNTING_METRIC_MAX; k++) { + char buf[FORMAT_BYTES_MAX] = ""; + uint64_t value = UINT64_MAX; + + assert(io_fields[k]); + + (void) unit_get_io_accounting(u, k, k > 0, &value); + if (value == UINT64_MAX) + continue; + + have_io_accounting = true; + if (value > 0) + any_io = true; + + /* Format IO accounting data for inclusion in the structured log message */ + if (asprintf(&t, "%s=%" PRIu64, io_fields[k], value) < 0) { + r = log_oom(); + goto finish; + } + iovec[n_iovec++] = IOVEC_MAKE_STRING(t); + + /* Format the IO accounting data for inclusion in the human language message string, but only + * for the bytes counters (and not for the operations counters) */ + if (k == CGROUP_IO_READ_BYTES) { + assert(!rr); + rr = strjoin("read ", format_bytes(buf, sizeof(buf), value), " from disk"); + if (!rr) { + r = log_oom(); + goto finish; + } + } else if (k == CGROUP_IO_WRITE_BYTES) { + assert(!wr); + wr = strjoin("written ", format_bytes(buf, sizeof(buf), value), " to disk"); + if (!wr) { + r = log_oom(); + goto finish; + } + } + } + + if (have_io_accounting) { + if (any_io) { + if (rr) + message_parts[n_message_parts++] = TAKE_PTR(rr); + if (wr) + message_parts[n_message_parts++] = TAKE_PTR(wr); + + } else { + char *k; + + k = strdup("no IO"); + if (!k) { + r = log_oom(); + goto finish; + } + + message_parts[n_message_parts++] = k; + } + } + for (m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) { char buf[FORMAT_BYTES_MAX] = ""; uint64_t value = UINT64_MAX; -- cgit v1.2.1 From 826f7cb15bee4240bc5833433e3bc3829ca2d4e1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Mar 2019 12:18:19 +0100 Subject: run: show IO stats in --wait resource output --- src/run/run.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/run/run.c b/src/run/run.c index 56aa9aaee6..18b46f2a40 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -930,6 +930,8 @@ typedef struct RunContext { uint64_t cpu_usage_nsec; uint64_t ip_ingress_bytes; uint64_t ip_egress_bytes; + uint64_t io_read_bytes; + uint64_t io_write_bytes; uint32_t exit_code; uint32_t exit_status; } RunContext; @@ -975,6 +977,8 @@ static int run_context_update(RunContext *c, const char *path) { { "CPUUsageNSec", "t", NULL, offsetof(RunContext, cpu_usage_nsec) }, { "IPIngressBytes", "t", NULL, offsetof(RunContext, ip_ingress_bytes) }, { "IPEgressBytes", "t", NULL, offsetof(RunContext, ip_egress_bytes) }, + { "IOReadBytes", "t", NULL, offsetof(RunContext, io_read_bytes) }, + { "IOWriteBytes", "t", NULL, offsetof(RunContext, io_write_bytes) }, {} }; @@ -1163,6 +1167,8 @@ static int start_transient_service( .cpu_usage_nsec = NSEC_INFINITY, .ip_ingress_bytes = UINT64_MAX, .ip_egress_bytes = UINT64_MAX, + .io_read_bytes = UINT64_MAX, + .io_write_bytes = UINT64_MAX, .inactive_exit_usec = USEC_INFINITY, .inactive_enter_usec = USEC_INFINITY, }; @@ -1262,6 +1268,14 @@ static int start_transient_service( char bytes[FORMAT_BYTES_MAX]; log_info("IP traffic sent: %s", format_bytes(bytes, sizeof(bytes), c.ip_egress_bytes)); } + if (c.io_read_bytes != UINT64_MAX) { + char bytes[FORMAT_BYTES_MAX]; + log_info("IO bytes read: %s", format_bytes(bytes, sizeof(bytes), c.io_read_bytes)); + } + if (c.io_write_bytes != UINT64_MAX) { + char bytes[FORMAT_BYTES_MAX]; + log_info("IO bytes written: %s", format_bytes(bytes, sizeof(bytes), c.io_write_bytes)); + } } /* Try to propagate the service's return value */ -- cgit v1.2.1 From 7a5de3f9518859aaeb8bcf9fdcea488a04cc57ed Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Mar 2019 12:18:31 +0100 Subject: systemctl: show IO stats in 'status' output --- src/systemctl/systemctl.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 92bbcfd276..cfcc4f6874 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -4125,9 +4125,10 @@ typedef struct UnitStatusInfo { uint64_t cpu_usage_nsec; uint64_t tasks_current; uint64_t tasks_max; - uint64_t ip_ingress_bytes; uint64_t ip_egress_bytes; + uint64_t io_read_bytes; + uint64_t io_write_bytes; LIST_HEAD(ExecStatusInfo, exec); } UnitStatusInfo; @@ -4482,6 +4483,14 @@ static void print_status_info( format_bytes(buf_out, sizeof(buf_out), i->ip_egress_bytes)); } + if (i->io_read_bytes != UINT64_MAX && i->io_write_bytes != UINT64_MAX) { + char buf_in[FORMAT_BYTES_MAX], buf_out[FORMAT_BYTES_MAX]; + + printf(" IO: %s read, %s written\n", + format_bytes(buf_in, sizeof(buf_in), i->io_read_bytes), + format_bytes(buf_out, sizeof(buf_out), i->io_write_bytes)); + } + if (i->tasks_current != (uint64_t) -1) { printf(" Tasks: %" PRIu64, i->tasks_current); @@ -5490,6 +5499,8 @@ static int show_one( { "TasksMax", "t", NULL, offsetof(UnitStatusInfo, tasks_max) }, { "IPIngressBytes", "t", NULL, offsetof(UnitStatusInfo, ip_ingress_bytes) }, { "IPEgressBytes", "t", NULL, offsetof(UnitStatusInfo, ip_egress_bytes) }, + { "IOReadBytes", "t", NULL, offsetof(UnitStatusInfo, io_read_bytes) }, + { "IOWriteBytes", "t", NULL, offsetof(UnitStatusInfo, io_write_bytes) }, { "ExecStartPre", "a(sasbttttuii)", map_exec, 0 }, { "ExecStart", "a(sasbttttuii)", map_exec, 0 }, { "ExecStartPost", "a(sasbttttuii)", map_exec, 0 }, @@ -5514,6 +5525,8 @@ static int show_one( .tasks_max = (uint64_t) -1, .ip_ingress_bytes = (uint64_t) -1, .ip_egress_bytes = (uint64_t) -1, + .io_read_bytes = UINT64_MAX, + .io_write_bytes = UINT64_MAX, }; char **pp; int r; -- cgit v1.2.1