Merge pull request #8149 from poettering/fake-root-cgroup
Properly synthesize CPU+memory accounting data for the root cgroup
This commit is contained in:
+62
-24
@@ -2030,46 +2030,84 @@ int cg_get_attribute(const char *controller, const char *path, const char *attri
|
||||
return read_one_line_file(p, ret);
|
||||
}
|
||||
|
||||
int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, const char **keys, char **values) {
|
||||
_cleanup_free_ char *filename = NULL, *content = NULL;
|
||||
char *line, *p;
|
||||
int i, r;
|
||||
int cg_get_keyed_attribute(
|
||||
const char *controller,
|
||||
const char *path,
|
||||
const char *attribute,
|
||||
char **keys,
|
||||
char **ret_values) {
|
||||
|
||||
for (i = 0; keys[i]; i++)
|
||||
values[i] = NULL;
|
||||
_cleanup_free_ char *filename = NULL, *contents = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
const char *p;
|
||||
size_t n, i, n_done = 0;
|
||||
char **v;
|
||||
int r;
|
||||
|
||||
/* Reads one or more fields of a cgroupsv2 keyed attribute file. The 'keys' parameter should be an strv with
|
||||
* all keys to retrieve. The 'ret_values' parameter should be passed as string size with the same number of
|
||||
* entries as 'keys'. On success each entry will be set to the value of the matching key.
|
||||
*
|
||||
* If the attribute file doesn't exist at all returns ENOENT, if any key is not found returns ENXIO. */
|
||||
|
||||
r = cg_get_path(controller, path, attribute, &filename);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = read_full_file(filename, &content, NULL);
|
||||
r = read_full_file(filename, &contents, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p = content;
|
||||
while ((line = strsep(&p, "\n"))) {
|
||||
char *key;
|
||||
n = strv_length(keys);
|
||||
if (n == 0) /* No keys to retrieve? That's easy, we are done then */
|
||||
return 0;
|
||||
|
||||
key = strsep(&line, " ");
|
||||
/* Let's build this up in a temporary array for now in order not to clobber the return parameter on failure */
|
||||
v = newa0(char*, n);
|
||||
|
||||
for (i = 0; keys[i]; i++) {
|
||||
if (streq(key, keys[i])) {
|
||||
values[i] = strdup(line);
|
||||
break;
|
||||
for (p = contents; *p;) {
|
||||
const char *w = NULL;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
if (!v[i]) {
|
||||
w = first_word(p, keys[i]);
|
||||
if (w)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (w) {
|
||||
size_t l;
|
||||
|
||||
l = strcspn(w, NEWLINE);
|
||||
v[i] = strndup(w, l);
|
||||
if (!v[i]) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
n_done++;
|
||||
if (n_done >= n)
|
||||
goto done;
|
||||
|
||||
p = w + l;
|
||||
} else
|
||||
p += strcspn(p, NEWLINE);
|
||||
|
||||
p += strspn(p, NEWLINE);
|
||||
}
|
||||
|
||||
for (i = 0; keys[i]; i++) {
|
||||
if (!values[i]) {
|
||||
for (i = 0; keys[i]; i++) {
|
||||
values[i] = mfree(values[i]);
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
r = -ENXIO;
|
||||
|
||||
fail:
|
||||
for (i = 0; i < n; i++)
|
||||
free(v[i]);
|
||||
|
||||
return r;
|
||||
|
||||
done:
|
||||
memcpy(ret_values, v, sizeof(char*) * n);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
|
||||
|
||||
@@ -186,7 +186,7 @@ int cg_create_and_attach(const char *controller, const char *path, pid_t pid);
|
||||
|
||||
int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value);
|
||||
int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret);
|
||||
int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, const char **keys, char **values);
|
||||
int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, char **keys, char **values);
|
||||
|
||||
int cg_set_access(const char *controller, const char *path, uid_t uid, gid_t gid);
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "def.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "parse-util.h"
|
||||
#include "process-util.h"
|
||||
@@ -136,3 +138,131 @@ int procfs_tasks_get_current(uint64_t *ret) {
|
||||
|
||||
return safe_atou64(nr, ret);
|
||||
}
|
||||
|
||||
static uint64_t calc_gcd64(uint64_t a, uint64_t b) {
|
||||
|
||||
while (b > 0) {
|
||||
uint64_t t;
|
||||
|
||||
t = a % b;
|
||||
|
||||
a = b;
|
||||
b = t;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
int procfs_cpu_get_usage(nsec_t *ret) {
|
||||
_cleanup_free_ char *first_line = NULL;
|
||||
unsigned long user_ticks = 0, nice_ticks = 0, system_ticks = 0,
|
||||
irq_ticks = 0, softirq_ticks = 0,
|
||||
guest_ticks = 0, guest_nice_ticks = 0;
|
||||
long ticks_per_second;
|
||||
uint64_t sum, gcd, a, b;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
r = read_one_line_file("/proc/stat", &first_line);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p = first_word(first_line, "cpu");
|
||||
if (!p)
|
||||
return -EINVAL;
|
||||
|
||||
if (sscanf(p, "%lu %lu %lu %*u %*u %lu %lu %*u %lu %lu",
|
||||
&user_ticks,
|
||||
&nice_ticks,
|
||||
&system_ticks,
|
||||
&irq_ticks,
|
||||
&softirq_ticks,
|
||||
&guest_ticks,
|
||||
&guest_nice_ticks) < 5) /* we only insist on the first five fields */
|
||||
return -EINVAL;
|
||||
|
||||
ticks_per_second = sysconf(_SC_CLK_TCK);
|
||||
if (ticks_per_second < 0)
|
||||
return -errno;
|
||||
assert(ticks_per_second > 0);
|
||||
|
||||
sum = (uint64_t) user_ticks + (uint64_t) nice_ticks + (uint64_t) system_ticks +
|
||||
(uint64_t) irq_ticks + (uint64_t) softirq_ticks +
|
||||
(uint64_t) guest_ticks + (uint64_t) guest_nice_ticks;
|
||||
|
||||
/* Let's reduce this fraction before we apply it to avoid overflows when converting this to µsec */
|
||||
gcd = calc_gcd64(NSEC_PER_SEC, ticks_per_second);
|
||||
|
||||
a = (uint64_t) NSEC_PER_SEC / gcd;
|
||||
b = (uint64_t) ticks_per_second / gcd;
|
||||
|
||||
*ret = DIV_ROUND_UP((nsec_t) sum * (nsec_t) a, (nsec_t) b);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int procfs_memory_get_current(uint64_t *ret) {
|
||||
uint64_t mem_total = UINT64_MAX, mem_free = UINT64_MAX;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
f = fopen("/proc/meminfo", "re");
|
||||
if (!f)
|
||||
return -errno;
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
uint64_t *v;
|
||||
char *p, *e;
|
||||
size_t n;
|
||||
|
||||
r = read_line(f, LONG_LINE_MAX, &line);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EINVAL; /* EOF: Couldn't find one or both fields? */
|
||||
|
||||
p = first_word(line, "MemTotal:");
|
||||
if (p)
|
||||
v = &mem_total;
|
||||
else {
|
||||
p = first_word(line, "MemFree:");
|
||||
if (p)
|
||||
v = &mem_free;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Determine length of numeric value */
|
||||
n = strspn(p, DIGITS);
|
||||
if (n == 0)
|
||||
return -EINVAL;
|
||||
e = p + n;
|
||||
|
||||
/* Ensure the line ends in " kB" */
|
||||
n = strspn(e, WHITESPACE);
|
||||
if (n == 0)
|
||||
return -EINVAL;
|
||||
if (!streq(e + n, "kB"))
|
||||
return -EINVAL;
|
||||
|
||||
*e = 0;
|
||||
r = safe_atou64(p, v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (*v == UINT64_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (mem_total != UINT64_MAX && mem_free != UINT64_MAX)
|
||||
break;
|
||||
}
|
||||
|
||||
if (mem_free > mem_total)
|
||||
return -EINVAL;
|
||||
|
||||
*ret = (mem_total - mem_free) * 1024U;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "time-util.h"
|
||||
|
||||
int procfs_tasks_get_limit(uint64_t *ret);
|
||||
int procfs_tasks_set_limit(uint64_t limit);
|
||||
int procfs_tasks_get_current(uint64_t *ret);
|
||||
|
||||
int procfs_cpu_get_usage(nsec_t *ret);
|
||||
|
||||
int procfs_memory_get_current(uint64_t *ret);
|
||||
|
||||
Reference in New Issue
Block a user