diff options
-rw-r--r-- | src/core/cgroup.c | 145 | ||||
-rw-r--r-- | src/core/cgroup.h | 12 | ||||
-rw-r--r-- | src/core/dbus-unit.c | 34 | ||||
-rw-r--r-- | src/core/unit.c | 48 | ||||
-rw-r--r-- | 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) */ |