mirror of
https://github.com/troglobit/finit.git
synced 2026-10-31 13:00:32 +00:00
initctl: resolve hierarchical cgroup limits
Cgroups v2 limits are hierarchical - a process is constrained by the most restrictive limit in its ancestor chain, not just its immediate cgroup. This patch updates cg_conf() to walk up the hierarchy and report effective limits by comparing values at each level. This fixes incorrect "max" (unlimited) reporting in 'initctl --json status', 'initctl cgroup', and 'initctl top' when child cgroups have no explicit limits but parents do. For memory.max and cpu.max: take minimum (most restrictive) For memory.min: take maximum (most protection) Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
2 files changed
+134
-2
No files matched your search
@@ -33,6 +33,8 @@ All relevant changes are documented in this file.
|
|||||||
scroll wheel and Shift+PgUp/PgDn sometimes would not work properly
|
scroll wheel and Shift+PgUp/PgDn sometimes would not work properly
|
||||||
- Fix flickering and artifacts in `initctl top`
|
- Fix flickering and artifacts in `initctl top`
|
||||||
- Fix file descriptor leak in `initctl top`
|
- Fix file descriptor leak in `initctl top`
|
||||||
|
- Fix incorrect "max" limit reporting in `initctl [top|cgroup|--json]` output
|
||||||
|
for child cgroups. Resolves effective limits by walking the cgroup hierarchy
|
||||||
- Ensure mount/unmount skips `noauto` entries
|
- Ensure mount/unmount skips `noauto` entries
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+132
-2
@@ -382,18 +382,148 @@ struct cg *cg_stats(char *path)
|
|||||||
return cg;
|
return cg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* query config */
|
/*
|
||||||
|
* Compare two memory limit values, returning the more restrictive one.
|
||||||
|
* For memory.max: smaller value wins (except "max" means unlimited)
|
||||||
|
* For memory.min: larger value wins (more protection)
|
||||||
|
*/
|
||||||
|
static uint64_t cmp_mem_limit(const char *a, const char *b, int is_max)
|
||||||
|
{
|
||||||
|
uint64_t val_a, val_b;
|
||||||
|
|
||||||
|
if (!a || !a[0])
|
||||||
|
return b && b[0] ? strtoull(b, NULL, 10) : 0;
|
||||||
|
if (!b || !b[0])
|
||||||
|
return strtoull(a, NULL, 10);
|
||||||
|
|
||||||
|
/* Handle "max" (unlimited) */
|
||||||
|
if (!strcmp(a, "max"))
|
||||||
|
return !strcmp(b, "max") ? UINT64_MAX : strtoull(b, NULL, 10);
|
||||||
|
if (!strcmp(b, "max"))
|
||||||
|
return strtoull(a, NULL, 10);
|
||||||
|
|
||||||
|
val_a = strtoull(a, NULL, 10);
|
||||||
|
val_b = strtoull(b, NULL, 10);
|
||||||
|
|
||||||
|
if (is_max)
|
||||||
|
return val_a < val_b ? val_a : val_b; /* min for max limit */
|
||||||
|
else
|
||||||
|
return val_a > val_b ? val_a : val_b; /* max for min limit */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compare two cpu.max values (format: "quota period")
|
||||||
|
* Returns the more restrictive quota (smallest quota/period ratio)
|
||||||
|
* Stores result in dst, up to len bytes
|
||||||
|
*/
|
||||||
|
static void cmp_cpu_max(const char *a, const char *b, char *dst, size_t len)
|
||||||
|
{
|
||||||
|
char a_copy[32], b_copy[32];
|
||||||
|
char *a_quota, *a_period, *b_quota, *b_period;
|
||||||
|
uint64_t qa, pa, qb, pb;
|
||||||
|
double ratio_a, ratio_b;
|
||||||
|
|
||||||
|
if (!a || !a[0]) {
|
||||||
|
if (b && b[0])
|
||||||
|
strlcpy(dst, b, len);
|
||||||
|
else
|
||||||
|
dst[0] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!b || !b[0]) {
|
||||||
|
strlcpy(dst, a, len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse a */
|
||||||
|
strlcpy(a_copy, a, sizeof(a_copy));
|
||||||
|
a_quota = strtok(a_copy, " ");
|
||||||
|
a_period = strtok(NULL, " ");
|
||||||
|
|
||||||
|
/* Parse b */
|
||||||
|
strlcpy(b_copy, b, sizeof(b_copy));
|
||||||
|
b_quota = strtok(b_copy, " ");
|
||||||
|
b_period = strtok(NULL, " ");
|
||||||
|
|
||||||
|
/* Handle "max" quota (unlimited) */
|
||||||
|
if (a_quota && !strcmp(a_quota, "max")) {
|
||||||
|
strlcpy(dst, b, len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (b_quota && !strcmp(b_quota, "max")) {
|
||||||
|
strlcpy(dst, a, len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compare ratios (quota/period) - smaller ratio is more restrictive */
|
||||||
|
if (!a_quota || !a_period || !b_quota || !b_period) {
|
||||||
|
strlcpy(dst, a, len); /* fallback */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qa = strtoull(a_quota, NULL, 10);
|
||||||
|
pa = strtoull(a_period, NULL, 10);
|
||||||
|
qb = strtoull(b_quota, NULL, 10);
|
||||||
|
pb = strtoull(b_period, NULL, 10);
|
||||||
|
|
||||||
|
if (pa == 0 || pb == 0) {
|
||||||
|
strlcpy(dst, a, len); /* fallback */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ratio_a = (double)qa / (double)pa;
|
||||||
|
ratio_b = (double)qb / (double)pb;
|
||||||
|
|
||||||
|
strlcpy(dst, ratio_a <= ratio_b ? a : b, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* query config with hierarchical limit resolution */
|
||||||
struct cg *cg_conf(char *path)
|
struct cg *cg_conf(char *path)
|
||||||
{
|
{
|
||||||
static struct cg cg;
|
static struct cg cg;
|
||||||
|
char parent[512];
|
||||||
|
char tmp[32];
|
||||||
|
uint64_t mem_min_val, mem_max_val;
|
||||||
|
|
||||||
|
/* Read initial values from the leaf cgroup */
|
||||||
cgroup_val(path, "memory.min", cg.cg_mem.min, sizeof(cg.cg_mem.min));
|
cgroup_val(path, "memory.min", cg.cg_mem.min, sizeof(cg.cg_mem.min));
|
||||||
cgroup_memval(path, "memory.max", cg.cg_mem.max, sizeof(cg.cg_mem.max));
|
cgroup_val(path, "memory.max", cg.cg_mem.max, sizeof(cg.cg_mem.max));
|
||||||
cgroup_val(path, "cpu.weight", cg.cg_cpu.weight, sizeof(cg.cg_cpu.weight));
|
cgroup_val(path, "cpu.weight", cg.cg_cpu.weight, sizeof(cg.cg_cpu.weight));
|
||||||
cgroup_val(path, "cpu.max", cg.cg_cpu.max, sizeof(cg.cg_cpu.max));
|
cgroup_val(path, "cpu.max", cg.cg_cpu.max, sizeof(cg.cg_cpu.max));
|
||||||
cgroup_val(path, "cpuset.cpus.effective", cg.cg_cpu.set, sizeof(cg.cg_cpu.set));
|
cgroup_val(path, "cpuset.cpus.effective", cg.cg_cpu.set, sizeof(cg.cg_cpu.set));
|
||||||
cg.cg_vmsize = cgroup_uint64(path, "memory.current");
|
cg.cg_vmsize = cgroup_uint64(path, "memory.current");
|
||||||
|
|
||||||
|
/* Walk up the hierarchy to find most restrictive limits */
|
||||||
|
strlcpy(parent, path, sizeof(parent));
|
||||||
|
while (strcmp(parent, FINIT_CGPATH) != 0) {
|
||||||
|
char *slash = strrchr(parent, '/');
|
||||||
|
if (!slash || slash == parent)
|
||||||
|
break;
|
||||||
|
*slash = '\0'; /* Move up one level */
|
||||||
|
|
||||||
|
/* Compare and update memory.min (take maximum) */
|
||||||
|
if (cgroup_val(parent, "memory.min", tmp, sizeof(tmp))) {
|
||||||
|
mem_min_val = cmp_mem_limit(cg.cg_mem.min, tmp, 0);
|
||||||
|
if (mem_min_val == UINT64_MAX)
|
||||||
|
strlcpy(cg.cg_mem.min, "max", sizeof(cg.cg_mem.min));
|
||||||
|
else
|
||||||
|
snprintf(cg.cg_mem.min, sizeof(cg.cg_mem.min), "%lu", mem_min_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compare and update memory.max (take minimum) */
|
||||||
|
if (cgroup_val(parent, "memory.max", tmp, sizeof(tmp))) {
|
||||||
|
mem_max_val = cmp_mem_limit(cg.cg_mem.max, tmp, 1);
|
||||||
|
if (mem_max_val == UINT64_MAX)
|
||||||
|
strlcpy(cg.cg_mem.max, "max", sizeof(cg.cg_mem.max));
|
||||||
|
else
|
||||||
|
snprintf(cg.cg_mem.max, sizeof(cg.cg_mem.max), "%lu", mem_max_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compare and update cpu.max (take most restrictive) */
|
||||||
|
if (cgroup_val(parent, "cpu.max", tmp, sizeof(tmp)))
|
||||||
|
cmp_cpu_max(cg.cg_cpu.max, tmp, cg.cg_cpu.max, sizeof(cg.cg_cpu.max));
|
||||||
|
}
|
||||||
|
|
||||||
return &cg;
|
return &cg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user