diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2019-04-25 11:59:37 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-04-25 11:59:37 +0200 |
commit | c5b7ae0edb4e32463b5326c973443a9bc6ae10e3 (patch) | |
tree | 6def44557f6affbc9bbe7ffe1116448dc5336810 | |
parent | 10434dbdfd4e71ccef8b62309a4545c3b88aee29 (diff) | |
parent | 7a5de3f9518859aaeb8bcf9fdcea488a04cc57ed (diff) | |
download | systemd-c5b7ae0edb4e32463b5326c973443a9bc6ae10e3.tar.gz |
Merge pull request #12074 from poettering/io-acct
expose IO stats on the bus and in "systemctl status" and "systemd-run --wait"
-rw-r--r-- | src/core/cgroup.c | 154 | ||||
-rw-r--r-- | src/core/cgroup.h | 13 | ||||
-rw-r--r-- | src/core/dbus-unit.c | 58 | ||||
-rw-r--r-- | src/core/scope.c | 3 | ||||
-rw-r--r-- | src/core/slice.c | 3 | ||||
-rw-r--r-- | src/core/unit.c | 134 | ||||
-rw-r--r-- | src/core/unit.h | 4 | ||||
-rw-r--r-- | src/run/run.c | 14 | ||||
-rw-r--r-- | src/systemctl/systemctl.c | 15 |
9 files changed, 365 insertions, 33 deletions
diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 946fab24c2..e4e0965bfb 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -3233,21 +3233,140 @@ 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) { - 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; } @@ -3267,6 +3386,35 @@ 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, v; + + assert(u); + + r = unit_reset_cpu_accounting(u); + q = unit_reset_io_accounting(u); + v = unit_reset_ip_accounting(u); + + return r < 0 ? r : q < 0 ? q : v; +} + void unit_invalidate_cgroup(Unit *u, CGroupMask m) { assert(u); diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 0cac8ce76b..8fb5481fc0 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -142,6 +142,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; @@ -210,10 +220,13 @@ 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 6f1a74d6b5..1d73e0d46d 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,30 +1030,57 @@ 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); } +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; @@ -1176,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/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 608a33530c..35c4c12575 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; } @@ -159,7 +162,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; @@ -2122,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; @@ -2137,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); @@ -2166,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; @@ -3203,6 +3271,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; @@ -3249,6 +3331,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); @@ -3324,8 +3413,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); @@ -3577,10 +3666,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); @@ -3591,6 +3678,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) { @@ -5394,8 +5505,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; } 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) */ diff --git a/src/run/run.c b/src/run/run.c index 8a98177575..6a0b0d78b9 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -948,6 +948,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; @@ -993,6 +995,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) }, {} }; @@ -1181,6 +1185,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, }; @@ -1280,6 +1286,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 */ diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 25621745b1..dd48533899 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; uint64_t default_memory_min; uint64_t default_memory_low; @@ -4485,6 +4486,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); @@ -5495,6 +5504,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 }, @@ -5519,6 +5530,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; |