Add support for configuring cgroups and their settings on services

This patch adds support for modifying settings for the default cgroups;
init, user, and system, as well as adding up to a total of eight groups
for the system.

Services can now be assigned to a cgroup, with optional extra settings
for that particular process group.  The syntax is slightly contrivied
but follows the overall Finit syntax of prop:value,prop':value', e.g.

   cgroup maint cpu.weight:123,mem.max:10000

Starting with the introduction of rlimits, a group of services sharing
the same .conf file can share the same (locally "global") rlimits, and
now also the same cgroup, e.g.

    cgroup.maint
    service foo
    service bar cgroup:mem.max:1000

This puts foo and bar in the same top-level cgroup 'maint', with an
extra memory restriction on bar for max 1000 bytes memory.

NOTE: 'mem.' is a Finit extension, a shorthand for cgroups2 'memory.'

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg committed 2021-03-30 10:21:01 +02:00
1 parent 9e6e653c57
commit 665d212bd6
8 files changed
+282 -28

No files matched your search

+103 -16
View File
@@ -28,6 +28,7 @@
#include <sys/mount.h>
#include <sys/sysinfo.h> /* get_nprocs_conf() */
#include "cgroup.h"
#include "finit.h"
#include "iwatch.h"
#include "log.h"
@@ -39,8 +40,52 @@ static struct iwatch iw_cgroup;
static uev_t cgw;
static void group_init(char *path, int leaf, int weight)
static void cgset(const char *path, char *ctrl, char *prop)
{
char *val;
if (!path || !ctrl) {
_e("Missing path or controller, skipping!");
return;
}
if (!prop) {
prop = strchr(ctrl, '.');
if (!prop) {
_e("Invalid cgroup ctrl syntax: %s", ctrl);
return;
}
*prop++ = 0;
}
val = strchr(prop, ':');
if (!val) {
_e("Missing cgroup ctrl value, prop %s", prop);
return;
}
*val++ = 0;
/* disallow sneaky relative paths */
if (strstr(ctrl, "..") || strstr(prop, "..")) {
_e("Possible security violation; '..' not allowed in cgroup config!");
return;
}
_d("%s/%s.%s <= %s", path, ctrl, prop, val);
if (fnwrite(val, "%s/%s.%s", path, ctrl, prop))
_pe("Failed setting %s/%s.%s = %s", path, ctrl, prop, val);
}
/*
* Settings for a cgroup are on the form: cpu.weight:1234,mem.max:4321,...
* Finit supports the short-form 'mem.', replacing it with 'memory.' when
* writing the setting to the file system.
*/
static void group_init(char *path, int leaf, const char *cfg)
{
char *ptr, *s;
if (mkdir(path, 0755) && EEXIST != errno) {
_pe("Failed creating cgroup %s", path);
return;
@@ -48,14 +93,33 @@ static void group_init(char *path, int leaf, int weight)
/* enable detected controllers on domain groups */
if (!leaf && fnwrite(controllers, "%s/cgroup.subtree_control", path))
_pe("Failed %s for %s", controllers, path);
_pe("Failed enabling %s for %s", controllers, path);
/* set initial group weight */
if (weight && fnwrite(str("%d", weight), "%s/cpu.weight", path))
_pe("Failed setting cpu.weight %d for %s", weight, path);
if (!cfg)
return;
s = strdup(cfg);
if (!s) {
_pe("Failed activating cgroup cfg for %s", path);
return;
}
_d("%s <=> %s", path, s);
ptr = strtok(s, ",");
while (ptr) {
_d("ptr: %s", ptr);
if (!strncmp("mem.", ptr, 4))
cgset(path, "memory", &ptr[4]);
else
cgset(path, ptr, NULL);
ptr = strtok(NULL, ",");
}
free(s);
}
static int cgroup_leaf_init(char *group, char *name, int pid)
static int cgroup_leaf_init(char *group, char *name, int pid, const char *cfg)
{
char path[256];
@@ -66,11 +130,11 @@ static int cgroup_leaf_init(char *group, char *name, int pid)
/* create and initialize new group */
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s", group, name);
group_init(path, 1, 0);
group_init(path, 1, cfg);
/* move process to new group */
if (fnwrite(str("%d", pid), "%s/cgroup.procs", path))
_pe("Failed moving pid %d to %s", pid, path);
_pe("Failed moving pid %d to group %s", pid, path);
strlcat(path, "/cgroup.events", sizeof(path));
@@ -79,12 +143,22 @@ static int cgroup_leaf_init(char *group, char *name, int pid)
int cgroup_user(char *name, int pid)
{
return cgroup_leaf_init("user", name, pid);
return cgroup_leaf_init("user", name, pid, NULL);
}
int cgroup_service(char *name, int pid)
int cgroup_service(char *name, int pid, struct cgroup *cg)
{
return cgroup_leaf_init("system", name, pid);
char *group = "system";
if (cg && cg->name[0]) {
char path[256];
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s", cg->name);
if (fisdir(path))
group = cg->name;
}
return cgroup_leaf_init(group, name, pid, cg->cfg);
}
static void append_ctrl(char *ctrl)
@@ -214,14 +288,14 @@ void cgroup_init(uev_ctx_t *ctx)
/* Enable all controllers */
if (fnwrite(controllers, FINIT_CGPATH "/cgroup.subtree_control"))
_pe("Failed %s for %s", controllers, FINIT_CGPATH "/cgroup.subtree_control");
_pe("Failed enabling %s for %s", controllers, FINIT_CGPATH "/cgroup.subtree_control");
/* Default groups, PID 1, services, and user/login processes */
group_init(FINIT_CGPATH "/init", 1, 100);
group_init(FINIT_CGPATH "/user", 0, 100);
group_init(FINIT_CGPATH "/system", 0, 9800);
group_init(FINIT_CGPATH "/init", 1, "cpu.weight:100");
group_init(FINIT_CGPATH "/user", 0, "cpu.weight:100");
group_init(FINIT_CGPATH "/system", 0, "cpu.weight:9800");
/* Move ourselves to init */
/* Move ourselves to init (best effort, otherwise run in 'root' group */
fnwrite("1", FINIT_CGPATH "/init/cgroup.procs");
/* prepare cgroup.events watcher */
@@ -232,6 +306,19 @@ void cgroup_init(uev_ctx_t *ctx)
}
}
/* the top-level init cgroup is a leaf, that's ensured in cgroup_init() */
void cgroup_config(size_t num, struct cgroup cg[])
{
size_t i;
for (i = 0; i < num; i++) {
char path[256];
snprintf(path, sizeof(path), "%s/%s", FINIT_CGPATH, cg[i].name);
group_init(path, 0, cg[i].cfg);
}
}
/**
* Local Variables:
* indent-tabs-mode: t