summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnita Zhang <the.anitazha@gmail.com>2020-03-07 17:58:33 -0800
committerAnita Zhang <the.anitazha@gmail.com>2020-10-07 16:17:24 -0700
commit42b83e8e3324e2dc876c537aef2345d1939f10a1 (patch)
treeccaae5c92d7154094d3e44a01b698e7ab0bf5c6e
parent510ca79cf2443bba5089f34e1fec2466cf5312ac (diff)
downloadsystemd-42b83e8e3324e2dc876c537aef2345d1939f10a1.tar.gz
shared: helpers to read pressure stats from cgroups
-rw-r--r--src/shared/meson.build2
-rw-r--r--src/shared/psi-util.c118
-rw-r--r--src/shared/psi-util.h30
-rw-r--r--src/test/meson.build4
-rw-r--r--src/test/test-psi-util.c80
5 files changed, 234 insertions, 0 deletions
diff --git a/src/shared/meson.build b/src/shared/meson.build
index 723e00e437..9ef12959e5 100644
--- a/src/shared/meson.build
+++ b/src/shared/meson.build
@@ -187,6 +187,8 @@ shared_sources = files('''
pkcs11-util.h
pretty-print.c
pretty-print.h
+ psi-util.c
+ psi-util.h
ptyfwd.c
ptyfwd.h
pwquality-util.c
diff --git a/src/shared/psi-util.c b/src/shared/psi-util.c
new file mode 100644
index 0000000000..21e965b04b
--- /dev/null
+++ b/src/shared/psi-util.c
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "extract-word.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "parse-util.h"
+#include "psi-util.h"
+#include "string-util.h"
+#include "stat-util.h"
+#include "strv.h"
+
+int read_resource_pressure(const char *path, PressureType type, ResourcePressure *ret) {
+ _cleanup_free_ char *line = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ unsigned field_filled = 0;
+ ResourcePressure rp = {};
+ const char *t, *cline;
+ char *word;
+ int r;
+
+ assert(path);
+ assert(IN_SET(type, PRESSURE_TYPE_SOME, PRESSURE_TYPE_FULL));
+ assert(ret);
+
+ if (type == PRESSURE_TYPE_SOME)
+ t = "some";
+ else if (type == PRESSURE_TYPE_FULL)
+ t = "full";
+ else
+ return -EINVAL;
+
+ r = fopen_unlocked(path, "re", &f);
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ _cleanup_free_ char *l = NULL;
+ char *w;
+
+ r = read_line(f, LONG_LINE_MAX, &l);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ w = first_word(l, t);
+ if (w) {
+ line = TAKE_PTR(l);
+ cline = w;
+ break;
+ }
+ }
+
+ if (!line)
+ return -ENODATA;
+
+ /* extracts either avgX=Y.Z or total=X */
+ while ((r = extract_first_word(&cline, &word, NULL, 0)) > 0) {
+ _cleanup_free_ char *w = word;
+ const char *v;
+
+ if ((v = startswith(w, "avg10="))) {
+ if (field_filled & (1U << 0))
+ return -EINVAL;
+
+ field_filled |= 1U << 0;
+ r = parse_loadavg_fixed_point(v, &rp.avg10);
+ } else if ((v = startswith(w, "avg60="))) {
+ if (field_filled & (1U << 1))
+ return -EINVAL;
+
+ field_filled |= 1U << 1;
+ r = parse_loadavg_fixed_point(v, &rp.avg60);
+ } else if ((v = startswith(w, "avg300="))) {
+ if (field_filled & (1U << 2))
+ return -EINVAL;
+
+ field_filled |= 1U << 2;
+ r = parse_loadavg_fixed_point(v, &rp.avg300);
+ } else if ((v = startswith(w, "total="))) {
+ if (field_filled & (1U << 3))
+ return -EINVAL;
+
+ field_filled |= 1U << 3;
+ r = safe_atou64(v, &rp.total);
+ } else
+ continue;
+
+ if (r < 0)
+ return r;
+ }
+
+ if (r < 0)
+ return r;
+
+ if (field_filled != 15U)
+ return -EINVAL;
+
+ *ret = rp;
+ return 0;
+}
+
+int is_pressure_supported(void) {
+ const char *p;
+
+ FOREACH_STRING(p, "/proc/pressure/cpu", "/proc/pressure/io", "/proc/pressure/memory")
+ if (access(p, F_OK) < 0) {
+ if (errno == ENOENT)
+ return 0;
+ return -errno;
+ }
+
+ return 1;
+}
diff --git a/src/shared/psi-util.h b/src/shared/psi-util.h
new file mode 100644
index 0000000000..9810dbec6e
--- /dev/null
+++ b/src/shared/psi-util.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdbool.h>
+
+#include "parse-util.h"
+#include "time-util.h"
+
+typedef enum PressureType {
+ PRESSURE_TYPE_SOME,
+ PRESSURE_TYPE_FULL,
+} PressureType;
+
+/* Averages are stored in fixed-point with 11 bit fractions */
+typedef struct ResourcePressure {
+ loadavg_t avg10;
+ loadavg_t avg60;
+ loadavg_t avg300;
+ usec_t total;
+} ResourcePressure;
+
+/** Upstream 4.20+ format
+ *
+ * some avg10=0.22 avg60=0.17 avg300=1.11 total=58761459
+ * full avg10=0.23 avg60=0.16 avg300=1.08 total=58464525
+ */
+int read_resource_pressure(const char *path, PressureType type, ResourcePressure *ret);
+
+/* Was the kernel compiled with CONFIG_PSI=y? 1 if yes, 0 if not, negative on error. */
+int is_pressure_supported(void);
diff --git a/src/test/meson.build b/src/test/meson.build
index 9bb3499963..60dec9512c 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -804,6 +804,10 @@ tests += [
[['src/test/test-local-addresses.c'],
[],
[]],
+
+ [['src/test/test-psi-util.c'],
+ [],
+ []],
]
############################################################
diff --git a/src/test/test-psi-util.c b/src/test/test-psi-util.c
new file mode 100644
index 0000000000..bde8ef80b1
--- /dev/null
+++ b/src/test/test-psi-util.c
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <linux/loadavg.h>
+
+#include "alloc-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "psi-util.h"
+#include "tests.h"
+
+static void test_read_mem_pressure(void) {
+ _cleanup_(unlink_tempfilep) char path[] = "/tmp/pressurereadtestXXXXXX";
+ ResourcePressure rp;
+
+ if (geteuid() != 0)
+ return (void) log_tests_skipped("not root");
+
+ assert_se(mkstemp(path));
+
+ assert_se(read_resource_pressure("/verylikelynonexistentpath", PRESSURE_TYPE_SOME, &rp) < 0);
+ assert_se(read_resource_pressure(path, PRESSURE_TYPE_SOME, &rp) < 0);
+
+ assert_se(write_string_file(path, "herpdederp\n", WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(read_resource_pressure(path, PRESSURE_TYPE_SOME, &rp) < 0);
+
+ /* Pressure file with some invalid values*/
+ assert_se(write_string_file(path, "some avg10=0.22=55 avg60=0.17=8 avg300=1.11=00 total=58761459\n"
+ "full avg10=0.23=55 avg60=0.16=8 avg300=1.08=00 total=58464525", WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(read_resource_pressure(path, PRESSURE_TYPE_SOME, &rp) < 0);
+
+ /* Same pressure valid values as below but with duplicate avg60 field */
+ assert_se(write_string_file(path, "some avg10=0.22 avg60=0.17 avg60=0.18 avg300=1.11 total=58761459\n"
+ "full avg10=0.23 avg60=0.16 avg300=1.08 total=58464525", WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(read_resource_pressure(path, PRESSURE_TYPE_SOME, &rp) < 0);
+
+ assert_se(write_string_file(path, "some avg10=0.22 avg60=0.17 avg300=1.11 total=58761459\n"
+ "full avg10=0.23 avg60=0.16 avg300=1.08 total=58464525", WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(read_resource_pressure(path, PRESSURE_TYPE_SOME, &rp) == 0);
+ assert_se(LOAD_INT(rp.avg10) == 0);
+ assert_se(LOAD_FRAC(rp.avg10) == 22);
+ assert_se(LOAD_INT(rp.avg60) == 0);
+ assert_se(LOAD_FRAC(rp.avg60) == 17);
+ assert_se(LOAD_INT(rp.avg300) == 1);
+ assert_se(LOAD_FRAC(rp.avg300) == 11);
+ assert_se(rp.total == 58761459);
+ assert(read_resource_pressure(path, PRESSURE_TYPE_FULL, &rp) == 0);
+ assert_se(LOAD_INT(rp.avg10) == 0);
+ assert_se(LOAD_FRAC(rp.avg10) == 23);
+ assert_se(LOAD_INT(rp.avg60) == 0);
+ assert_se(LOAD_FRAC(rp.avg60) == 16);
+ assert_se(LOAD_INT(rp.avg300) == 1);
+ assert_se(LOAD_FRAC(rp.avg300) == 8);
+ assert_se(rp.total == 58464525);
+
+ /* Pressure file with extra unsupported fields */
+ assert_se(write_string_file(path, "some avg5=0.55 avg10=0.22 avg60=0.17 avg300=1.11 total=58761459\n"
+ "full avg10=0.23 avg60=0.16 avg300=1.08 avg600=2.00 total=58464525", WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(read_resource_pressure(path, PRESSURE_TYPE_SOME, &rp) == 0);
+ assert_se(LOAD_INT(rp.avg10) == 0);
+ assert_se(LOAD_FRAC(rp.avg10) == 22);
+ assert_se(LOAD_INT(rp.avg60) == 0);
+ assert_se(LOAD_FRAC(rp.avg60) == 17);
+ assert_se(LOAD_INT(rp.avg300) == 1);
+ assert_se(LOAD_FRAC(rp.avg300) == 11);
+ assert_se(rp.total == 58761459);
+ assert(read_resource_pressure(path, PRESSURE_TYPE_FULL, &rp) == 0);
+ assert_se(LOAD_INT(rp.avg10) == 0);
+ assert_se(LOAD_FRAC(rp.avg10) == 23);
+ assert_se(LOAD_INT(rp.avg60) == 0);
+ assert_se(LOAD_FRAC(rp.avg60) == 16);
+ assert_se(LOAD_INT(rp.avg300) == 1);
+ assert_se(LOAD_FRAC(rp.avg300) == 8);
+ assert_se(rp.total == 58464525);
+}
+
+int main(void) {
+ test_setup_logging(LOG_DEBUG);
+ test_read_mem_pressure();
+ return 0;
+}