summaryrefslogtreecommitdiffstats
path: root/src/basic/psi-util.c
blob: af8e278bd0615565dfcb9dfe75670541881885fd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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);
}