From 5428dea5bf63ce2c4cdec88fc055e525dc379573 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 14 Mar 2021 11:07:13 +0100 Subject: [PATCH] cgroup: refactor, use a unified hierarchy for finit As of now, the default finit cgroup behavior is to use a unified hierarchy of controllers under /sys/fs/cgroup/finit. We mount cpu,cpuacct,cpuset,memory (if available) and gain the ability to control our three major groups: init, system, user. The default CPU share setup is ~10% for init and user, and 90% for system. These are guaranteed CPU shares to ensure we do not starve PID 1 or user processes. Support for configuring these limits will be added in a later commit. Signed-off-by: Joachim Wiberg --- src/cgreaper.sh | 4 +-- src/cgroup.c | 94 ++++++++++++++++++++++++++++++++++++++++++------- src/cgroup.h | 4 +-- src/getty.c | 2 +- src/service.c | 29 +++++++++++++-- 5 files changed, 113 insertions(+), 20 deletions(-) diff --git a/src/cgreaper.sh b/src/cgreaper.sh index 29dbe97d..d6bcf174 100755 --- a/src/cgreaper.sh +++ b/src/cgreaper.sh @@ -2,10 +2,10 @@ # Called by kernel when last child in cgroup exists BASE=/sys/fs/cgroup/finit -PROC=`basename $1` +PROC=$(basename "$1") DIR="$BASE$1" #/libexec/finit/logit -p daemon.info -t finit "$PROC stopped, cleaning up control group $DIR" -rmdir $DIR +rmdir "$DIR" exit 0 diff --git a/src/cgroup.c b/src/cgroup.c index 436871d1..505ae7e7 100644 --- a/src/cgroup.c +++ b/src/cgroup.c @@ -26,19 +26,61 @@ #include #include #include +#include #include "finit.h" #include "log.h" #include "util.h" +static char controllers[256] = "none"; + +static void group_init(char *path, char *nproc, int share) +{ + char file[strlen(path) + 64]; + + paste(file, sizeof(file), path, "cgroup.clone_children"); + echo(file, 0, "1"); + + paste(file, sizeof(file), path, "cpuset.cpus"); + echo(file, 0, nproc); + + paste(file, sizeof(file), path, "cpuset.mems"); + echo(file, 0, "0"); + + echo(FINIT_CGPATH "/init/cpu.shares", 0, "%d", share); +} + +static void append_ctrl(char *ctrl) +{ + char *wanted[] = { + "cpu", + "cpuacct", + "cpuset", + "memory" + }; + size_t i; + + for (i = 0; i < NELEMS(wanted); i++) { + if (strcmp(wanted[i], ctrl)) + continue; + + if (strcmp(controllers, "none")) + strlcat(controllers, ",", sizeof(controllers)); + else + strlcpy(controllers, "", sizeof(controllers)); + + strlcat(controllers, ctrl, sizeof(controllers)); + } +} + /* * Called by Finit at early boot to mount initial cgroups */ void cgroup_init(void) { - FILE *fp; - char buf[80]; int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID; + char buf[80]; + FILE *fp; fp = fopen("/proc/cgroups", "r"); if (!fp) { @@ -53,25 +95,40 @@ void cgroup_init(void) /* Skip first line, header */ (void)fgets(buf, sizeof(buf), fp); + + /* Create and mount traditional cgroups v1 hier */ while (fgets(buf, sizeof(buf), fp)) { char *cgroup; +#if 0 char rc[80]; +#endif cgroup = strtok(buf, "\t "); if (!cgroup) continue; +#if 0 snprintf(rc, sizeof(rc), "/sys/fs/cgroup/%s", cgroup); if (mkdir(rc, 0755) && EEXIST != errno) continue; + if (mount("cgroup", rc, "cgroup", opts, cgroup)) _d("Failed mounting %s cgroup on %s", cgroup, rc); +#else + append_ctrl(cgroup); +#endif } - /* Default cgroups for process monitoring */ + /* Finit default cgroups for process monitoring */ if (mkdir(FINIT_CGPATH, 0755) && EEXIST != errno) goto fail; - if (mount("none", FINIT_CGPATH, "cgroup", opts, "none,name=finit")) { + +#if 0 + strlcpy(controllers, "none,name=finit", sizeof(controllers)); +#else + strlcat(controllers, ",name=finit", sizeof(controllers)); +#endif + if (mount("none", FINIT_CGPATH, "cgroup", opts, controllers)) { _pe("Failed mounting Finit cgroup hierarchy"); goto fail; } @@ -88,6 +145,21 @@ void cgroup_init(void) if (mkdir(FINIT_CGPATH "/user", 0755) && EEXIST != errno) goto fail; + /* + * Set up basic limits for our groups. Finit optimized defaults + * for embedded and server systems include limiting the groups + * init and user to a single CPU with 10% share, the system + * group gets the rest. Override this from finit.conf + */ +#if 0 +#else + snprintf(buf, sizeof(buf), "0-%d", get_nprocs_conf() - 1); + + group_init(FINIT_CGPATH "/init", "0", 50); + group_init(FINIT_CGPATH "/user", "0", 75); + group_init(FINIT_CGPATH "/system", buf, 900); +#endif + /* Move ourselves to init */ echo(FINIT_CGPATH "/init/cgroup.procs", 0, "1"); @@ -95,15 +167,11 @@ fail: fclose(fp); } -static int move_pid(char *group, char *name, char *id, int pid) +static int move_pid(char *group, char *name, int pid) { char path[256]; snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s", group, name); - if (id && strlen(id)) { - strlcat(path, ":", sizeof(path)); - strlcat(path, id, sizeof(path)); - } if (mkdir(path, 0755) && errno != EEXIST) return 1; @@ -112,19 +180,19 @@ static int move_pid(char *group, char *name, char *id, int pid) return echo(path, 0, "%d", pid); } -int cgroup_user(char *name) +int cgroup_user(char *name, int pid) { - return move_pid("finit/user", name, NULL, getpid()); + return move_pid("finit/user", name, pid); } -int cgroup_service(char *nm, char *id, int pid) +int cgroup_service(char *name, int pid) { if (pid <= 0) { errno = EINVAL; return 1; } - return move_pid("finit/system", nm, id, pid); + return move_pid("finit/system", name, pid); } /** diff --git a/src/cgroup.h b/src/cgroup.h index 753bb9ee..2f6f546b 100644 --- a/src/cgroup.h +++ b/src/cgroup.h @@ -26,7 +26,7 @@ void cgroup_init (void); -int cgroup_user (char *name); -int cgroup_service (char *cmd, char *id, int pid); +int cgroup_user (char *name, int pid); +int cgroup_service (char *name, int pid); #endif /* FINIT_CGROUP_H_ */ diff --git a/src/getty.c b/src/getty.c index 020b84d8..5cf064d8 100644 --- a/src/getty.c +++ b/src/getty.c @@ -186,7 +186,7 @@ static int do_login(char *name) { struct stat st; - cgroup_user(name); + cgroup_user(name, getpid()); execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL); /* diff --git a/src/service.c b/src/service.c index 36ad9f05..a03905f7 100644 --- a/src/service.c +++ b/src/service.c @@ -351,6 +351,30 @@ static int is_norespawn(void) fexist("/tmp/norespawn"); } +/* used for process group name, derived from originating filename, + * so to group multiple services, place them in the same .conf + */ +static char *group_name(svc_t *svc, char *buf, size_t len) +{ + char *ptr; + + if (!svc->file[0]) + return svc_ident(svc, buf, len); + + ptr = strrchr(svc->file, '/'); + if (ptr) + ptr++; + else + ptr = svc->file; + + strlcpy(buf, ptr, len); + ptr = strstr(buf, ".conf"); + if (ptr) + *ptr = 0; + + return buf; +} + /** * service_start - Start service * @svc: Service to start @@ -362,6 +386,7 @@ static int service_start(svc_t *svc) { int result = 0, do_progress = 1; sigset_t nmask, omask; + char grnam[80]; pid_t pid; size_t i; @@ -408,8 +433,6 @@ static int service_start(svc_t *svc) sigprocmask(SIG_BLOCK, &nmask, &omask); pid = fork(); - cgroup_service(svc->name, svc->id, pid); - if (pid == 0) { int status; char *home = NULL; @@ -547,6 +570,8 @@ static int service_start(svc_t *svc) _d("Starting %s %s", svc->cmd, buf); } + cgroup_service(group_name(svc, grnam, sizeof(grnam)), pid); + logit(LOG_CONSOLE | LOG_NOTICE, "Starting %s[%d]", svc_ident(svc, NULL, 0), pid); svc->pid = pid;