From dc3ff3be03ef397a247a9cbe281a6c808f1fecae Mon Sep 17 00:00:00 2001 From: Tom Hromatka Date: Thu, 2 Mar 2023 16:25:12 -0700 Subject: [PATCH] wrapper: Add cgroup v2 support to cgroup_add_all_controllers() Add cgroup v2 support to cgroup_add_all_controllers(). For cgroup v1 (both legacy and hybrid), cgroup_add_all_controllers() reads /proc/cgroups. For cgroup v2, cgroup_add_all_controllers() reads the cgroup's cgroup.controllers file. Signed-off-by: Tom Hromatka Reviewed-by: Kamalesh Babulal (cherry picked from commit 4124f4d6853a5e12082d277b2320af0adecdbe5a) --- src/api.c | 24 +++----- src/libcgroup-internal.h | 12 ++++ src/wrapper.c | 122 +++++++++++++++++++++++++++++---------- 3 files changed, 113 insertions(+), 45 deletions(-) diff --git a/src/api.c b/src/api.c index 7f9e49d9..43464fe6 100644 --- a/src/api.c +++ b/src/api.c @@ -66,13 +66,6 @@ static __thread char errtext[MAXLEN]; /* Task command name length */ #define TASK_COMM_LEN 16 -/* cgroup v2 files */ -#define CGV2_CONTROLLERS_FILE "cgroup.controllers" -#define CGV2_SUBTREE_CTRL_FILE "cgroup.subtree_control" - -/* maximum line length when reading the cgroup.controllers file */ -#define LL_MAX 100 - /* Check if cgroup_init has been called or not. */ static int cgroup_initialized; @@ -86,7 +79,7 @@ static struct cgroup_rule_list trl; static pthread_rwlock_t rl_lock = PTHREAD_RWLOCK_INITIALIZER; /* Cgroup v2 mount path. Null if v2 isn't mounted */ -static char cg_cgroup_v2_mount_path[FILENAME_MAX]; +char cg_cgroup_v2_mount_path[FILENAME_MAX]; /* Namespace */ __thread char *cg_namespace_table[CG_CONTROLLER_MAX]; @@ -1218,7 +1211,8 @@ out: */ STATIC int cgroup_process_v2_mnt(struct mntent *ent, int *mnt_tbl_idx) { - char *ret_c = NULL, line[LL_MAX], *stok_buff = NULL, *controller, *controllers = NULL; + char *ret_c = NULL, line[CGV2_CONTROLLERS_LL_MAX], *stok_buff = NULL; + char *controller = NULL, *controllers = NULL; char cgroup_controllers_path[FILENAME_MAX]; int ret = 0, i, duplicate, shared_mnt; FILE *fp = NULL; @@ -1240,7 +1234,7 @@ STATIC int cgroup_process_v2_mnt(struct mntent *ent, int *mnt_tbl_idx) goto out; } - ret_c = fgets(line, LL_MAX, fp); + ret_c = fgets(line, CGV2_CONTROLLERS_LL_MAX, fp); if (ret_c == NULL) { struct cg_mount_point *tmp, *t; @@ -1460,14 +1454,14 @@ static int cgroup_populate_controllers(char *controllers[CG_CONTROLLER_MAX]) * The first line of the file has stuff we are not interested in. * So just read it and discard the information. */ - buf = malloc(LL_MAX); + buf = malloc(CGV2_CONTROLLERS_LL_MAX); if (!buf) { last_errno = errno; ret = ECGOTHER; goto err; } - if (!fgets(buf, LL_MAX, proc_cgroup)) { + if (!fgets(buf, CGV2_CONTROLLERS_LL_MAX, proc_cgroup)) { cgroup_err("cannot read /proc/cgroups: %s\n", strerror(errno)); last_errno = errno; ret = ECGOTHER; @@ -1838,7 +1832,7 @@ static int cgroup_get_cg_type(const char * const path, char * const type, size_t type_sz) { char cg_type_path[FILENAME_MAX]; - char cg_type[LL_MAX]; + char cg_type[CGV2_CONTROLLERS_LL_MAX]; int len, err = 0; FILE *fp = NULL; @@ -1856,7 +1850,7 @@ static int cgroup_get_cg_type(const char * const path, char * const type, } } - if (fgets(cg_type, LL_MAX, fp) == NULL) { + if (fgets(cg_type, CGV2_CONTROLLERS_LL_MAX, fp) == NULL) { cgroup_warn("failed to read file %s: %s\n", cg_type_path, strerror(errno)); err = ECGOTHER; goto out; @@ -1891,7 +1885,7 @@ int cgroup_build_tasks_procs_path(char * const path, size_t path_sz, const char const char * const ctrl_name) { enum cg_version_t version; - char cg_type[LL_MAX]; + char cg_type[CGV2_CONTROLLERS_LL_MAX]; int err = ECGOTHER; if (!cg_build_path(cg_name, path, ctrl_name)) diff --git a/src/libcgroup-internal.h b/src/libcgroup-internal.h index b53c9c4d..1c423e1d 100644 --- a/src/libcgroup-internal.h +++ b/src/libcgroup-internal.h @@ -70,6 +70,13 @@ extern "C" { #define CGROUP_FILE_PREFIX "cgroup" +/* cgroup v2 files */ +#define CGV2_CONTROLLERS_FILE "cgroup.controllers" +#define CGV2_SUBTREE_CTRL_FILE "cgroup.subtree_control" + +/* maximum line length when reading the cgroup.controllers file */ +#define CGV2_CONTROLLERS_LL_MAX 100 + #define cgroup_err(x...) cgroup_log(CGROUP_LOG_ERROR, "Error: " x) #define cgroup_warn(x...) cgroup_log(CGROUP_LOG_WARNING, "Warning: " x) #define cgroup_info(x...) cgroup_log(CGROUP_LOG_INFO, "Info: " x) @@ -230,8 +237,13 @@ void init_cgroup_table(struct cgroup *cgroups, size_t count); /* * Main mounting structures + * + * cg_mount_table_lock must be held to access: + * cg_mount_table + * cg_cgroup_v2_mount_path */ extern struct cg_mount_table_s cg_mount_table[CG_CONTROLLER_MAX]; +extern char cg_cgroup_v2_mount_path[FILENAME_MAX]; extern pthread_rwlock_t cg_mount_table_lock; /* diff --git a/src/wrapper.c b/src/wrapper.c index 5d8b0023..a20bf361 100644 --- a/src/wrapper.c +++ b/src/wrapper.c @@ -112,47 +112,109 @@ int cgroup_add_all_controllers(struct cgroup *cgroup) { struct cgroup_controller *cgc; struct controller_data info; + enum cg_setup_mode_t mode; void *handle; - int ret; + int ret = 0; - /* go through the controller list */ - ret = cgroup_get_all_controller_begin(&handle, &info); - if ((ret != 0) && (ret != ECGEOF)) { - fprintf(stderr, "cannot read controller data: %s\n", cgroup_strerror(ret)); - return ret; - } + if (!cgroup) + return ECGINVAL; + + mode = cgroup_setup_mode(); + + /* + * Per kernel documentation, cgroup-v2.rst, /proc/cgroups is "meaningless" for cgroup v2. + * Use the cgroup's cgroup.controllers file instead + */ + if (mode == CGROUP_MODE_UNIFIED) { + char *ret_c, *controller, *stok_buff = NULL, line[CGV2_CONTROLLERS_LL_MAX]; + /* + * cg_cgroup_v2_mount_path (FILENAME_MAX) + cgroup->name (FILENAME_MAX) + + * strlen("cgroup.controllers") (18) + 2 forward slashes + 1 NULL terminator + */ + char cgroup_controllers_path[FILENAME_MAX * 2 + 18 + 2 + 1]; + FILE *fp; - while (ret == 0) { - if (info.hierarchy == 0) { - /* - * the controller is not attached to any - * hierarchy skip it. - */ - goto next; + pthread_rwlock_rdlock(&cg_mount_table_lock); + if (strlen(cg_cgroup_v2_mount_path) == 0) { + ret = ECGOTHER; + goto out; } - /* add mounted controller to cgroup structure */ - cgc = cgroup_add_controller(cgroup, info.name); - if (!cgc) { - ret = ECGINVAL; - fprintf(stderr, "controller %s can't be added\n", info.name); - goto end; + snprintf(cgroup_controllers_path, sizeof(cgroup_controllers_path), "%s/%s/%s", + cg_cgroup_v2_mount_path, cgroup->name, CGV2_CONTROLLERS_FILE); + pthread_rwlock_unlock(&cg_mount_table_lock); + + fp = fopen(cgroup_controllers_path, "re"); + if (!fp) { + ret = ECGOTHER; + goto out; + } + + ret_c = fgets(line, CGV2_CONTROLLERS_LL_MAX, fp); + fclose(fp); + if (ret_c == NULL) { + /* no controllers are enabled */ + goto out; + } + + /* Remove the trailing newline */ + ret_c[strlen(ret_c) - 1] = '\0'; + + /* + * cgroup.controllers returns a list of available controllers in + * the following format: + * cpuset cpu io memory pids rdma + */ + controller = strtok_r(ret_c, " ", &stok_buff); + do { + cgc = cgroup_add_controller(cgroup, controller); + if (!cgc) { + ret = ECGINVAL; + fprintf(stderr, "controller %s can't be added\n", info.name); + goto end; + } + } while ((controller = strtok_r(NULL, " ", &stok_buff))); + } else { + /* go through the controller list */ + ret = cgroup_get_all_controller_begin(&handle, &info); + if ((ret != 0) && (ret != ECGEOF)) { + fprintf(stderr, "cannot read controller data: %s\n", cgroup_strerror(ret)); + return ret; } + while (ret == 0) { + if (info.hierarchy == 0) { + /* + * the controller is not attached to any + * hierarchy skip it. + */ + goto next; + } + + /* add mounted controller to cgroup structure */ + cgc = cgroup_add_controller(cgroup, info.name); + if (!cgc) { + ret = ECGINVAL; + fprintf(stderr, "controller %s can't be added\n", info.name); + goto end; + } + next: - ret = cgroup_get_all_controller_next(&handle, &info); - if (ret && ret != ECGEOF) - goto end; - } + ret = cgroup_get_all_controller_next(&handle, &info); + if (ret && ret != ECGEOF) + goto end; + } end: - cgroup_get_all_controller_end(&handle); - if (ret == ECGEOF) - ret = 0; - if (ret) - fprintf(stderr, "cgroup_get_controller_begin/next failed (%s)\n", - cgroup_strerror(ret)); + cgroup_get_all_controller_end(&handle); + if (ret == ECGEOF) + ret = 0; + if (ret) + fprintf(stderr, "cgroup_get_controller_begin/next failed (%s)\n", + cgroup_strerror(ret)); + } +out: return ret; } -- 2.47.2