diff mbox series

[4/6] API/cgroup: Implement tst_cgroup_load_config()

Message ID 39c482de2c75e393fa53c31d06620995dabc5284.1641376050.git.luke.nowakowskikrijger@canonical.com
State Superseded
Headers show
Series Expand Cgroup shell library | expand

Commit Message

Luke Nowakowski-Krijger Jan. 5, 2022, 10 a.m. UTC
Implement tst_cgroup_load_config which consumes the state given by
tst_cgroup_print_config() to update the internal test state to reflect
the given config.

This allows for programs using the cgroup C API to load and reload
state, allowing functionality such as calling tst_cgroup_require and
tst_cgroup_cleanup to function properly between programs or between
invocations of a binary using the C API.

Signed-off-by: Luke Nowakowski-Krijger <luke.nowakowskikrijger@canonical.com>
---
 include/tst_cgroup.h |   4 +-
 lib/tst_cgroup.c     | 108 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 111 insertions(+), 1 deletion(-)

Comments

Li Wang Jan. 14, 2022, 6:57 a.m. UTC | #1
Luke Nowakowski-Krijger <luke.nowakowskikrijger@canonical.com> wrote:

> +static int parse_root_config(char *config_entry)
> +{
> +       char *key;
> +       char *value;
> +       struct cgroup_root *root;
> +
> +       for (key = strtok(config_entry, " "); key; key = strtok(NULL, " ")) {
> +               if (!(value = strchr(key, '='))) {
> +                       if (!(root = cgroup_find_root(key)))
> +                               tst_brk(TBROK, "Could not find root from config. Roots changing between calls?");
> +
> +                       continue;
> +               }
> +
> +               *value = '\0';
> +               value = value + 1;
> +
> +               if (!strcmp(key, CONFIG_MOUNTROOT_KEY) && !strcmp(value, "yes")) {
> +                       root->we_mounted_it = 1;
> +

> +               } else if (!strcmp(key, CONFIG_LTPDIR_KEY) && !root->ltp_dir.dir_name) {
> +                       cgroup_dir_mk(&root->mnt_dir, cgroup_ltp_dir, &root->ltp_dir);
> +                       if (!strcmp(value, "yes"))
> +                               root->ltp_dir.we_created_it = 1;
> +
> +               } else if (!strcmp(key, CONFIG_DRAINDIR_KEY) && !root->drain_dir.dir_name) {
> +                       cgroup_dir_mk(&root->ltp_dir, cgroup_ltp_drain_dir, &root->drain_dir);
> +                       if (!strcmp(value, "yes"))
> +                               root->ltp_dir.we_created_it = 1;

I think that parsing the  CONFIG_DRAINDIR_KEY from '$_cgroup_state'
is superfluous. Because from the tst_cgroup_cleanup, if
root->ltp_dir.we_created_it
is 1 then both of the two directories will be removed, so just using
CONFIG_LTPDIR_KE
to track the status is enough.

And maybe it is not necessary to print "Created_Drain_Dir=xx" in
tst_cgroup_print_config at all.

Then, the code snippet could be as:

                } else if (!strcmp(key, CONFIG_LTPDIR_KEY) &&
!root->ltp_dir.dir_name) {
                        cgroup_dir_mk(&root->mnt_dir, cgroup_ltp_dir,
&root->ltp_dir);
                        cgroup_dir_mk(&root->ltp_dir,
cgroup_ltp_drain_dir, &root->drain_dir);
                        if (!strcmp(value, "yes"))
                                root->ltp_dir.we_created_it = 1;


> +
> +               } else if (!strcmp(key, CONFIG_TESTID_KEY) && strcmp(value, "NULL") &&
> +                                  !root->test_dir.dir_name) {
> +                       cgroup_dir_mk(&root->ltp_dir, value, &root->test_dir);
> +                       root->test_dir.we_created_it = 1;
> +               }
> +       }
> +
> +       return 0;
> +}




--
Regards,
Li Wang
Luke Nowakowski-Krijger Jan. 14, 2022, 9:32 a.m. UTC | #2
Hi Li,

On Thu, Jan 13, 2022 at 10:57 PM Li Wang <liwang@redhat.com> wrote:

