summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2019-04-25 11:59:37 +0200
committerGitHub <noreply@github.com>2019-04-25 11:59:37 +0200
commitc5b7ae0edb4e32463b5326c973443a9bc6ae10e3 (patch)
tree6def44557f6affbc9bbe7ffe1116448dc5336810
parent10434dbdfd4e71ccef8b62309a4545c3b88aee29 (diff)
parent7a5de3f9518859aaeb8bcf9fdcea488a04cc57ed (diff)
downloadsystemd-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.c154
-rw-r--r--src/core/cgroup.h13
-rw-r--r--src/core/dbus-unit.c58
-rw-r--r--src/core/scope.c3
-rw-r--r--src/core/slice.c3
-rw-r--r--src/core/unit.c134
-rw-r--r--src/core/unit.h4
-rw-r--r--src/run/run.c14
-rw-r--r--src/systemctl/systemctl.c15
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;