]> git.ipfire.org Git - thirdparty/libcgroup.git/commitdiff
tools/cgset: add support for cgroup.subtree_control
authorKamalesh Babulal <kamalesh.babulal@oracle.com>
Wed, 11 Oct 2023 11:29:38 +0000 (16:59 +0530)
committerTom Hromatka <tom.hromatka@oracle.com>
Wed, 1 Nov 2023 14:59:13 +0000 (08:59 -0600)
The cgroup.subtree_control settings are special, in comparison to other
controller settings. It can both enable and disable the controllers in
the single argument, depending on the argument the cgroup hierarchy walk
is either pre-order or post-order.

example:
--------
$ sudo cgget -n -v -r cgroup.subtree_control a
cpu memory pids
$ sudo cgget -n -v -r cgroup.subtree_control a/b
cpu memory pids
$ sudo cgget -n -v -r cgroup.subtree_control a/b/c

$
$ sudo cgset -R -r cgroup.subtree_control="-pids +cpuset" a
$ sudo cgget -n -v -r cgroup.subtree_control a
cpuset cpu memory
$ sudo cgget -n -v -r cgroup.subtree_control a/b
cpuset cpu memory
$ sudo cgget -n -v -r cgroup.subtree_control a/b/c

$

Signed-off-by: Kamalesh Babulal <kamalesh.babulal@oracle.com>
Signed-off-by: Tom Hromatka <tom.hromatka@oracle.com>
src/tools/cgset.c

index ecd6cfe00fc0290df1d384f882ad741d484759b6..51c4d1447a2c513a4ba14727b0abd1e8189ae489 100644 (file)
@@ -30,6 +30,9 @@ int flags; /* used input method */
 
 static char *program_name;
 
