]> git.ipfire.org Git - thirdparty/libcgroup.git/commitdiff
wrapper: Add cgroup v2 support to cgroup_add_all_controllers()
authorTom Hromatka <tom.hromatka@oracle.com>
Thu, 2 Mar 2023 23:25:12 +0000 (16:25 -0700)
committerTom Hromatka <tom.hromatka@oracle.com>
Tue, 28 Mar 2023 17:08:35 +0000 (11:08 -0600)
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 <tom.hromatka@oracle.com>
Reviewed-by: Kamalesh Babulal <kamalesh.babulal@oracle.com>
(cherry picked from commit 4124f4d6853a5e12082d277b2320af0adecdbe5a)

src/api.c
src/libcgroup-internal.h
src/wrapper.c

index 7f9e49d9e22c6cc0834dff9f9aec80f205e553c6..43464fe6c7824eb18678db9ddbd84f5b362ea728 100644 (file)
--- 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))
index b53c9c4d5d4a964659d73137a6a690d48fda8dbd..1c423e1d7ba79d8de728bead3af998e845e2047d 100644 (file)
@@ -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;
 
 /*
index 5d8b0023361f06667d2d9d9302ecec20c39f57d0..a20bf361a5f598987ceb397e068ed15a8ea7241c 100644 (file)
@@ -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;
 }