>  Luke Nowakowski-Krijger <luke.nowakowskikrijger@canonical.com> wrote:
>
> > +static int parse_root_config(char *config_entry)
> > +{
> > +       char *key;
> > +       char *value;
> > +       struct cgroup_root *root;
> > +
> > +       for (key = strtok(config_entry, " "); key; key = strtok(NULL, "
> ")) {
> > +               if (!(value = strchr(key, '='))) {
> > +                       if (!(root = cgroup_find_root(key)))
> > +                               tst_brk(TBROK, "Could not find root from
> config. Roots changing between calls?");
> > +
> > +                       continue;
> > +               }
> > +
> > +               *value = '\0';
> > +               value = value + 1;
> > +
> > +               if (!strcmp(key, CONFIG_MOUNTROOT_KEY) && !strcmp(value,
> "yes")) {
> > +                       root->we_mounted_it = 1;
> > +
>
> > +               } else if (!strcmp(key, CONFIG_LTPDIR_KEY) &&
> !root->ltp_dir.dir_name) {
> > +                       cgroup_dir_mk(&root->mnt_dir, cgroup_ltp_dir,
> &root->ltp_dir);
> > +                       if (!strcmp(value, "yes"))
> > +                               root->ltp_dir.we_created_it = 1;
> > +
> > +               } else if (!strcmp(key, CONFIG_DRAINDIR_KEY) &&
> !root->drain_dir.dir_name) {
> > +                       cgroup_dir_mk(&root->ltp_dir,
> cgroup_ltp_drain_dir, &root->drain_dir);
> > +                       if (!strcmp(value, "yes"))
> > +                               root->ltp_dir.we_created_it = 1;
>
> I think that parsing the  CONFIG_DRAINDIR_KEY from '$_cgroup_state'
> is superfluous. Because from the tst_cgroup_cleanup, if
> root->ltp_dir.we_created_it
> is 1 then both of the two directories will be removed, so just using
> CONFIG_LTPDIR_KE
> to track the status is enough.
>
>
This is definitely true and I think it makes it a lot simpler. I will add
this in the next version.

And maybe it is not necessary to print "Created_Drain_Dir=xx" in
> tst_cgroup_print_config at all.
>
> Then, the code snippet could be as:
>
>                 } else if (!strcmp(key, CONFIG_LTPDIR_KEY) &&
> !root->ltp_dir.dir_name) {
>                         cgroup_dir_mk(&root->mnt_dir, cgroup_ltp_dir,
> &root->ltp_dir);
>                         cgroup_dir_mk(&root->ltp_dir,
> cgroup_ltp_drain_dir, &root->drain_dir);
>                         if (!strcmp(value, "yes"))
>                                 root->ltp_dir.we_created_it = 1;
>
>
> > +
> > +               } else if (!strcmp(key, CONFIG_TESTID_KEY) &&
> strcmp(value, "NULL") &&
> > +                                  !root->test_dir.dir_name) {
> > +                       cgroup_dir_mk(&root->ltp_dir, value,
> &root->test_dir);
> > +                       root->test_dir.we_created_it = 1;
> > +               }
> > +       }
> > +
> > +       return 0;
> > +}
>
>
>
>
I'll be posting the changes along with all the modified tests probably
later today or early next week, and I will definitely need help reviewing
that :)


>
> --
> Regards,
> Li Wang
>
>
Thanks as always,
- Luke
diff mbox series

Patch

diff --git a/include/tst_cgroup.h b/include/tst_cgroup.h
index cfcc189ee..9785bf7b9 100644
--- a/include/tst_cgroup.h
+++ b/include/tst_cgroup.h
@@ -112,7 +112,9 @@  struct tst_cgroup_group;
 void tst_cgroup_scan(void);
 /* Print the config detected by tst_cgroup_scan */
 void tst_cgroup_print_config(void);
-
+/* Load the config printed by tst_cgroup_print_config() to update the
+ * the internal state of the test to the given config */
+void tst_cgroup_load_config(const char *const config);
 /* Ensure the specified controller is available in the test's default
  * CGroup, mounting/enabling it if necessary */
 void tst_cgroup_require(const char *const ctrl_name,
diff --git a/lib/tst_cgroup.c b/lib/tst_cgroup.c
index 1cec3e722..9b3c639fc 100644
--- a/lib/tst_cgroup.c
+++ b/lib/tst_cgroup.c
@@ -378,6 +378,114 @@  static struct cgroup_root *cgroup_find_root(const char *const mnt_path)
 	return root;
 }
 
+static int parse_ctrl_config(const char *const config_entry)
+{
+	char *token;
+	struct cgroup_ctrl *ctrl = NULL;
+	int we_require_it = 0;
+
+	if (!strcmp(CONFIG_CTRL_HEADER, config_entry))
+		return 0;
+
+	if (!strcmp(CONFIG_ROOT_HEADER, config_entry))
+		return 1;
+
+	for (token = strtok(config_entry, " "); token; token = strtok(NULL, " ")) {
+		if (!ctrl && !(ctrl = cgroup_find_ctrl(token)))
+			tst_brk(TBROK, "Could not find ctrl from config. Ctrls changing between calls?");
+
+		if (ctrl && !strcmp(token, CONFIG_CTRL_REQUIRED))
+			ctrl->we_require_it = 1;
+	}
+
+	return 0;
+}
+
+static int parse_root_config(char *config_entry)
+{
+	char *key;
+	char *value;
+	struct cgroup_root *root;
+
+	for (key = strtok(config_entry, " "); key; key = strtok(NULL, " ")) {
+		if (!(value = strchr(key, '='))) {
+			if (!(root = cgroup_find_root(key)))
+				tst_brk(TBROK, "Could not find root from config. Roots changing between calls?");
+
+			continue;
+		}
+
+		*value = '\0';
+		value = value + 1;
+
+		if (!strcmp(key, CONFIG_MOUNTROOT_KEY) && !strcmp(value, "yes")) {
+			root->we_mounted_it = 1;
+
+		} else if (!strcmp(key, CONFIG_LTPDIR_KEY) && !root->ltp_dir.dir_name) {
+			cgroup_dir_mk(&root->mnt_dir, cgroup_ltp_dir, &root->ltp_dir);
+			if (!strcmp(value, "yes"))
+				root->ltp_dir.we_created_it = 1;
+
+		} else if (!strcmp(key, CONFIG_DRAINDIR_KEY) && !root->drain_dir.dir_name) {
+			cgroup_dir_mk(&root->ltp_dir, cgroup_ltp_drain_dir, &root->drain_dir);
+			if (!strcmp(value, "yes"))
+				root->ltp_dir.we_created_it = 1;
+
+		} else if (!strcmp(key, CONFIG_TESTID_KEY) && strcmp(value, "NULL") &&
+				   !root->test_dir.dir_name) {
+			cgroup_dir_mk(&root->ltp_dir, value, &root->test_dir);
+			root->test_dir.we_created_it = 1;
+		}
+	}
+
+	return 0;
+}
+
+/* Load the test state configuration provided by tst_cgroup_print_config()
+ *
+ * This will reload some internal tst_cgroup state given by the config
+ * that might otherwise have been lost between calls or between different
+ * processes. In particular this is used by tools/cgroup/tst_cgctl to
+ * provide access to this C api to shell scripts.
+ *
+ * The config keeps track of the minimal state needed for tst_cgroup_cleanup
+ * to cleanup after a test and does not keep track of the creation of
+ * test cgroups that might be created through tst_cgroup_group_mk().
+ */
+void tst_cgroup_load_config(const char *const config)
+{
+	char temp_config[BUFSIZ];
+	char *curr_line;
+	char *next_line;
+
+	if (strlen(config) >= BUFSIZ)
+		tst_brk(TBROK, "Config has exceeded buffer size?");
+
+	strncpy(temp_config, config, BUFSIZ);
+
+	for (curr_line = &temp_config[0]; curr_line; curr_line = next_line + 1) {
+		next_line = strchr(curr_line, '\n');
+		if (next_line)
+			*next_line = '\0';
+
+		if (parse_ctrl_config(curr_line))
+			break;
+	}
+
+	for (curr_line = next_line + 1; curr_line; curr_line = next_line + 1) {
+		next_line = strchr(curr_line, '\n');
+		if (next_line)
+			*next_line = '\0';
+
+		parse_root_config(curr_line);
+		/* Bash will truncate the last newline that we are using to deliminate
+		 * the start and end of valid entries, so if we could not detect any more
+		 * newlines we assume that was our last entry. */
+		if (!next_line)
+			break;
+	}
+}
+
 /* Determine if a mounted cgroup hierarchy is unique and record it if so.
  *
  * For CGroups V2 this is very simple as there is only one