+/* cgroup.subtree_control -r name, value */
+static struct control_value *cgrp_subtree_ctrl_val;
+
 static struct cgroup *copy_name_value_from_cgroup(char src_cg_path[FILENAME_MAX])
 {
        struct cgroup *src_cgroup;
@@ -89,6 +92,10 @@ static int cgroup_set_cgroup_values(struct cgroup *src_cgrp, const char * const
        struct cgroup *cgrp;
        int ret = ECGFAIL;
 
+       /* subtree_cgrp (src_cgrp) can be empty */
+       if (!src_cgrp)
+               return 0;
+
        cgrp = create_and_copy_cgroup(src_cgrp, new_cgrp);
        if (!cgrp)
                return ret;
@@ -102,16 +109,44 @@ static int cgroup_set_cgroup_values(struct cgroup *src_cgrp, const char * const
        return ret;
 }
 
-static int _cgroup_set_cgroup_values_r(struct cgroup *src_cgrp, const char * const new_cgrp)
+static int is_leaf_node(void **handle)
 {
+       struct cgroup_tree_handle *entry;
+       FTSENT *ent;
+
+       entry = (struct  cgroup_tree_handle *)  *handle;
+       ent = fts_children(entry->fts, 0);
+       if (!ent)
+               return errno;
+
+       while (ent != NULL) {
+               /* return on the first child cgroup */
+               if (ent->fts_info == FTS_D)
+                       return 0;
+
+               ent = ent->fts_link;
+       }
+
+       return 1;
+}
+
+static int _cgroup_set_cgroup_values_r(struct cgroup *src_cgrp, const char * const new_cgrp,
+               bool post_order_walk)
+{
+       struct cgroup_controller *ctrl = NULL;
+       bool subtree_control = false;
        struct cgroup_file_info info;
        int prefix_len;
        void *handle;
        int lvl, ret;
 
 
-       ret = cgroup_walk_tree_begin(src_cgrp->controller[0]->name, new_cgrp, 0, &handle, &info,
-                       &lvl);
+       ctrl = src_cgrp->controller[0];
+
+       if (!strcmp(ctrl->values[0]->name, "cgroup.subtree_control"))
+               subtree_control = true;
+
+       ret = cgroup_walk_tree_begin(ctrl->name, new_cgrp, 0, &handle, &info, &lvl);
        if (ret) {
                err("%s: failed to walk the tree for cgroup %s controller %s\n",
                    program_name, new_cgrp, src_cgrp->controller[0]->name);
@@ -119,20 +154,42 @@ static int _cgroup_set_cgroup_values_r(struct cgroup *src_cgrp, const char * con
                return ret;
        }
 
+       if (post_order_walk) {
+               ret = cgroup_walk_tree_set_flags(&handle, CGROUP_WALK_TYPE_POST_DIR);
+               if (ret) {
+                       err("%s: failed to set CGROUP_WALK_TYPE_POST_DIR flag\n", program_name);
+                       goto err;
+               }
+       }
+
        prefix_len = strlen(info.full_path) - strlen(new_cgrp) - 1;
-       ret = cgroup_set_cgroup_values(src_cgrp, &info.full_path[prefix_len]);
-       if (ret)
-               goto err;
+
+       /* In post order cgroup tree walk, parent should be modify last */
+       if (!post_order_walk) {
+               ret = cgroup_set_cgroup_values(src_cgrp, &info.full_path[prefix_len]);
+               if (ret)
+                       goto err;
+       }
 
        while ((ret = cgroup_walk_tree_next(0, &handle, &info, lvl)) == 0) {
                if (info.type == CGROUP_FILE_TYPE_DIR) {
 
+                       /* skip modify subtree_control file for the leaf nodes */
+                       if (subtree_control && is_leaf_node(&handle))
+                               continue;
+
                        ret = cgroup_set_cgroup_values(src_cgrp, &info.full_path[prefix_len]);
                        if (ret)
                                goto err;
                }
        }
 
+       if (!post_order_walk) {
+               ret = cgroup_set_cgroup_values(src_cgrp, &info.full_path[prefix_len]);
+               if (ret)
+                       goto err;
+       }
+
 err:
        if (ret == ECGEOF)
                ret = 0; /* we successfully walked the tree */
@@ -167,17 +224,86 @@ static int cgroup_copy_controller_idx(struct cgroup *dst_cgrp, struct cgroup *sr
        return 0;
 }
 
-static int cgroup_set_cgroup_values_r(struct cgroup *src_cgrp, const char * const new_cgrp)
+static int cgroup_populate_cgroup_ctrl(const char * const new_cgrp)
 {
+       char *ctrl, *ctrl_list = cgrp_subtree_ctrl_val->value;
+       struct cgroup_controller *cgc;
        struct cgroup *cgrp;
+       int ret = ECGFAIL;
+
+       /* create new cgroup */
+       cgrp = cgroup_new_cgroup(new_cgrp);
+       if (!cgrp) {
+               err("%s: can't add new cgroup: %s\n", program_name, cgroup_strerror(ret));
+               return ret;
+       }
+
+       cgc =  cgroup_add_controller(cgrp, "cgroup");
+       if (!cgc)  {
+               err("%s: failed to add controller cgroup to %s\n", program_name, cgrp->name);
+               goto err;
+       }
+
+       /*
+        * this is need for adding the controller setting, which
+        * will be reset in the loop below.
+        */
+       ret = cgroup_add_value_string(cgc, cgrp_subtree_ctrl_val->name,
+                       cgrp_subtree_ctrl_val->value);
+       if (ret) {
+               err("%s: failed to add value %s to cgroup %s controller cgroup\n", program_name,
+                               cgrp_subtree_ctrl_val->name, cgrp->name);
+               goto err;
+       }
+
+       while ((ctrl = strtok(ctrl_list, " ")) != NULL) {
+               ret = cgroup_set_value_string(cgc, cgrp_subtree_ctrl_val->name, ctrl);
+               if (ret) {
+                       err("%s: failed to set value %s:%s to cgroup %s controller cgroup\n",
+                           program_name, cgrp_subtree_ctrl_val->name, ctrl, cgrp->name);
+                       goto err;
+               }
+
+               if (ctrl[0] == '-')
+                       ret = _cgroup_set_cgroup_values_r(cgrp, new_cgrp, true);
+               else if (ctrl[0] == '+')
+                       ret = _cgroup_set_cgroup_values_r(cgrp, new_cgrp, false);
+               else
+                       ret = ECGFAIL;
+
+               if (ret)
+                       goto err;
+
+               ctrl_list = NULL;
+       }
+
+       ret = 0;
+
+err:
+       cgroup_free(&cgrp);
+       return ret;
+}
+
+static int cgroup_set_cgroup_values_r(struct cgroup *src_cgrp, const char * const new_cgrp)
+{
+       struct cgroup *cgrp = NULL;
        int i, ret = ECGFAIL;
 
+       if (cgrp_subtree_ctrl_val) {
+               ret = cgroup_populate_cgroup_ctrl(new_cgrp);
+               if (ret)
+                       goto err;
+       }
+
        if (is_cgroup_mode_unified()) {
+               if (!src_cgrp->index)
+                       return 0;
+
                cgrp = create_and_copy_cgroup(src_cgrp, new_cgrp);
                if (!cgrp)
                        return ret;
 
-               ret = _cgroup_set_cgroup_values_r(cgrp, new_cgrp);
+               ret = _cgroup_set_cgroup_values_r(cgrp, new_cgrp, false);
                goto err;
        }
 
@@ -207,7 +333,7 @@ static int cgroup_set_cgroup_values_r(struct cgroup *src_cgrp, const char * cons
                if (ret)
                        goto err;
 
-               ret = _cgroup_set_cgroup_values_r(cgrp, new_cgrp);
+               ret = _cgroup_set_cgroup_values_r(cgrp, new_cgrp, false);
                if (ret)
                        goto err;
        }
@@ -297,6 +423,25 @@ err:
 }
 
 #ifndef UNIT_TEST
+static int add_subtree_control_name_value(struct control_value *name_value)
+{
+       if (cgrp_subtree_ctrl_val) {
+               err("%s: duplicate -r %s option found\n", program_name, name_value->name);
+               return ECGFAIL;
+       }
+
+       cgrp_subtree_ctrl_val = calloc(1, sizeof(struct control_value));
+       if (!cgrp_subtree_ctrl_val) {
+               err("%s: not enough memory\n", program_name);
+               return ECGFAIL;
+       }
+
+       snprintf(cgrp_subtree_ctrl_val->name, FILENAME_MAX, "%s", name_value->name);
+       snprintf(cgrp_subtree_ctrl_val->value, CG_CONTROL_VALUE_MAX, "%s", name_value->value);
+
+       return 0;
+}
+
 int main(int argc, char *argv[])
 {
        int ignore_default_systemd_delegate_slice = 0;
@@ -306,6 +451,7 @@ int main(int argc, char *argv[])
        int nv_max = 0;
 
        char src_cg_path[FILENAME_MAX] = "\0";
+       struct cgroup *subtree_cgrp = NULL;
        struct cgroup *src_cgroup = NULL;
 
        int ret = 0;
@@ -319,6 +465,14 @@ int main(int argc, char *argv[])
                exit(EXIT_BADARGS);
        }
 
+       /* initialize libcgroup */
+       ret = cgroup_init();
+       if (ret) {
+               err("%s: libcgroup initialization failed: %s\n", program_name,
+                   cgroup_strerror(ret));
+               goto err;
+       }
+
        /* parse arguments */
 #ifdef WITH_SYSTEMD
        while ((c = getopt_long (argc, argv, "r:hbR", long_options, NULL)) != -1) {
@@ -359,7 +513,14 @@ int main(int argc, char *argv[])
                        if (ret)
                                goto err;
 
-                       nv_number++;
+                       if (!strcmp(name_value[nv_number].name, "cgroup.subtree_control")) {
+                               ret = add_subtree_control_name_value(&name_value[nv_number]);
+                               if (ret)
+                                       goto err;
+                               memset(&name_value[nv_number], '\0', sizeof(struct control_value));
+                       } else {
+                               nv_number++;
+                       }
 
                        break;
                case COPY_FROM_OPTION:
@@ -412,6 +573,13 @@ int main(int argc, char *argv[])
                src_cgroup = create_cgroup_from_name_value_pairs("tmp", name_value, nv_number);
                if (src_cgroup == NULL)
                        goto err;
+
+               if (cgrp_subtree_ctrl_val && !recursive) {
+                       subtree_cgrp = create_cgroup_from_name_value_pairs("tmp1",
+                                                       cgrp_subtree_ctrl_val, 1);
+                       if (!subtree_cgrp)
+                               goto err;
+               }
        }
 
        /* copy the name-value from the given group */
@@ -422,10 +590,14 @@ int main(int argc, char *argv[])
        }
 
        while (optind < argc) {
-               if (recursive)
+               if (recursive) {
                        ret = cgroup_set_cgroup_values_r(src_cgroup, argv[optind]);
-               else
+               } else {
+                       ret = cgroup_set_cgroup_values(subtree_cgrp, argv[optind]);
+                       if (ret)
+                               goto err;
                        ret = cgroup_set_cgroup_values(src_cgroup, argv[optind]);
+               }
                if (ret)
                        goto err;
 
@@ -434,6 +606,7 @@ int main(int argc, char *argv[])
 
 err:
        cgroup_free(&src_cgroup);
+       cgroup_free(&subtree_cgrp);
        free(name_value);
 
        return ret;