summaryrefslogtreecommitdiffstats
path: root/src/basic/psi-util.c
blob: 2a43b03d97476ece55f9271d768b19a1f9481f8b (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
/* 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 == -ENOENT || ERRNO_IS_NEG_NOT_SUPPORTED(r))
                        return (cached = false);
                if (r < 0)
                        return r;
        }

        return (cached = true);
}