diff options
Diffstat (limited to 'src/shared/psi-util.c')
-rw-r--r-- | src/shared/psi-util.c | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/src/shared/psi-util.c b/src/shared/psi-util.c new file mode 100644 index 0000000..af8e278 --- /dev/null +++ b/src/shared/psi-util.c @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <stdio.h> +#include <unistd.h> + +#include "alloc-util.h" +#include "errno-util.h" +#include "extract-word.h" +#include "fd-util.h" +#include "fileio.h" +#include "missing_threads.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) { + static thread_local int cached = -1; + int r; + + /* The pressure files, both under /proc/ and in cgroups, will exist even if the kernel has PSI + * support disabled; we have to read the file to make sure it doesn't return -EOPNOTSUPP */ + + if (cached >= 0) + return cached; + + FOREACH_STRING(p, "/proc/pressure/cpu", "/proc/pressure/io", "/proc/pressure/memory") { + r = read_virtual_file(p, 0, NULL, NULL); + if (r < 0) { + if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r)) + return (cached = false); + + return r; + } + } + + return (cached = true); +} |