Merge pull request #8149 from poettering/fake-root-cgroup

Properly synthesize CPU+memory accounting data for the root cgroup
This commit is contained in:
Lennart Poettering
2018-03-01 11:10:24 +01:00
committed by GitHub
13 changed files with 429 additions and 121 deletions
+62 -24
View File
@@ -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) {
+1 -1
View File
@@ -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);
+130
View File
@@ -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;
}
+6
View File
@@ -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);