From: Alexey K. <ale...@or...> - 2013-05-15 09:42:15
|
Support of extended attributes in cgroups was added in Linux 3.7. Signed-off-by: Alexey Kodanev <ale...@or...> --- runtest/controllers | 1 + .../kernel/controllers/cgroup_xattr/.gitignore | 1 + testcases/kernel/controllers/cgroup_xattr/Makefile | 19 ++ testcases/kernel/controllers/cgroup_xattr/README | 25 ++ .../kernel/controllers/cgroup_xattr/cgroup_xattr.c | 343 ++++++++++++++++++++ 5 files changed, 389 insertions(+), 0 deletions(-) create mode 100644 testcases/kernel/controllers/cgroup_xattr/.gitignore create mode 100644 testcases/kernel/controllers/cgroup_xattr/Makefile create mode 100644 testcases/kernel/controllers/cgroup_xattr/README create mode 100644 testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c diff --git a/runtest/controllers b/runtest/controllers index b78d828..94fc185 100644 --- a/runtest/controllers +++ b/runtest/controllers @@ -12,3 +12,4 @@ memcg_stress memcg_stress_test.sh memcg_control PAGESIZE=$(mem_process -p);memcg_control_test.sh $PAGESIZE $PAGESIZE $((PAGESIZE * 2)) cgroup_fj run_cgroup_test_fj.sh controllers test_controllers.sh +cgroup_xattr cgroup_xattr diff --git a/testcases/kernel/controllers/cgroup_xattr/.gitignore b/testcases/kernel/controllers/cgroup_xattr/.gitignore new file mode 100644 index 0000000..3664cc9 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/.gitignore @@ -0,0 +1 @@ +/cgroup_xattr diff --git a/testcases/kernel/controllers/cgroup_xattr/Makefile b/testcases/kernel/controllers/cgroup_xattr/Makefile new file mode 100644 index 0000000..cd49588 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/Makefile @@ -0,0 +1,19 @@ +# Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/controllers/cgroup_xattr/README b/testcases/kernel/controllers/cgroup_xattr/README new file mode 100644 index 0000000..cbec077 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/README @@ -0,0 +1,25 @@ +TEST SUITE: + +The directory cgroup_xattr contains the tests related to extended +attributes in cgroup filesystem. + +WARNING: + +This test can cause a kernel panic due to a bug in kernels prior to 3.8. +It was fixed by kernel upstream commit +712317ad97f41e738e1a19aa0a6392a78a84094e: + +"We should store file xattrs in struct cfent instead of struct cftype, +because cftype is a type while cfent is object instance of cftype." + +TESTS AIM: + +The aim of the tests is to check the extended attributes in cgroup +filesystem. This feature was added in Linux 3.7 to allow attaching runtime +meta information to cgroups and everything they model (services, apps, vms) +and can easily be shared among applications. + +Test creates all available subsystems (cpu, cpuset, memory, ...) in the +cgroup root directory and in one more created hierarchy. Then sets extended +attributes to all files and subsequently reads the file's extended attributes +back, checking values during the process. diff --git a/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c b/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c new file mode 100644 index 0000000..0425ae1 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Alexey Kodanev <ale...@or...> + * + * Test checks following preconditions: + * since Linux kernel 3.7 it is possible to set extended attributes + * to cgroup files. + */ + +#include <sys/stat.h> +#include <sys/mount.h> +#include <sys/types.h> +#include <sys/xattr.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <dirent.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include "test.h" +#include "usctest.h" +#include "safe_macros.h" + +char *TCID = "cgroup_xattr"; + +static const char cgrp_point[] = "/sys/fs/cgroup"; +static const char cgrp_name[] = "cgrp_test"; +static const char subdir_name[] = "test"; + +struct tst_key { + const char *name; + int good; +}; + +/* only security.* & trusted.* is valid key names */ +static const struct tst_key tkeys[] = { + { .name = "trusted.test", .good = 1, }, + { .name = "security.", .good = 1, }, + { .name = "user.", .good = 0, }, + { .name = "system.", .good = 0, }, +}; + +#define VALUE_SIZE 8 + +/* + * values that can be written to xattr keys, + * their can be anything + */ +struct tst_val { + char buf[VALUE_SIZE]; + size_t size; +}; + +/* cleanup flags */ +static int cgrp_mounted; +static int subdir_created; + +/* test options */ +static int skip_cleanup; +static int verbose; +static const option_t options[] = { + {"s", &skip_cleanup, NULL}, + {"v", &verbose, NULL}, + {NULL, NULL, NULL} +}; + +/* save to change back in the end */ +static char start_work_dir[PATH_MAX]; + +static void help(void); +static void setup(int argc, char *argv[]); +static void test_run(void); +static void cleanup(void); + +static int set_xattrs(const char *file, const struct tst_val *val); +static char *get_xattr(const char *file, const char *key_name, size_t *size); +static int get_xattrs(const char *file, const struct tst_val *val); +/* + * set or get xattr recursively + * + * @path: start directory + * @id: start char to fill in value + * @xattr_operation: can be set_xattrs() or get_xattrs() + */ +static int cgrp_files_walking(const char *path, char *id, + int (*xattr_operation)(const char *, const struct tst_val *)); +static char *get_hex_value(const char *value, size_t size); +static void fill_test_value(char *id, struct tst_val *val); + + +int main(int argc, char *argv[]) +{ + setup(argc, argv); + + test_run(); + + cleanup(); + + tst_exit(); +} + +static void help(void) +{ + printf(" -s Skip cleanup\n"); + printf(" -v Verbose\n"); +} + +void setup(int argc, char *argv[]) +{ + char *msg; + msg = parse_opts(argc, argv, options, help); + if (msg != NULL) + tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); + + tst_require_root(NULL); + + if (access("/proc/cgroups", F_OK) == -1) + tst_brkm(TCONF, cleanup, "Kernel doesn't support for cgroups"); + + if (tst_kvercmp(3, 7, 0) < 0) { + tst_brkm(TCONF, cleanup, + "Test must be run with kernel 3.7 or newer"); + } + + tst_sig(FORK, DEF_HANDLER, cleanup); + + /* mount all available subsystems (cpu, cpuset, memory, ...) */ + if (mount(cgrp_name, cgrp_point, "cgroup", 0, "xattr") == -1) + tst_brkm(TBROK, cleanup, "Can't mount: %s", cgrp_point); + cgrp_mounted = 1; + + /* save current working directory */ + SAFE_GETCWD(cleanup, start_work_dir, PATH_MAX); + SAFE_CHDIR(cleanup, cgrp_point); + + /* create new hierarchy */ + SAFE_MKDIR(cleanup, subdir_name, 0755); + subdir_created = 1; +} + +static void test_run() +{ + char id; + /* set xattr to each file in cgroup fs */ + id = 0; + int set_res = cgrp_files_walking(cgrp_point, &id, set_xattrs); + + /* get & check xattr from each file in cgroup fs */ + id = 0; + int get_res = cgrp_files_walking(cgrp_point, &id, get_xattrs); + + /* final results */ + tst_resm(TINFO, "All test-cases have been completed, summary:"); + tst_resm(TINFO, "Set tests result: %s", (set_res) ? "FAIL" : "PASS"); + tst_resm(TINFO, "Get tests result: %s", (get_res) ? "FAIL" : "PASS"); +} + +static void cleanup(void) +{ + if (skip_cleanup) + return; + + fflush(stdout); + + if (subdir_created) { + SAFE_CHDIR(NULL, cgrp_point); + if (rmdir(subdir_name) == -1) { + tst_brkm(TBROK, NULL, "Can't remove dir, error: %s", + strerror(errno)); + } + } + + if (strlen(start_work_dir) > 0) + SAFE_CHDIR(NULL, start_work_dir); + + if (cgrp_mounted) { + if (umount(cgrp_point) == -1) + tst_brkm(TBROK, NULL, "Can't unmount: %s", cgrp_point); + } + + TEST_CLEANUP; +} + +static int set_xattrs(const char *file, const struct tst_val *val) +{ + int i, res; + res = 0; + for (i = 0; i < ARRAY_SIZE(tkeys); ++i) { + int good = setxattr(file, tkeys[i].name, + (const void *)val->buf, val->size, 0) != -1; + + int fail = good != tkeys[i].good; + res |= fail; + + tst_resm((fail) ? TFAIL : TPASS, + "Expect: %s set xattr key '%s' to file '%s'", + (tkeys[i].good) ? "can" : "can't", + tkeys[i].name, file); + + if (verbose && tkeys[i].good) { + char *rval = get_hex_value(val->buf, val->size); + tst_resm(TINFO, "value =%s", rval); + free(rval); + } + } + return res; +} + +static char *get_xattr(const char *file, const char *key_name, size_t *size) +{ + char *xval = NULL; + /* get value size */ + ssize_t xval_size = getxattr(file, key_name, (void *)xval, 0); + if (xval_size == -1) + return NULL; + + xval = SAFE_MALLOC(cleanup, xval_size); + + if (getxattr(file, key_name, (void *)xval, xval_size) == -1) { + free(xval); + return NULL; + } + *size = xval_size; + return xval; +} + +static int get_xattrs(const char *file, const struct tst_val *val) +{ + int i, fail, res; + res = 0; + for (i = 0; i < ARRAY_SIZE(tkeys); ++i) { + size_t xval_size = 0; + char *xval; + + xval = get_xattr(file, tkeys[i].name, &xval_size); + fail = (xval == NULL && tkeys[i].good); + res |= fail; + + tst_resm((fail) ? TFAIL : TPASS, + "Expect: %s read xattr %s of file '%s'", + (xval == NULL) ? "can't" : "can", tkeys[i].name, file); + if (xval == NULL) + continue; + + if (fail) { + free(xval); + continue; + } + + fail |= val->size != xval_size || + strncmp(val->buf, xval, val->size) != 0; + res |= fail; + + tst_resm((fail) ? TFAIL : TPASS, "Expect: values equal"); + + if (verbose && fail) { + char *rval = get_hex_value(xval, xval_size); + tst_resm(TINFO, "Read xattr value:%s", rval); + free(rval); + char *cval = get_hex_value(val->buf, val->size); + tst_resm(TINFO, "Expected value:%s", cval); + free(cval); + } + free(xval); + } + return res; +} + +static int cgrp_files_walking(const char *path, char *id, + int (*xattr_operation)(const char *, const struct tst_val *)) +{ + int res = 0; + struct dirent *entry; + DIR *dir; + dir = opendir(path); + SAFE_CHDIR(cleanup, path); + tst_resm(TINFO, "In dir %s", path); + errno = 0; + while ((entry = readdir(dir)) != NULL) { + const char *file = entry->d_name; + /* skip current and up directories */ + if (!strcmp(file, "..") || !strcmp(file, ".")) + continue; + + struct stat stat_buf; + TEST(lstat(file, &stat_buf)); + if (TEST_RETURN != -1 && S_ISDIR(stat_buf.st_mode)) { + /* proceed to subdir */ + res |= cgrp_files_walking(file, id, xattr_operation); + /* change directory back */ + SAFE_CHDIR(cleanup, ".."); + tst_resm(TINFO, "In dir %s", path); + } + struct tst_val val; + fill_test_value(id, &val); + res |= xattr_operation(file, &val); + errno = 0; + } + if (errno && !entry) + tst_brkm(TWARN, cleanup, "%s", strerror(errno)); + if (closedir(dir) == -1) + tst_brkm(TWARN, cleanup, "Failed to close dir '%s'", path); + + return res; +} + +static char *get_hex_value(const char *value, size_t size) +{ + const size_t symb_num = 5; /* space + 0xXX*/ + char *buf = SAFE_MALLOC(cleanup, size * symb_num + 1); + size_t i; + for (i = 0; i < size; ++i) { + sprintf(buf + i * symb_num, " 0x%02X", + (unsigned char) *(value++)); + } + return buf; +} + +static void fill_test_value(char *id, struct tst_val *val) +{ + int i; + for (i = 0; i < VALUE_SIZE; ++i) + val->buf[i] = *id; + val->size = VALUE_SIZE; + ++(*id); +} -- 1.7.1 |
From: Alexey K. <ale...@or...> - 2013-05-22 10:25:10
|
Support of extended attributes in cgroups was added in Linux 3.7. Signed-off-by: Alexey Kodanev <ale...@or...> --- runtest/controllers | 1 + .../kernel/controllers/cgroup_xattr/.gitignore | 1 + testcases/kernel/controllers/cgroup_xattr/Makefile | 19 + testcases/kernel/controllers/cgroup_xattr/README | 25 ++ .../kernel/controllers/cgroup_xattr/cgroup_xattr.c | 344 ++++++++++++++++++++ 5 files changed, 390 insertions(+), 0 deletions(-) create mode 100644 testcases/kernel/controllers/cgroup_xattr/.gitignore create mode 100644 testcases/kernel/controllers/cgroup_xattr/Makefile create mode 100644 testcases/kernel/controllers/cgroup_xattr/README create mode 100644 testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c diff --git a/runtest/controllers b/runtest/controllers index b78d828..94fc185 100644 --- a/runtest/controllers +++ b/runtest/controllers @@ -12,3 +12,4 @@ memcg_stress memcg_stress_test.sh memcg_control PAGESIZE=$(mem_process -p);memcg_control_test.sh $PAGESIZE $PAGESIZE $((PAGESIZE * 2)) cgroup_fj run_cgroup_test_fj.sh controllers test_controllers.sh +cgroup_xattr cgroup_xattr diff --git a/testcases/kernel/controllers/cgroup_xattr/.gitignore b/testcases/kernel/controllers/cgroup_xattr/.gitignore new file mode 100644 index 0000000..3664cc9 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/.gitignore @@ -0,0 +1 @@ +/cgroup_xattr diff --git a/testcases/kernel/controllers/cgroup_xattr/Makefile b/testcases/kernel/controllers/cgroup_xattr/Makefile new file mode 100644 index 0000000..cd49588 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/Makefile @@ -0,0 +1,19 @@ +# Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/controllers/cgroup_xattr/README b/testcases/kernel/controllers/cgroup_xattr/README new file mode 100644 index 0000000..9cc5176 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/README @@ -0,0 +1,25 @@ +TEST SUITE: + +The directory cgroup_xattr contains the tests related to extended +attributes in cgroup filesystem. + +WARNING: + +This test can cause a kernel panic due to a bug in kernels prior to 3.8. +It was fixed by kernel upstream commit +712317ad97f41e738e1a19aa0a6392a78a84094e: + +"We should store file xattrs in struct cfent instead of struct cftype, +because cftype is a type while cfent is object instance of cftype." + +TESTS AIM: + +The aim of the tests is to check the extended attributes in cgroup +filesystem. This feature was added in Linux 3.7 to allow attaching runtime +meta information to cgroups and everything they model (services, apps, vms) +and can easily be shared among applications. + +Test creates all available subsystems (cpu, cpuset, memory, ...) in the +cgroup directory and in one more created hierarchy. Then sets extended +attributes to all files in cgroup fs and subsequently reads the file's +extended attributes back, checking values during the process. diff --git a/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c b/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c new file mode 100644 index 0000000..4fa2768 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Alexey Kodanev <ale...@or...> + * + * Test checks following preconditions: + * since Linux kernel 3.7 it is possible to set extended attributes + * to cgroup files. + */ + +#include <sys/stat.h> +#include <sys/mount.h> +#include <sys/types.h> +#include <sys/xattr.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <dirent.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include "test.h" +#include "usctest.h" +#include "safe_macros.h" + +char *TCID = "cgroup_xattr"; + +static const char cgrp_point[] = "cgroup"; +static const char cgrp_name[] = "cgrp_test"; +static const char subdir_name[] = "test"; + +struct tst_key { + const char *name; + int good; +}; + +/* only security.* & trusted.* are valid key names */ +static const struct tst_key tkeys[] = { + { .name = "trusted.test", .good = 1, }, + { .name = "security.", .good = 1, }, + { .name = "user.", .good = 0, }, + { .name = "system.", .good = 0, }, +}; + +#define DEFAULT_VALUE_SIZE 8 + +/* struct to store key's value */ +struct tst_val { + char *buf; + size_t size; +}; +static struct tst_val val; + +/* it fills value's buffer */ +static char id; + +/* cleanup flags */ +static int cgrp_mounted; +static int subdir_created; + +/* + * When test breaks, all open dirs should be closed + * otherwise umount won't succeed + */ +#define MAX_OPEN_DIR 32 +static DIR *odir[MAX_OPEN_DIR]; +static int odir_num; + +/* test options */ +static char *narg; +static int nflag; +static int skip_cleanup; +static int verbose; +static const option_t options[] = { + {"n:", &nflag, &narg}, + {"s", &skip_cleanup, NULL}, + {"v", &verbose, NULL}, + {NULL, NULL, NULL} +}; + +static void help(void); +static void setup(int argc, char *argv[]); +static void test_run(void); +static void cleanup(void); + +static int set_xattrs(const char *file); +static int get_xattrs(const char *file); +/* + * set or get xattr recursively + * + * @path: start directory + * @xattr_operation: can be set_xattrs() or get_xattrs() + */ +static int cgrp_files_walking(const char *path, + int (*xattr_operation)(const char *)); + +int main(int argc, char *argv[]) +{ + setup(argc, argv); + + test_run(); + + cleanup(); + + tst_exit(); +} + +static void help(void) +{ + printf(" -n x Write x bytes to xattr value, default is %d\n", + DEFAULT_VALUE_SIZE); + printf(" -s Skip cleanup\n"); + printf(" -v Verbose\n"); +} + +void setup(int argc, char *argv[]) +{ + char *msg; + msg = parse_opts(argc, argv, options, help); + if (msg != NULL) + tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); + + tst_require_root(NULL); + + if (access("/proc/cgroups", F_OK) == -1) + tst_brkm(TCONF, NULL, "Kernel doesn't support for cgroups"); + + if (tst_kvercmp(3, 7, 0) < 0) { + tst_brkm(TCONF, NULL, + "Test must be run with kernel 3.7 or newer"); + } + + int value_size = DEFAULT_VALUE_SIZE; + if (nflag) { + if (sscanf(narg, "%i", &value_size) != 1) + tst_brkm(TBROK, NULL, "-n option arg is not a number"); + if (value_size <= 0) + tst_brkm(TBROK, NULL, "-n option arg is less than 1"); + } + + /* initialize test value */ + val.size = value_size; + val.buf = SAFE_MALLOC(NULL, value_size); + + tst_sig(FORK, DEF_HANDLER, cleanup); + + tst_tmpdir(); + + SAFE_MKDIR(cleanup, cgrp_point, 0755); + + /* mount all available subsystems (cpu, cpuset, memory, ...) */ + if (mount(cgrp_name, cgrp_point, "cgroup", 0, "xattr") == -1) + tst_brkm(TBROK, cleanup, "Can't mount: %s", cgrp_point); + cgrp_mounted = 1; + + /* create new hierarchy */ + SAFE_CHDIR(cleanup, cgrp_point); + SAFE_MKDIR(cleanup, subdir_name, 0755); + subdir_created = 1; +} + +static void test_run(void) +{ + /* set xattr to each file in cgroup fs */ + int set_res = cgrp_files_walking(".", set_xattrs); + /* reset value */ + id = 0; + /* get & check xattr */ + int get_res = cgrp_files_walking(".", get_xattrs); + + /* final results */ + tst_resm(TINFO, "All test-cases have been completed, summary:"); + tst_resm(TINFO, "Set tests result: %s", (set_res) ? "FAIL" : "PASS"); + tst_resm(TINFO, "Get tests result: %s", (get_res) ? "FAIL" : "PASS"); +} + +static void cleanup(void) +{ + if (val.buf != NULL) + free(val.buf); + + if (skip_cleanup) + return; + + /* + * Kernels 3.7 can crash while unmounting cgroups with xattr, + * call tst_flush() to make sure all buffered data written + * before it happens + */ + tst_flush(); + + int i; + for (i = 0; i < odir_num; ++i) { + if (closedir(odir[i]) == -1) + tst_brkm(TBROK, NULL, "Failed to close dir\n"); + } + + char *cwd = get_tst_tmpdir(); + SAFE_CHDIR(NULL, cwd); + free(cwd); + + if (subdir_created) { + SAFE_CHDIR(NULL, cgrp_point); + if (rmdir(subdir_name) == -1) + tst_brkm(TBROK | TERRNO, NULL, "Can't remove dir"); + SAFE_CHDIR(NULL, ".."); + } + + if (cgrp_mounted) { + if (umount(cgrp_point) == -1) { + tst_brkm(TBROK | TERRNO, NULL, + "Can't unmount: %s", cgrp_point); + } + } + + tst_rmdir(); + TEST_CLEANUP; +} + +static int set_xattrs(const char *file) +{ + int i, err, fail, res; + res = 0; + for (i = 0; i < ARRAY_SIZE(tkeys); ++i) { + err = setxattr(file, tkeys[i].name, + (const void *)val.buf, val.size, 0) == -1; + + fail = err && tkeys[i].good; + res |= fail; + + tst_resm((fail) ? TFAIL : TPASS, + "Expect: %s set xattr key '%s' to file '%s'", + (tkeys[i].good) ? "can" : "can't", + tkeys[i].name, file); + + if (verbose && tkeys[i].good) + tst_resm_hexd(TINFO, val.buf, val.size, "value:"); + } + return res; +} + +static int get_xattrs(const char *file) +{ + int i, fail, res; + res = 0; + for (i = 0; i < ARRAY_SIZE(tkeys); ++i) { + /* get value size */ + ssize_t size = getxattr(file, tkeys[i].name, NULL, 0); + fail = (size == -1 && tkeys[i].good); + res |= fail; + + tst_resm((fail) ? TFAIL : TPASS, + "Expect: %s read xattr %s of file '%s'", + (size == -1) ? "can't" : "can", + tkeys[i].name, file); + + if (fail || size == -1) + continue; + + /* get xattr value */ + char xval[size]; + if (getxattr(file, tkeys[i].name, (void *)xval, size) == -1) { + tst_brkm(TBROK, cleanup, + "Can't get buffer of key %s", + tkeys[i].name); + } + fail = val.size != size || + strncmp(val.buf, xval, val.size) != 0; + res |= fail; + + tst_resm((fail) ? TFAIL : TPASS, "Expect: values equal"); + + if (verbose && fail) { + tst_resm_hexd(TINFO, xval, size, + "Read xattr value:"); + tst_resm_hexd(TINFO, val.buf, val.size, + "Expect xattr value:"); + } + } + return res; +} + +static int cgrp_files_walking(const char *path, + int (*xattr_operation)(const char *)) +{ + int res = 0; + struct dirent *entry; + DIR *dir = opendir(path); + + odir[odir_num] = dir; + if (++odir_num >= MAX_OPEN_DIR) { + tst_brkm(TBROK, cleanup, + "Unexpected num of open dirs, max: %d", MAX_OPEN_DIR); + } + + SAFE_CHDIR(cleanup, path); + + tst_resm(TINFO, "In dir %s", path); + + errno = 0; + while ((entry = readdir(dir)) != NULL) { + const char *file = entry->d_name; + /* skip current and up directories */ + if (!strcmp(file, "..") || !strcmp(file, ".")) + continue; + struct stat stat_buf; + TEST(lstat(file, &stat_buf)); + if (TEST_RETURN != -1 && S_ISDIR(stat_buf.st_mode)) { + /* proceed to subdir */ + res |= cgrp_files_walking(file, xattr_operation); + tst_resm(TINFO, "In dir %s", path); + } + memset(val.buf, id++, val.size); + res |= xattr_operation(file); + errno = 0; + } + if (errno && !entry) { + tst_brkm(TWARN | TERRNO, cleanup, + "Error while reading dir '%s'", path); + } + if (closedir(dir) == -1) + tst_brkm(TWARN, cleanup, "Failed to close dir '%s'", path); + else + odir[--odir_num] = NULL; + + if (strcmp(path, ".")) + SAFE_CHDIR(cleanup, ".."); + return res; +} -- 1.7.1 |
From: <ch...@su...> - 2013-05-22 14:25:36
|
Hi! > Support of extended attributes in cgroups was added in Linux 3.7. > + /* get xattr value */ > + char xval[size]; > + if (getxattr(file, tkeys[i].name, (void *)xval, size) == -1) { The conversion from char * to void * should be automatic so the cast to (void *) shouldn't be needed, or not? The rest if fine. Waiting for resping of the tst_resm_hexd() patch before aplying this one. -- Cyril Hrubis ch...@su... |
From: <ale...@or...> - 2013-05-23 07:25:41
|
On 05/22/2013 06:26 PM, ch...@su... wrote: > Hi! >> Support of extended attributes in cgroups was added in Linux 3.7. >> + /* get xattr value */ >> + char xval[size]; >> + if (getxattr(file, tkeys[i].name, (void *)xval, size) == -1) { > The conversion from char * to void * should be automatic so the cast to > (void *) shouldn't be needed, or not? > Yes indeed it converted implicitly. |
From: Alexey K. <ale...@or...> - 2013-05-23 07:10:35
|
Support of extended attributes in cgroups was added in Linux 3.7. Signed-off-by: Alexey Kodanev <ale...@or...> --- runtest/controllers | 1 + .../kernel/controllers/cgroup_xattr/.gitignore | 1 + testcases/kernel/controllers/cgroup_xattr/Makefile | 19 + testcases/kernel/controllers/cgroup_xattr/README | 25 ++ .../kernel/controllers/cgroup_xattr/cgroup_xattr.c | 344 ++++++++++++++++++++ 5 files changed, 390 insertions(+), 0 deletions(-) create mode 100644 testcases/kernel/controllers/cgroup_xattr/.gitignore create mode 100644 testcases/kernel/controllers/cgroup_xattr/Makefile create mode 100644 testcases/kernel/controllers/cgroup_xattr/README create mode 100644 testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c diff --git a/runtest/controllers b/runtest/controllers index b78d828..94fc185 100644 --- a/runtest/controllers +++ b/runtest/controllers @@ -12,3 +12,4 @@ memcg_stress memcg_stress_test.sh memcg_control PAGESIZE=$(mem_process -p);memcg_control_test.sh $PAGESIZE $PAGESIZE $((PAGESIZE * 2)) cgroup_fj run_cgroup_test_fj.sh controllers test_controllers.sh +cgroup_xattr cgroup_xattr diff --git a/testcases/kernel/controllers/cgroup_xattr/.gitignore b/testcases/kernel/controllers/cgroup_xattr/.gitignore new file mode 100644 index 0000000..3664cc9 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/.gitignore @@ -0,0 +1 @@ +/cgroup_xattr diff --git a/testcases/kernel/controllers/cgroup_xattr/Makefile b/testcases/kernel/controllers/cgroup_xattr/Makefile new file mode 100644 index 0000000..cd49588 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/Makefile @@ -0,0 +1,19 @@ +# Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/controllers/cgroup_xattr/README b/testcases/kernel/controllers/cgroup_xattr/README new file mode 100644 index 0000000..9cc5176 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/README @@ -0,0 +1,25 @@ +TEST SUITE: + +The directory cgroup_xattr contains the tests related to extended +attributes in cgroup filesystem. + +WARNING: + +This test can cause a kernel panic due to a bug in kernels prior to 3.8. +It was fixed by kernel upstream commit +712317ad97f41e738e1a19aa0a6392a78a84094e: + +"We should store file xattrs in struct cfent instead of struct cftype, +because cftype is a type while cfent is object instance of cftype." + +TESTS AIM: + +The aim of the tests is to check the extended attributes in cgroup +filesystem. This feature was added in Linux 3.7 to allow attaching runtime +meta information to cgroups and everything they model (services, apps, vms) +and can easily be shared among applications. + +Test creates all available subsystems (cpu, cpuset, memory, ...) in the +cgroup directory and in one more created hierarchy. Then sets extended +attributes to all files in cgroup fs and subsequently reads the file's +extended attributes back, checking values during the process. diff --git a/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c b/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c new file mode 100644 index 0000000..2cc592f --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Alexey Kodanev <ale...@or...> + * + * Test checks following preconditions: + * since Linux kernel 3.7 it is possible to set extended attributes + * to cgroup files. + */ + +#include <sys/stat.h> +#include <sys/mount.h> +#include <sys/types.h> +#include <sys/xattr.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <dirent.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include "test.h" +#include "usctest.h" +#include "safe_macros.h" + +char *TCID = "cgroup_xattr"; + +static const char cgrp_point[] = "cgroup"; +static const char cgrp_name[] = "cgrp_test"; +static const char subdir_name[] = "test"; + +struct tst_key { + const char *name; + int good; +}; + +/* only security.* & trusted.* are valid key names */ +static const struct tst_key tkeys[] = { + { .name = "trusted.test", .good = 1, }, + { .name = "security.", .good = 1, }, + { .name = "user.", .good = 0, }, + { .name = "system.", .good = 0, }, +}; + +#define DEFAULT_VALUE_SIZE 8 + +/* struct to store key's value */ +struct tst_val { + char *buf; + size_t size; +}; +static struct tst_val val; + +/* it fills value's buffer */ +static char id; + +/* cleanup flags */ +static int cgrp_mounted; +static int subdir_created; + +/* + * When test breaks, all open dirs should be closed + * otherwise umount won't succeed + */ +#define MAX_OPEN_DIR 32 +static DIR *odir[MAX_OPEN_DIR]; +static int odir_num; + +/* test options */ +static char *narg; +static int nflag; +static int skip_cleanup; +static int verbose; +static const option_t options[] = { + {"n:", &nflag, &narg}, + {"s", &skip_cleanup, NULL}, + {"v", &verbose, NULL}, + {NULL, NULL, NULL} +}; + +static void help(void); +static void setup(int argc, char *argv[]); +static void test_run(void); +static void cleanup(void); + +static int set_xattrs(const char *file); +static int get_xattrs(const char *file); +/* + * set or get xattr recursively + * + * @path: start directory + * @xattr_operation: can be set_xattrs() or get_xattrs() + */ +static int cgrp_files_walking(const char *path, + int (*xattr_operation)(const char *)); + +int main(int argc, char *argv[]) +{ + setup(argc, argv); + + test_run(); + + cleanup(); + + tst_exit(); +} + +static void help(void) +{ + printf(" -n x Write x bytes to xattr value, default is %d\n", + DEFAULT_VALUE_SIZE); + printf(" -s Skip cleanup\n"); + printf(" -v Verbose\n"); +} + +void setup(int argc, char *argv[]) +{ + char *msg; + msg = parse_opts(argc, argv, options, help); + if (msg != NULL) + tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); + + tst_require_root(NULL); + + if (access("/proc/cgroups", F_OK) == -1) + tst_brkm(TCONF, NULL, "Kernel doesn't support for cgroups"); + + if (tst_kvercmp(3, 7, 0) < 0) { + tst_brkm(TCONF, NULL, + "Test must be run with kernel 3.7 or newer"); + } + + int value_size = DEFAULT_VALUE_SIZE; + if (nflag) { + if (sscanf(narg, "%i", &value_size) != 1) + tst_brkm(TBROK, NULL, "-n option arg is not a number"); + if (value_size <= 0) + tst_brkm(TBROK, NULL, "-n option arg is less than 1"); + } + + /* initialize test value */ + val.size = value_size; + val.buf = SAFE_MALLOC(NULL, value_size); + + tst_sig(FORK, DEF_HANDLER, cleanup); + + tst_tmpdir(); + + SAFE_MKDIR(cleanup, cgrp_point, 0755); + + /* mount all available subsystems (cpu, cpuset, memory, ...) */ + if (mount(cgrp_name, cgrp_point, "cgroup", 0, "xattr") == -1) + tst_brkm(TBROK, cleanup, "Can't mount: %s", cgrp_point); + cgrp_mounted = 1; + + /* create new hierarchy */ + SAFE_CHDIR(cleanup, cgrp_point); + SAFE_MKDIR(cleanup, subdir_name, 0755); + subdir_created = 1; +} + +static void test_run(void) +{ + /* set xattr to each file in cgroup fs */ + int set_res = cgrp_files_walking(".", set_xattrs); + /* reset value */ + id = 0; + /* get & check xattr */ + int get_res = cgrp_files_walking(".", get_xattrs); + + /* final results */ + tst_resm(TINFO, "All test-cases have been completed, summary:"); + tst_resm(TINFO, "Set tests result: %s", (set_res) ? "FAIL" : "PASS"); + tst_resm(TINFO, "Get tests result: %s", (get_res) ? "FAIL" : "PASS"); +} + +static void cleanup(void) +{ + if (val.buf != NULL) + free(val.buf); + + if (skip_cleanup) + return; + + /* + * Kernels 3.7 can crash while unmounting cgroups with xattr, + * call tst_flush() to make sure all buffered data written + * before it happens + */ + tst_flush(); + + int i; + for (i = 0; i < odir_num; ++i) { + if (closedir(odir[i]) == -1) + tst_brkm(TBROK, NULL, "Failed to close dir\n"); + } + + char *cwd = get_tst_tmpdir(); + SAFE_CHDIR(NULL, cwd); + free(cwd); + + if (subdir_created) { + SAFE_CHDIR(NULL, cgrp_point); + if (rmdir(subdir_name) == -1) + tst_brkm(TBROK | TERRNO, NULL, "Can't remove dir"); + SAFE_CHDIR(NULL, ".."); + } + + if (cgrp_mounted) { + if (umount(cgrp_point) == -1) { + tst_brkm(TBROK | TERRNO, NULL, + "Can't unmount: %s", cgrp_point); + } + } + + tst_rmdir(); + TEST_CLEANUP; +} + +static int set_xattrs(const char *file) +{ + int i, err, fail, res; + res = 0; + for (i = 0; i < ARRAY_SIZE(tkeys); ++i) { + err = setxattr(file, tkeys[i].name, + (const void *)val.buf, val.size, 0) == -1; + + fail = err && tkeys[i].good; + res |= fail; + + tst_resm((fail) ? TFAIL : TPASS, + "Expect: %s set xattr key '%s' to file '%s'", + (tkeys[i].good) ? "can" : "can't", + tkeys[i].name, file); + + if (verbose && tkeys[i].good) + tst_resm_hexd(TINFO, val.buf, val.size, "value:"); + } + return res; +} + +static int get_xattrs(const char *file) +{ + int i, fail, res; + res = 0; + for (i = 0; i < ARRAY_SIZE(tkeys); ++i) { + /* get value size */ + ssize_t size = getxattr(file, tkeys[i].name, NULL, 0); + fail = (size == -1 && tkeys[i].good); + res |= fail; + + tst_resm((fail) ? TFAIL : TPASS, + "Expect: %s read xattr %s of file '%s'", + (size == -1) ? "can't" : "can", + tkeys[i].name, file); + + if (fail || size == -1) + continue; + + /* get xattr value */ + char xval[size]; + if (getxattr(file, tkeys[i].name, xval, size) == -1) { + tst_brkm(TBROK, cleanup, + "Can't get buffer of key %s", + tkeys[i].name); + } + fail = val.size != size || + strncmp(val.buf, xval, val.size) != 0; + res |= fail; + + tst_resm((fail) ? TFAIL : TPASS, "Expect: values equal"); + + if (verbose && fail) { + tst_resm_hexd(TINFO, xval, size, + "Read xattr value:"); + tst_resm_hexd(TINFO, val.buf, val.size, + "Expect xattr value:"); + } + } + return res; +} + +static int cgrp_files_walking(const char *path, + int (*xattr_operation)(const char *)) +{ + int res = 0; + struct dirent *entry; + DIR *dir = opendir(path); + + odir[odir_num] = dir; + if (++odir_num >= MAX_OPEN_DIR) { + tst_brkm(TBROK, cleanup, + "Unexpected num of open dirs, max: %d", MAX_OPEN_DIR); + } + + SAFE_CHDIR(cleanup, path); + + tst_resm(TINFO, "In dir %s", path); + + errno = 0; + while ((entry = readdir(dir)) != NULL) { + const char *file = entry->d_name; + /* skip current and up directories */ + if (!strcmp(file, "..") || !strcmp(file, ".")) + continue; + struct stat stat_buf; + TEST(lstat(file, &stat_buf)); + if (TEST_RETURN != -1 && S_ISDIR(stat_buf.st_mode)) { + /* proceed to subdir */ + res |= cgrp_files_walking(file, xattr_operation); + tst_resm(TINFO, "In dir %s", path); + } + memset(val.buf, id++, val.size); + res |= xattr_operation(file); + errno = 0; + } + if (errno && !entry) { + tst_brkm(TWARN | TERRNO, cleanup, + "Error while reading dir '%s'", path); + } + if (closedir(dir) == -1) + tst_brkm(TWARN, cleanup, "Failed to close dir '%s'", path); + else + odir[--odir_num] = NULL; + + if (strcmp(path, ".")) + SAFE_CHDIR(cleanup, ".."); + return res; +} -- 1.7.1 |
From: <ch...@su...> - 2013-05-23 13:30:39
|
Hi! > Support of extended attributes in cgroups was added in Linux 3.7. I've tried to run the test on my machine where cgroups are allready mounted to /sys/fs/cgroup (which seems to be the case for modern distributions). In such situation the mount in the test fails with EBUSY. I looks like the root hierarchy can be mounted only once. Now we cannot unmout the /sys/fs/cgroup as this one is used by the system. But it looks like we can mount each of the controllers, i.e. for each controller: mkdir /path/type mount -t cgroup -o type type /path/type So the options are either to use the /sys/fs/cgroup directory or mount each controller one by one. What do you think? -- Cyril Hrubis ch...@su... |
From: <ale...@or...> - 2013-05-23 14:25:09
|
Hi! On 05/23/2013 05:31 PM, ch...@su... wrote: > Hi! >> Support of extended attributes in cgroups was added in Linux 3.7. > I've tried to run the test on my machine where cgroups are allready > mounted to /sys/fs/cgroup (which seems to be the case for modern > distributions). In such situation the mount in the test fails with > EBUSY. I looks like the root hierarchy can be mounted only once. Now we > cannot unmout the /sys/fs/cgroup as this one is used by the system. > > But it looks like we can mount each of the controllers, i.e. > > for each controller: > mkdir /path/type > mount -t cgroup -o type type /path/type > > So the options are either to use the /sys/fs/cgroup directory or mount > each controller one by one. > > What do you think? > it appears in case you already mounted cgroups with only a few subsystems (using -o type1, ...) and want to mount somewhere all available subsystems without specifying ones. Yes, it's solving by mounting subsystems using -o type1, type2,... and we can specify all, it will be ok! but it doesn't work in case all available sybsystems has been already mounted (without options) before and we will get the same busy error. It seems that each way of mounting should coincide with previous. So one possible way out is detect how many subsystems was already mounted and mount accordingly. Any ideas? |
From: <ale...@or...> - 2013-05-24 11:45:00
|
Hi! To make my test run on systems where cgroups have already been mounted to some location, becoming a bit tricky. I need to calc which of subsystems combinations already used to mount cgroups, and mount it in the same way, but subsys which are not mounted don't have such restriction. For example, I have a machine where cgroups (cpu, cpuset) are mounted to /sys/fs/cgroup. After that I can only mount (to other directory) cgroups with options "cpu,cpuset" and all other combinations except ones which include "cpu" or "cpuset". If I try to mount only cpu, it will come with EBUSY error. Also, there is another issue: if, for instance, assume that somewhere in the system "cpuset" (or any) subsystem mounted without "xattr" option. Then, my test decided to mount it in the same way but with additional option "xattr". And it will be successfully mounted (return code is 0). But it's not possible to set any xattr to cgroups files in that new mounted subsystem, the error: operation is not supported. At least I expect that mount should be denied with EBUSY or xattr could be set as usual. This issue was reported to cgroups author, and may be he will fix it soon. I can see only one workaround: mount should be made to subsystems which are not mounted! When fix is committed, the workaround can be removed. Thanks, Alexey On 05/23/2013 06:24 PM, ale...@or... wrote: > Hi! > On 05/23/2013 05:31 PM, ch...@su... wrote: >> Hi! >>> Support of extended attributes in cgroups was added in Linux 3.7. >> I've tried to run the test on my machine where cgroups are allready >> mounted to /sys/fs/cgroup (which seems to be the case for modern >> distributions). In such situation the mount in the test fails with >> EBUSY. I looks like the root hierarchy can be mounted only once. Now we >> cannot unmout the /sys/fs/cgroup as this one is used by the system. >> >> But it looks like we can mount each of the controllers, i.e. >> >> for each controller: >> mkdir /path/type >> mount -t cgroup -o type type /path/type >> >> So the options are either to use the /sys/fs/cgroup directory or mount >> each controller one by one. >> >> What do you think? >> > it appears in case you already mounted cgroups with only a few > subsystems (using -o type1, ...) and want to mount somewhere all > available subsystems without specifying ones. > > Yes, it's solving by mounting subsystems using -o type1, type2,... and > we can specify all, it will be ok! > > but it doesn't work in case all available sybsystems has been already > mounted (without options) before and we will get the same busy error. > > It seems that each way of mounting should coincide with previous. So > one possible way out is detect how many subsystems was already mounted > and mount accordingly. > Any ideas? > > > > > |
From: Alexey K. <ale...@or...> - 2013-05-23 16:29:28
|
Support of extended attributes in cgroups was added in Linux 3.7. Signed-off-by: Alexey Kodanev <ale...@or...> --- runtest/controllers | 1 + .../kernel/controllers/cgroup_xattr/.gitignore | 1 + testcases/kernel/controllers/cgroup_xattr/Makefile | 19 + testcases/kernel/controllers/cgroup_xattr/README | 25 ++ .../kernel/controllers/cgroup_xattr/cgroup_xattr.c | 402 ++++++++++++++++++++ 5 files changed, 448 insertions(+), 0 deletions(-) create mode 100644 testcases/kernel/controllers/cgroup_xattr/.gitignore create mode 100644 testcases/kernel/controllers/cgroup_xattr/Makefile create mode 100644 testcases/kernel/controllers/cgroup_xattr/README create mode 100644 testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c diff --git a/runtest/controllers b/runtest/controllers index b78d828..94fc185 100644 --- a/runtest/controllers +++ b/runtest/controllers @@ -12,3 +12,4 @@ memcg_stress memcg_stress_test.sh memcg_control PAGESIZE=$(mem_process -p);memcg_control_test.sh $PAGESIZE $PAGESIZE $((PAGESIZE * 2)) cgroup_fj run_cgroup_test_fj.sh controllers test_controllers.sh +cgroup_xattr cgroup_xattr diff --git a/testcases/kernel/controllers/cgroup_xattr/.gitignore b/testcases/kernel/controllers/cgroup_xattr/.gitignore new file mode 100644 index 0000000..3664cc9 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/.gitignore @@ -0,0 +1 @@ +/cgroup_xattr diff --git a/testcases/kernel/controllers/cgroup_xattr/Makefile b/testcases/kernel/controllers/cgroup_xattr/Makefile new file mode 100644 index 0000000..cd49588 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/Makefile @@ -0,0 +1,19 @@ +# Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/controllers/cgroup_xattr/README b/testcases/kernel/controllers/cgroup_xattr/README new file mode 100644 index 0000000..9cc5176 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/README @@ -0,0 +1,25 @@ +TEST SUITE: + +The directory cgroup_xattr contains the tests related to extended +attributes in cgroup filesystem. + +WARNING: + +This test can cause a kernel panic due to a bug in kernels prior to 3.8. +It was fixed by kernel upstream commit +712317ad97f41e738e1a19aa0a6392a78a84094e: + +"We should store file xattrs in struct cfent instead of struct cftype, +because cftype is a type while cfent is object instance of cftype." + +TESTS AIM: + +The aim of the tests is to check the extended attributes in cgroup +filesystem. This feature was added in Linux 3.7 to allow attaching runtime +meta information to cgroups and everything they model (services, apps, vms) +and can easily be shared among applications. + +Test creates as many subsystems as possible (cpu, cpuset, ...) in the +cgroup tmp directory and in one more created hierarchy. Then sets extended +attributes to all files in cgroup fs and subsequently reads the file's +extended attributes back, checking values during the process. diff --git a/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c b/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c new file mode 100644 index 0000000..4ef5e16 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Alexey Kodanev <ale...@or...> + * + * Test checks following preconditions: + * since Linux kernel 3.7 it is possible to set extended attributes + * to cgroup files. + */ + +#include <sys/stat.h> +#include <sys/mount.h> +#include <sys/types.h> +#include <sys/xattr.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <dirent.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include "test.h" +#include "usctest.h" +#include "safe_macros.h" + +char *TCID = "cgroup_xattr"; + +static const char cgrp_point[] = "cgroup"; +static const char cgrp_name[] = "cgrp_test"; +static const char subdir_name[] = "test"; + +#define MAX_SUBSYS 64 +#define MAX_OPTIONS_LEN 256 + +static char subsys[MAX_OPTIONS_LEN]; + +struct tst_key { + const char *name; + int good; +}; + +/* only security.* & trusted.* are valid key names */ +static const struct tst_key tkeys[] = { + { .name = "trusted.test", .good = 1, }, + { .name = "security.", .good = 1, }, + { .name = "user.", .good = 0, }, + { .name = "system.", .good = 0, }, +}; + +#define DEFAULT_VALUE_SIZE 8 + +/* struct to store key's value */ +struct tst_val { + char *buf; + size_t size; +}; +static struct tst_val val; + +/* it fills value's buffer */ +static char id; + +/* cleanup flags */ +static int cgrp_mounted; +static int subdir_created; + +/* + * When test breaks, all open dirs should be closed + * otherwise umount won't succeed + */ +#define MAX_OPEN_DIR 32 +static DIR *odir[MAX_OPEN_DIR]; +static int odir_num; + +/* test options */ +static char *narg; +static int nflag; +static int skip_cleanup; +static int verbose; +static const option_t options[] = { + {"n:", &nflag, &narg}, + {"s", &skip_cleanup, NULL}, + {"v", &verbose, NULL}, + {NULL, NULL, NULL} +}; + +static void help(void); +static void setup(int argc, char *argv[]); +static void test_run(void); +static void cleanup(void); + +/* + * get info about mounted subsystems, + * it is possible to mount only already mounted subsystems + * or ones not mounted, other combinations won't succeed + */ +static void make_cgroup_options(void); +static int set_xattrs(const char *file); +static int get_xattrs(const char *file); +/* + * set or get xattr recursively + * + * @path: start directory + * @xattr_operation: can be set_xattrs() or get_xattrs() + */ +static int cgrp_files_walking(const char *path, + int (*xattr_operation)(const char *)); + +int main(int argc, char *argv[]) +{ + setup(argc, argv); + + test_run(); + + cleanup(); + + tst_exit(); +} + +static void help(void) +{ + printf(" -n x Write x bytes to xattr value, default is %d\n", + DEFAULT_VALUE_SIZE); + printf(" -s Skip cleanup\n"); + printf(" -v Verbose\n"); +} + +void setup(int argc, char *argv[]) +{ + char *msg; + msg = parse_opts(argc, argv, options, help); + if (msg != NULL) + tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); + + tst_require_root(NULL); + + if (access("/proc/cgroups", F_OK) == -1) + tst_brkm(TCONF, NULL, "Kernel doesn't support for cgroups"); + + if (tst_kvercmp(3, 7, 0) < 0) { + tst_brkm(TCONF, NULL, + "Test must be run with kernel 3.7 or newer"); + } + + int value_size = DEFAULT_VALUE_SIZE; + if (nflag) { + if (sscanf(narg, "%i", &value_size) != 1) + tst_brkm(TBROK, NULL, "-n option arg is not a number"); + if (value_size <= 0) + tst_brkm(TBROK, NULL, "-n option arg is less than 1"); + } + + /* initialize test value */ + val.size = value_size; + val.buf = SAFE_MALLOC(NULL, value_size); + + tst_sig(FORK, DEF_HANDLER, cleanup); + + tst_tmpdir(); + + SAFE_MKDIR(cleanup, cgrp_point, 0755); + + make_cgroup_options(); + + if (mount(cgrp_name, cgrp_point, "cgroup", 0, subsys) == -1) + tst_brkm(TBROK, cleanup, "Can't mount: %s", cgrp_point); + cgrp_mounted = 1; + + /* create new hierarchy */ + SAFE_CHDIR(cleanup, cgrp_point); + SAFE_MKDIR(cleanup, subdir_name, 0755); + subdir_created = 1; +} + +static void test_run(void) +{ + /* set xattr to each file in cgroup fs */ + int set_res = cgrp_files_walking(".", set_xattrs); + /* reset value */ + id = 0; + /* get & check xattr */ + int get_res = cgrp_files_walking(".", get_xattrs); + + /* final results */ + tst_resm(TINFO, "All test-cases have been completed, summary:"); + tst_resm(TINFO, "Set tests result: %s", (set_res) ? "FAIL" : "PASS"); + tst_resm(TINFO, "Get tests result: %s", (get_res) ? "FAIL" : "PASS"); +} + +static void cleanup(void) +{ + if (val.buf != NULL) + free(val.buf); + + if (skip_cleanup) + return; + + /* + * Kernels 3.7 can crash while unmounting cgroups with xattr, + * call tst_flush() to make sure all buffered data written + * before it happens + */ + tst_flush(); + + int i; + for (i = 0; i < odir_num; ++i) { + if (closedir(odir[i]) == -1) + tst_brkm(TBROK, NULL, "Failed to close dir\n"); + } + + char *cwd = get_tst_tmpdir(); + SAFE_CHDIR(NULL, cwd); + free(cwd); + + if (subdir_created) { + SAFE_CHDIR(NULL, cgrp_point); + if (rmdir(subdir_name) == -1) + tst_brkm(TBROK | TERRNO, NULL, "Can't remove dir"); + SAFE_CHDIR(NULL, ".."); + } + + if (cgrp_mounted) { + if (umount(cgrp_point) == -1) { + tst_brkm(TBROK | TERRNO, NULL, + "Can't unmount: %s", cgrp_point); + } + } + + tst_rmdir(); + TEST_CLEANUP; +} + +void make_cgroup_options(void) +{ + /* detect sybsystems */ + FILE *fd = fopen("/proc/cgroups", "r"); + if (fd == NULL) + tst_brkm(TBROK, cleanup, "Failed to read /proc/cgroups"); + + int subsys_ynum = 0; + /* store not mounted subsys here*/ + char subsys_n[MAX_OPTIONS_LEN]; + int subsys_nnum = 0; + + strcpy(subsys, "xattr"); + strcpy(subsys_n, "xattr"); + + char str[MAX_SUBSYS]; + + int first = 1; + + while ((fgets(str, MAX_SUBSYS, fd)) != NULL) { + /* skip first line */ + if (first) { + first = 0; + continue; + } + int num = 0; + char name[MAX_SUBSYS]; + sscanf(str, "%s\t%d", name, &num); + + strcat((num > 0) ? subsys : subsys_n, ","); + strcat((num > 0) ? subsys : subsys_n, name); + + if (num > 0) + ++subsys_ynum; + else + ++subsys_nnum; + } + fclose(fd); + + /* get as many subsystems as possible */ + if (subsys_ynum < subsys_nnum) + strcpy(subsys, subsys_n); + + tst_resm(TINFO, "mount options: %s", subsys); +} + +static int set_xattrs(const char *file) +{ + int i, err, fail, res; + res = 0; + for (i = 0; i < ARRAY_SIZE(tkeys); ++i) { + err = setxattr(file, tkeys[i].name, + (const void *)val.buf, val.size, 0) == -1; + + fail = err && tkeys[i].good; + res |= fail; + + tst_resm((fail) ? TFAIL : TPASS, + "Expect: %s set xattr key '%s' to file '%s'", + (tkeys[i].good) ? "can" : "can't", + tkeys[i].name, file); + + if (verbose && tkeys[i].good) + tst_resm_hexd(TINFO, val.buf, val.size, "value:"); + } + return res; +} + +static int get_xattrs(const char *file) +{ + int i, fail, res; + res = 0; + for (i = 0; i < ARRAY_SIZE(tkeys); ++i) { + /* get value size */ + ssize_t size = getxattr(file, tkeys[i].name, NULL, 0); + fail = (size == -1 && tkeys[i].good); + res |= fail; + + tst_resm((fail) ? TFAIL : TPASS, + "Expect: %s read xattr %s of file '%s'", + (size == -1) ? "can't" : "can", + tkeys[i].name, file); + + if (fail || size == -1) + continue; + + /* get xattr value */ + char xval[size]; + if (getxattr(file, tkeys[i].name, xval, size) == -1) { + tst_brkm(TBROK, cleanup, + "Can't get buffer of key %s", + tkeys[i].name); + } + fail = val.size != size || + strncmp(val.buf, xval, val.size) != 0; + res |= fail; + + tst_resm((fail) ? TFAIL : TPASS, "Expect: values equal"); + + if (verbose && fail) { + tst_resm_hexd(TINFO, xval, size, + "Read xattr value:"); + tst_resm_hexd(TINFO, val.buf, val.size, + "Expect xattr value:"); + } + } + return res; +} + +static int cgrp_files_walking(const char *path, + int (*xattr_operation)(const char *)) +{ + int res = 0; + struct dirent *entry; + DIR *dir = opendir(path); + + odir[odir_num] = dir; + if (++odir_num >= MAX_OPEN_DIR) { + tst_brkm(TBROK, cleanup, + "Unexpected num of open dirs, max: %d", MAX_OPEN_DIR); + } + + SAFE_CHDIR(cleanup, path); + + tst_resm(TINFO, "In dir %s", path); + + errno = 0; + while ((entry = readdir(dir)) != NULL) { + const char *file = entry->d_name; + /* skip current and up directories */ + if (!strcmp(file, "..") || !strcmp(file, ".")) + continue; + struct stat stat_buf; + TEST(lstat(file, &stat_buf)); + if (TEST_RETURN != -1 && S_ISDIR(stat_buf.st_mode)) { + /* proceed to subdir */ + res |= cgrp_files_walking(file, xattr_operation); + tst_resm(TINFO, "In dir %s", path); + } + memset(val.buf, id++, val.size); + res |= xattr_operation(file); + errno = 0; + } + if (errno && !entry) { + tst_brkm(TWARN | TERRNO, cleanup, + "Error while reading dir '%s'", path); + } + if (closedir(dir) == -1) + tst_brkm(TWARN, cleanup, "Failed to close dir '%s'", path); + else + odir[--odir_num] = NULL; + + if (strcmp(path, ".")) + SAFE_CHDIR(cleanup, ".."); + return res; +} -- 1.7.1 |
From: Alexey K. <ale...@or...> - 2013-05-24 12:22:02
|
Support of extended attributes in cgroups was added in Linux 3.7. Signed-off-by: Alexey Kodanev <ale...@or...> --- runtest/controllers | 1 + .../kernel/controllers/cgroup_xattr/.gitignore | 1 + testcases/kernel/controllers/cgroup_xattr/Makefile | 19 + testcases/kernel/controllers/cgroup_xattr/README | 25 + .../kernel/controllers/cgroup_xattr/cgroup_xattr.c | 460 ++++++++++++++++++++ 5 files changed, 506 insertions(+), 0 deletions(-) create mode 100644 testcases/kernel/controllers/cgroup_xattr/.gitignore create mode 100644 testcases/kernel/controllers/cgroup_xattr/Makefile create mode 100644 testcases/kernel/controllers/cgroup_xattr/README create mode 100644 testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c diff --git a/runtest/controllers b/runtest/controllers index b78d828..94fc185 100644 --- a/runtest/controllers +++ b/runtest/controllers @@ -12,3 +12,4 @@ memcg_stress memcg_stress_test.sh memcg_control PAGESIZE=$(mem_process -p);memcg_control_test.sh $PAGESIZE $PAGESIZE $((PAGESIZE * 2)) cgroup_fj run_cgroup_test_fj.sh controllers test_controllers.sh +cgroup_xattr cgroup_xattr diff --git a/testcases/kernel/controllers/cgroup_xattr/.gitignore b/testcases/kernel/controllers/cgroup_xattr/.gitignore new file mode 100644 index 0000000..3664cc9 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/.gitignore @@ -0,0 +1 @@ +/cgroup_xattr diff --git a/testcases/kernel/controllers/cgroup_xattr/Makefile b/testcases/kernel/controllers/cgroup_xattr/Makefile new file mode 100644 index 0000000..cd49588 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/Makefile @@ -0,0 +1,19 @@ +# Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/controllers/cgroup_xattr/README b/testcases/kernel/controllers/cgroup_xattr/README new file mode 100644 index 0000000..42e294d --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/README @@ -0,0 +1,25 @@ +TEST SUITE: + +The directory cgroup_xattr contains the tests related to extended +attributes in cgroup filesystem. + +WARNING: + +This test can cause a kernel panic due to a bug in kernels prior to 3.8. +It was fixed by kernel upstream commit +712317ad97f41e738e1a19aa0a6392a78a84094e: + +"We should store file xattrs in struct cfent instead of struct cftype, +because cftype is a type while cfent is object instance of cftype." + +TESTS AIM: + +The aim of the tests is to check the extended attributes in cgroup +filesystem. This feature was added in Linux 3.7 to allow attaching runtime +meta information to cgroups and everything they model (services, apps, vms) +and can easily be shared among applications. + +Test creates as many subsystems as possible (cpu, cpuset, memory, ...) in +the cgroup directory and in one more created hierarchy. Then sets extended +attributes to all files in cgroup fs and subsequently reads the file's +extended attributes back, checking values during the process. diff --git a/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c b/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c new file mode 100644 index 0000000..ebf6ca4 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Alexey Kodanev <ale...@or...> + * + * Test checks following preconditions: + * since Linux kernel 3.7 it is possible to set extended attributes + * to cgroup files. + */ + +#include <sys/stat.h> +#include <sys/mount.h> +#include <sys/types.h> +#include <sys/xattr.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <dirent.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include "test.h" +#include "usctest.h" +#include "safe_macros.h" + +char *TCID = "cgroup_xattr"; + +static const char subdir_name[] = "test"; + +#define MAX_SUBSYS 16 +#define MAX_OPTIONS_LEN 256 +#define MAX_DIR_NAME 64 + +/* struct to store available mount options */ +struct cgrp_option { + char opt[MAX_OPTIONS_LEN]; + char dir[MAX_DIR_NAME]; + int hier; + int mounted; + int subdir; +}; +static struct cgrp_option cgrp_opt[MAX_SUBSYS]; +static int cgrp_opt_num; + +struct tst_key { + const char *name; + int good; +}; + +/* only security.* & trusted.* are valid key names */ +static const struct tst_key tkeys[] = { + { .name = "trusted.test", .good = 1, }, + { .name = "security.", .good = 1, }, + { .name = "user.", .good = 0, }, + { .name = "system.", .good = 0, }, +}; + +#define DEFAULT_VALUE_SIZE 8 + +/* struct to store key's value */ +struct tst_val { + char *buf; + size_t size; +}; +static struct tst_val val; + +/* it fills value's buffer */ +static char id; + +/* + * When test breaks, all open dirs should be closed + * otherwise umount won't succeed + */ +#define MAX_OPEN_DIR 32 +static DIR *odir[MAX_OPEN_DIR]; +static int odir_num; + +/* test options */ +static char *narg; +static int nflag; +static int skip_cleanup; +static int verbose; +static const option_t options[] = { + {"n:", &nflag, &narg}, + {"s", &skip_cleanup, NULL}, + {"v", &verbose, NULL}, + {NULL, NULL, NULL} +}; + +static void help(void); +static void setup(int argc, char *argv[]); +static void test_run(void); +static void cleanup(void); + +/* + * get info about mounted subsystems, + * make cgroup mount options + */ +static void make_cgroup_options(void); +static int set_xattrs(const char *file); +static int get_xattrs(const char *file); +/* + * set or get xattr recursively + * + * @path: start directory + * @xattr_operation: can be set_xattrs() or get_xattrs() + */ +static int cgrp_files_walking(const char *path, + int (*xattr_operation)(const char *)); + +int main(int argc, char *argv[]) +{ + setup(argc, argv); + + test_run(); + + cleanup(); + + tst_exit(); +} + +static void help(void) +{ + printf(" -n x Write x bytes to xattr value, default is %d\n", + DEFAULT_VALUE_SIZE); + printf(" -s Skip cleanup\n"); + printf(" -v Verbose\n"); +} + +void setup(int argc, char *argv[]) +{ + char *msg; + msg = parse_opts(argc, argv, options, help); + if (msg != NULL) + tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); + + tst_require_root(NULL); + + if (access("/proc/cgroups", F_OK) == -1) + tst_brkm(TCONF, NULL, "Kernel doesn't support for cgroups"); + + if (tst_kvercmp(3, 7, 0) < 0) { + tst_brkm(TCONF, NULL, + "Test must be run with kernel 3.7 or newer"); + } + + int value_size = DEFAULT_VALUE_SIZE; + if (nflag) { + if (sscanf(narg, "%i", &value_size) != 1) + tst_brkm(TBROK, NULL, "-n option arg is not a number"); + if (value_size <= 0) + tst_brkm(TBROK, NULL, "-n option arg is less than 1"); + } + + /* initialize test value */ + val.size = value_size; + val.buf = SAFE_MALLOC(NULL, value_size); + + tst_sig(FORK, DEF_HANDLER, cleanup); + + tst_tmpdir(); + + make_cgroup_options(); + + int any_mounted = 0; + int i; + for (i = 0; i < cgrp_opt_num; ++i) { + char dir[MAX_DIR_NAME]; + snprintf(cgrp_opt[i].dir, MAX_DIR_NAME, "cgx_%d", + cgrp_opt[i].hier); + SAFE_MKDIR(cleanup, cgrp_opt[i].dir, 0755); + if (mount(cgrp_opt[i].dir, cgrp_opt[i].dir, "cgroup", 0, + cgrp_opt[i].opt) == -1) { + tst_resm(TINFO, "Can't mount: %s", dir); + continue; + } + + any_mounted = 1; + cgrp_opt[i].mounted = 1; + + /* create new hierarchy */ + SAFE_CHDIR(cleanup, cgrp_opt[i].dir); + SAFE_MKDIR(cleanup, subdir_name, 0755); + cgrp_opt[i].subdir = 1; + SAFE_CHDIR(cleanup, ".."); + } + + if (!any_mounted) + tst_brkm(TBROK, cleanup, "Mounted nothing"); +} + +static void test_run(void) +{ + int set_res = 0, + get_res = 0, + i; + for (i = 0; i < cgrp_opt_num; ++i) { + if (!cgrp_opt[i].mounted) + continue; + + SAFE_CHDIR(cleanup, cgrp_opt[i].dir); + /* reset value */ + id = 0; + /* set xattr to each file in cgroup fs */ + set_res |= cgrp_files_walking(".", set_xattrs); + /* reset value */ + id = 0; + /* get & check xattr */ + get_res |= cgrp_files_walking(".", get_xattrs); + SAFE_CHDIR(cleanup, ".."); + } + + /* final results */ + tst_resm(TINFO, "All test-cases have been completed, summary:"); + tst_resm(TINFO, "Set tests result: %s", (set_res) ? "FAIL" : "PASS"); + tst_resm(TINFO, "Get tests result: %s", (get_res) ? "FAIL" : "PASS"); +} + +static void cleanup(void) +{ + if (val.buf != NULL) + free(val.buf); + + if (skip_cleanup) + return; + + /* + * Kernels 3.7 can crash while unmounting cgroups with xattr, + * call tst_flush() to make sure all buffered data written + * before it happens + */ + tst_flush(); + + int i; + for (i = 0; i < odir_num; ++i) { + if (closedir(odir[i]) == -1) + tst_brkm(TBROK, NULL, "Failed to close dir\n"); + } + + char *cwd = get_tst_tmpdir(); + SAFE_CHDIR(NULL, cwd); + free(cwd); + + for (i = 0; i < cgrp_opt_num; ++i) { + if (cgrp_opt[i].subdir) { + SAFE_CHDIR(NULL, cgrp_opt[i].dir); + if (rmdir(subdir_name) == -1) { + tst_brkm(TBROK | TERRNO, NULL, + "Can't remove dir"); + } + SAFE_CHDIR(NULL, ".."); + } + if (cgrp_opt[i].mounted) { + if (umount(cgrp_opt[i].dir) == -1) { + tst_brkm(TBROK | TERRNO, NULL, + "Can't unmount: %s", cgrp_opt[i].dir); + } + } + } + + tst_rmdir(); + TEST_CLEANUP; +} + +void make_cgroup_options(void) +{ + /* detect subsystems */ + FILE *fd = fopen("/proc/cgroups", "r"); + if (fd == NULL) + tst_brkm(TBROK, cleanup, "Failed to read /proc/cgroups"); + + char str[MAX_DIR_NAME]; + char name[MAX_DIR_NAME]; + int hier = 0, + num = 0, + enabled = 0, + first = 1; + + while ((fgets(str, MAX_DIR_NAME, fd)) != NULL) { + /* skip first line */ + if (first) { + first = 0; + continue; + } + if (sscanf(str, "%s\t%d\t%d\t%d", + name, &hier, &num, &enabled) != 4) + tst_brkm(TBROK, cleanup, "Can't parse /proc/cgroups"); + + if (!enabled) + continue; + + /* BUG WORKAROUND + * Only mount those subsystems, which are not mounted yet. + * It's a workaround to a bug when mount doesn't + * return any err code while mounting already mounted + * subsystems, but with additional "xattr" option. + * In that case, mount will succeed, but xattr won't be + * supported in the new mount anyway. + * Should be removed as soon as a fix committed to upstream. + */ + if (hier != 0) + continue; + /* end of workaround */ + + int found = 0; + int i; + for (i = 0; i < cgrp_opt_num; ++i) { + if (cgrp_opt[i].hier == hier) { + found = 1; + break; + } + } + + if (!found) { + i = cgrp_opt_num++; + cgrp_opt[i].hier = hier; + } + + int len = strlen(cgrp_opt[i].opt); + if (len == 0) { + strcpy(cgrp_opt[i].opt, "xattr"); + len = strlen(cgrp_opt[i].opt); + } + + sprintf(cgrp_opt[i].opt + len, ",%s", name); + } + fclose(fd); + + int i; + for (i = 0; i < cgrp_opt_num; ++i) { + tst_resm(TINFO, "mount options %d: %s (hier = %d)", + i, cgrp_opt[i].opt, cgrp_opt[i].hier); + } +} + +static int set_xattrs(const char *file) +{ + int i, err, fail, res; + res = 0; + for (i = 0; i < ARRAY_SIZE(tkeys); ++i) { + err = setxattr(file, tkeys[i].name, + (const void *)val.buf, val.size, 0) == -1; + + fail = err && tkeys[i].good; + res |= fail; + + tst_resm((fail) ? TFAIL : TPASS, + "Expect: %s set xattr key '%s' to file '%s'", + (tkeys[i].good) ? "can" : "can't", + tkeys[i].name, file); + + if (verbose && tkeys[i].good) + tst_resm_hexd(TINFO, val.buf, val.size, "value:"); + } + return res; +} + +static int get_xattrs(const char *file) +{ + int i, fail, res; + res = 0; + for (i = 0; i < ARRAY_SIZE(tkeys); ++i) { + /* get value size */ + ssize_t size = getxattr(file, tkeys[i].name, NULL, 0); + fail = (size == -1 && tkeys[i].good); + res |= fail; + + tst_resm((fail) ? TFAIL : TPASS, + "Expect: %s read xattr %s of file '%s'", + (tkeys[i].good) ? "can" : "can't", + tkeys[i].name, file); + + if (fail || size == -1) + continue; + + /* get xattr value */ + char xval[size]; + if (getxattr(file, tkeys[i].name, xval, size) == -1) { + tst_brkm(TBROK, cleanup, + "Can't get buffer of key %s", + tkeys[i].name); + } + fail = val.size != size || + strncmp(val.buf, xval, val.size) != 0; + res |= fail; + + tst_resm((fail) ? TFAIL : TPASS, "Expect: values equal"); + + if (verbose && fail) { + tst_resm_hexd(TINFO, xval, size, + "Read xattr value:"); + tst_resm_hexd(TINFO, val.buf, val.size, + "Expect xattr value:"); + } + } + return res; +} + +static int cgrp_files_walking(const char *path, + int (*xattr_operation)(const char *)) +{ + int res = 0; + struct dirent *entry; + DIR *dir = opendir(path); + + odir[odir_num] = dir; + if (++odir_num >= MAX_OPEN_DIR) { + tst_brkm(TBROK, cleanup, + "Unexpected num of open dirs, max: %d", MAX_OPEN_DIR); + } + + SAFE_CHDIR(cleanup, path); + + tst_resm(TINFO, "In dir %s", path); + + errno = 0; + while ((entry = readdir(dir)) != NULL) { + const char *file = entry->d_name; + /* skip current and up directories */ + if (!strcmp(file, "..") || !strcmp(file, ".")) + continue; + struct stat stat_buf; + TEST(lstat(file, &stat_buf)); + if (TEST_RETURN != -1 && S_ISDIR(stat_buf.st_mode)) { + /* proceed to subdir */ + res |= cgrp_files_walking(file, xattr_operation); + tst_resm(TINFO, "In dir %s", path); + } + memset(val.buf, id++, val.size); + res |= xattr_operation(file); + errno = 0; + } + if (errno && !entry) { + tst_brkm(TWARN | TERRNO, cleanup, + "Error while reading dir '%s'", path); + } + if (closedir(dir) == -1) + tst_brkm(TWARN, cleanup, "Failed to close dir '%s'", path); + else + odir[--odir_num] = NULL; + + if (strcmp(path, ".")) + SAFE_CHDIR(cleanup, ".."); + return res; +} -- 1.7.1 |
From: <ch...@su...> - 2013-05-28 14:36:03
|
Hi! > +void setup(int argc, char *argv[]) > +{ > + char *msg; > + msg = parse_opts(argc, argv, options, help); > + if (msg != NULL) > + tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); > + > + tst_require_root(NULL); > + > + if (access("/proc/cgroups", F_OK) == -1) > + tst_brkm(TCONF, NULL, "Kernel doesn't support for cgroups"); ^ ^ Either add have here or remove the for > + /* initialize test value */ > + val.size = value_size; > + val.buf = SAFE_MALLOC(NULL, value_size); > + > + tst_sig(FORK, DEF_HANDLER, cleanup); > + > + tst_tmpdir(); > + > + make_cgroup_options(); > + > + int any_mounted = 0; > + int i; > + for (i = 0; i < cgrp_opt_num; ++i) { > + char dir[MAX_DIR_NAME]; > + snprintf(cgrp_opt[i].dir, MAX_DIR_NAME, "cgx_%d", > + cgrp_opt[i].hier); > + SAFE_MKDIR(cleanup, cgrp_opt[i].dir, 0755); > + if (mount(cgrp_opt[i].dir, cgrp_opt[i].dir, "cgroup", 0, > + cgrp_opt[i].opt) == -1) { > + tst_resm(TINFO, "Can't mount: %s", dir); > + continue; > + } > + > + any_mounted = 1; > + cgrp_opt[i].mounted = 1; > + > + /* create new hierarchy */ > + SAFE_CHDIR(cleanup, cgrp_opt[i].dir); > + SAFE_MKDIR(cleanup, subdir_name, 0755); > + cgrp_opt[i].subdir = 1; > + SAFE_CHDIR(cleanup, ".."); > + } I would keep the mounting code in the function that parses the cgroups proc file, that way we can have simple: if (!mount_cgroups()) tst_brkm(TBROK, cleanup, "Nothing mounted"); in the setup and keep all the details in the function. > + if (!any_mounted) > + tst_brkm(TBROK, cleanup, "Mounted nothing"); This should be TCONF. > +} > + > +void make_cgroup_options(void) > +{ > + /* detect subsystems */ > + FILE *fd = fopen("/proc/cgroups", "r"); > + if (fd == NULL) > + tst_brkm(TBROK, cleanup, "Failed to read /proc/cgroups"); > + > + char str[MAX_DIR_NAME]; > + char name[MAX_DIR_NAME]; > + int hier = 0, > + num = 0, > + enabled = 0, > + first = 1; I would keep these at one line, which would save vertical space, but that is purely cosmetic change. > + while ((fgets(str, MAX_DIR_NAME, fd)) != NULL) { > + /* skip first line */ > + if (first) { > + first = 0; > + continue; > + } > + if (sscanf(str, "%s\t%d\t%d\t%d", > + name, &hier, &num, &enabled) != 4) > + tst_brkm(TBROK, cleanup, "Can't parse /proc/cgroups"); > + > + if (!enabled) > + continue; > + > + /* BUG WORKAROUND > + * Only mount those subsystems, which are not mounted yet. > + * It's a workaround to a bug when mount doesn't > + * return any err code while mounting already mounted > + * subsystems, but with additional "xattr" option. > + * In that case, mount will succeed, but xattr won't be > + * supported in the new mount anyway. > + * Should be removed as soon as a fix committed to upstream. > + */ > + if (hier != 0) > + continue; > + /* end of workaround */ Remove the /* end of workaround */ please. > + int found = 0; > + int i; > + for (i = 0; i < cgrp_opt_num; ++i) { > + if (cgrp_opt[i].hier == hier) { > + found = 1; > + break; > + } > + } > + > + if (!found) { > + i = cgrp_opt_num++; > + cgrp_opt[i].hier = hier; > + } > + > + int len = strlen(cgrp_opt[i].opt); > + if (len == 0) { Could also be if (cgrp_opt[i].opt[0] == '\0') { (or something similar) > + strcpy(cgrp_opt[i].opt, "xattr"); > + len = strlen(cgrp_opt[i].opt); len == 5 in this case ;) > + } > + > + sprintf(cgrp_opt[i].opt + len, ",%s", name); So we create a list of subsystem by hierarchy here, and try to mount them with xattr later? Ah but due to bug in kernel (hier == 0 when we got to this part) we end up only with these that are not mounted at the moment, right? That looks OK. Hopefully the kernel gets fixed. Will the kernel support mouting the subsystems with additional xattr flags or will the mount return an error (on my machine all mounted subsystems are mounted without xattr)? > + } > + fclose(fd); > + > + int i; > + for (i = 0; i < cgrp_opt_num; ++i) { > + tst_resm(TINFO, "mount options %d: %s (hier = %d)", > + i, cgrp_opt[i].opt, cgrp_opt[i].hier); > + } > +} > + -- Cyril Hrubis ch...@su... |
From: <ale...@or...> - 2013-05-28 15:00:21
|
Hi >> + } >> + >> + sprintf(cgrp_opt[i].opt + len, ",%s", name); > So we create a list of subsystem by hierarchy here, and try to mount > them with xattr later? That's right > Ah but due to bug in kernel (hier == 0 when we got to this part) we end > up only with these that are not mounted at the moment, right? Yes > That looks OK. Hopefully the kernel gets fixed. Will the kernel support > mouting the subsystems with additional xattr flags or will the mount > return an error (on my machine all mounted subsystems are mounted > without xattr)? > It's difficult to say, for now, we have a recent upstream commit <http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=873fe09ea5df6ccf6bb34811d8c9992aacb67598> in which new cgroup mount option introduced (__DEVEL__sane_behavior). It can be used to get mount error. Developer says that this bug is a part of largerbreakage - cgroup silently ignores mount option changes whenremounted. He thinks that fixing only this part is not meaningful, suggesting to add warning message if the mount options mismatch. Thanks, Alexey |
From: <ch...@su...> - 2013-05-28 15:42:21
|
Hi! > > So we create a list of subsystem by hierarchy here, and try to mount > > them with xattr later? > That's right > > Ah but due to bug in kernel (hier == 0 when we got to this part) we end > > up only with these that are not mounted at the moment, right? > Yes > > That looks OK. Hopefully the kernel gets fixed. Will the kernel support > > mouting the subsystems with additional xattr flags or will the mount > > return an error (on my machine all mounted subsystems are mounted > > without xattr)? > > > It's difficult to say, for now, we have a recent upstream commit > <http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=873fe09ea5df6ccf6bb34811d8c9992aacb67598> > in which new cgroup mount option introduced (__DEVEL__sane_behavior). It > can be used to get mount error. Developer says that this bug is a part > of largerbreakage - cgroup silently ignores mount option changes > whenremounted. He thinks that fixing only this part is not meaningful, > suggesting to add warning message if the mount options mismatch. OK. Lets get keep the test as it is now. We will fix it later, once the kernel devs figure out their solution. -- Cyril Hrubis ch...@su... |
From: <ale...@or...> - 2013-05-29 11:33:00
|
Hi All, I'm developing a test of device firmware loading (after 3.7 it can be loaded directly or as usual), but I'm not sure under which ltp directory it should be placed. Is it OK if I use two locations as described below? .../testcases/kernel/device-drivers/firmware/fw_load_kernel/fw_load.ko - kernel space part (calls request_firmware with specified parameters), and .../testcases/kernel/device-drivers/firmware/fw_load_user/fw_load - user space part (creates firmware files, replaces udev's firmware searched paths, loads the module, prints results...). Would appreciate any comments. Thanks, Alexey |
From: <ch...@su...> - 2013-05-30 11:06:44
|
Hi! > I'm developing a test of device firmware loading (after 3.7 it can be > loaded directly or as usual), but I'm not sure under which ltp directory > it should be placed. Is it OK if I use two locations as described below? > > .../testcases/kernel/device-drivers/firmware/fw_load_kernel/fw_load.ko - > kernel space part (calls request_firmware with specified parameters), and > > .../testcases/kernel/device-drivers/firmware/fw_load_user/fw_load - user > space part (creates firmware files, replaces udev's firmware searched > paths, loads the module, prints results...). The problem with device-drivers directory is that the code there is broken (unmaintained since 2009 or so) and the make (from the top directory) doesn't go there. It should be cleaned/removed/fixed but nobody had time to look at the tests and decide what to do with them. Otherwise the destination fits well enough. And there is another problem with building kernel modules, I think that we don't have any configure checks to find out if kernel-devel is installed (i.e. /lib/modules/$(version)/build/ exists) and which kernel version to use, etc. But this part shouldn't be that hard. -- Cyril Hrubis ch...@su... |
From: <ale...@or...> - 2013-05-30 12:37:33
|
Hi! >> I'm developing a test of device firmware loading (after 3.7 it can be >> loaded directly or as usual), but I'm not sure under which ltp directory >> it should be placed. Is it OK if I use two locations as described below? >> >> .../testcases/kernel/device-drivers/firmware/fw_load_kernel/fw_load.ko - >> kernel space part (calls request_firmware with specified parameters), and >> >> .../testcases/kernel/device-drivers/firmware/fw_load_user/fw_load - user >> space part (creates firmware files, replaces udev's firmware searched >> paths, loads the module, prints results...). > The problem with device-drivers directory is that the code there is > broken (unmaintained since 2009 or so) and the make (from the top > directory) doesn't go there. It should be cleaned/removed/fixed but > nobody had time to look at the tests and decide what to do with them. > > Otherwise the destination fits well enough. As I understand from README file in that directory, these tests should not be run automatically. It explains why make doesn't go there. But the firmware loading test can be (preferably should be) run automatically. That way, may be change directory to .../testcases/kernel/firmware? > And there is another problem with building kernel modules, I think that > we don't have any configure checks to find out if kernel-devel is > installed (i.e. /lib/modules/$(version)/build/ exists) and which kernel > version to use, etc. But this part shouldn't be that hard. > OK, it can be fixed. |
From: <ch...@su...> - 2013-05-30 13:12:22
|
Hi! > > The problem with device-drivers directory is that the code there is > > broken (unmaintained since 2009 or so) and the make (from the top > > directory) doesn't go there. It should be cleaned/removed/fixed but > > nobody had time to look at the tests and decide what to do with them. > > > > Otherwise the destination fits well enough. > As I understand from README file in that directory, these tests should > not be run automatically. It explains why make doesn't go there. But the > firmware loading test can be (preferably should be) run automatically. > That way, may be change directory to .../testcases/kernel/firmware? Hmm, so the test tests userspace API for firmware loading but needs helper kernel module, am I right?. In that case kernel/firmware/ should be ok as well. -- Cyril Hrubis ch...@su... |
From: <ale...@or...> - 2013-05-30 13:28:54
|
>> As I understand from README file in that directory, these tests should >> not be run automatically. It explains why make doesn't go there. But the >> firmware loading test can be (preferably should be) run automatically. >> That way, may be change directory to .../testcases/kernel/firmware? > Hmm, so the test tests userspace API for firmware loading but needs > helper kernel module, am I right?. In that case kernel/firmware/ should > be ok as well. > You're right, it needs helper kernel module, but test tests kernelspace API (request_firmware() func) and also udev, which runs in userspace. |
From: Alexey K. <ale...@or...> - 2013-05-29 10:29:20
|
Support of extended attributes in cgroups was added in Linux 3.7. Signed-off-by: Alexey Kodanev <ale...@or...> --- runtest/controllers | 1 + .../kernel/controllers/cgroup_xattr/.gitignore | 1 + testcases/kernel/controllers/cgroup_xattr/Makefile | 19 + testcases/kernel/controllers/cgroup_xattr/README | 25 ++ .../kernel/controllers/cgroup_xattr/cgroup_xattr.c | 435 ++++++++++++++++++++ 5 files changed, 481 insertions(+), 0 deletions(-) create mode 100644 testcases/kernel/controllers/cgroup_xattr/.gitignore create mode 100644 testcases/kernel/controllers/cgroup_xattr/Makefile create mode 100644 testcases/kernel/controllers/cgroup_xattr/README create mode 100644 testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c diff --git a/runtest/controllers b/runtest/controllers index b78d828..94fc185 100644 --- a/runtest/controllers +++ b/runtest/controllers @@ -12,3 +12,4 @@ memcg_stress memcg_stress_test.sh memcg_control PAGESIZE=$(mem_process -p);memcg_control_test.sh $PAGESIZE $PAGESIZE $((PAGESIZE * 2)) cgroup_fj run_cgroup_test_fj.sh controllers test_controllers.sh +cgroup_xattr cgroup_xattr diff --git a/testcases/kernel/controllers/cgroup_xattr/.gitignore b/testcases/kernel/controllers/cgroup_xattr/.gitignore new file mode 100644 index 0000000..3664cc9 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/.gitignore @@ -0,0 +1 @@ +/cgroup_xattr diff --git a/testcases/kernel/controllers/cgroup_xattr/Makefile b/testcases/kernel/controllers/cgroup_xattr/Makefile new file mode 100644 index 0000000..cd49588 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/Makefile @@ -0,0 +1,19 @@ +# Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/controllers/cgroup_xattr/README b/testcases/kernel/controllers/cgroup_xattr/README new file mode 100644 index 0000000..5aff0a8 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/README @@ -0,0 +1,25 @@ +TEST SUITE: + +The directory cgroup_xattr contains the tests related to extended +attributes in cgroup filesystem. + +WARNING: + +This test can cause a kernel panic due to a bug in kernels prior to 3.8. +It was fixed by kernel upstream commit +712317ad97f41e738e1a19aa0a6392a78a84094e: + +"We should store file xattrs in struct cfent instead of struct cftype, +because cftype is a type while cfent is object instance of cftype." + +TESTS AIM: + +The aim of the tests is to check the extended attributes in cgroup +filesystem. This feature was added in Linux 3.7 to allow attaching runtime +meta information to cgroups and everything they model (services, apps, vms) +and can easily be shared among applications. + +Test mounts as many subsystems as possible (cpu, cpuset, ...) in the +cgroup tmp directory and creates one more hierarchy. Then sets extended +attributes to all files in cgroup fs and subsequently reads the file's +extended attributes back, checking values during the process. diff --git a/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c b/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c new file mode 100644 index 0000000..20473d4 --- /dev/null +++ b/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c @@ -0,0 +1,435 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Alexey Kodanev <ale...@or...> + * + * Test checks following preconditions: + * since Linux kernel 3.7 it is possible to set extended attributes + * to cgroup files. + */ + +#include <sys/stat.h> +#include <sys/mount.h> +#include <sys/types.h> +#include <sys/xattr.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <dirent.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include "test.h" +#include "usctest.h" +#include "safe_macros.h" + +char *TCID = "cgroup_xattr"; + +static const char subdir_name[] = "test"; + +#define MAX_SUBSYS 16 +#define MAX_OPTIONS_LEN 256 +#define MAX_DIR_NAME 64 + +/* struct to store available mount options */ +struct cgrp_option { + char str[MAX_OPTIONS_LEN]; + char dir[MAX_DIR_NAME]; + int hier; + int mounted; + int subdir; +}; +static struct cgrp_option cgrp_opt[MAX_SUBSYS]; +static int cgrp_opt_num; + +struct tst_key { + const char *name; + int good; +}; + +/* only security.* & trusted.* are valid key names */ +static const struct tst_key tkeys[] = { + { .name = "trusted.test", .good = 1, }, + { .name = "security.", .good = 1, }, + { .name = "user.", .good = 0, }, + { .name = "system.", .good = 0, }, +}; + +#define DEFAULT_VALUE_SIZE 8 + +/* struct to store key's value */ +struct tst_val { + char *buf; + size_t size; +}; +static struct tst_val val; + +/* it fills value's buffer */ +static char id; + +/* + * When test breaks, all open dirs should be closed + * otherwise umount won't succeed + */ +#define MAX_OPEN_DIR 32 +static DIR *odir[MAX_OPEN_DIR]; +static int odir_num; + +/* test options */ +static char *narg; +static int nflag; +static int skip_cleanup; +static int verbose; +static const option_t options[] = { + {"n:", &nflag, &narg}, + {"s", &skip_cleanup, NULL}, + {"v", &verbose, NULL}, + {NULL, NULL, NULL} +}; + +static void help(void); +static void setup(int argc, char *argv[]); +static void test_run(void); +static void cleanup(void); + +static int mount_cgroup(void); +static int set_xattrs(const char *file); +static int get_xattrs(const char *file); +/* + * set or get xattr recursively + * + * @path: start directory + * @xattr_operation: can be set_xattrs() or get_xattrs() + */ +static int cgrp_files_walking(const char *path, + int (*xattr_operation)(const char *)); + +int main(int argc, char *argv[]) +{ + setup(argc, argv); + + test_run(); + + cleanup(); + + tst_exit(); +} + +static void help(void) +{ + printf(" -n x Write x bytes to xattr value, default is %d\n", + DEFAULT_VALUE_SIZE); + printf(" -s Skip cleanup\n"); + printf(" -v Verbose\n"); +} + +void setup(int argc, char *argv[]) +{ + char *msg; + msg = parse_opts(argc, argv, options, help); + if (msg != NULL) + tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); + + tst_require_root(NULL); + + if (access("/proc/cgroups", F_OK) == -1) + tst_brkm(TCONF, NULL, "Kernel doesn't support cgroups"); + + if (tst_kvercmp(3, 7, 0) < 0) { + tst_brkm(TCONF, NULL, + "Test must be run with kernel 3.7 or newer"); + } + + int value_size = DEFAULT_VALUE_SIZE; + if (nflag) { + if (sscanf(narg, "%i", &value_size) != 1) + tst_brkm(TBROK, NULL, "-n option arg is not a number"); + if (value_size <= 0) + tst_brkm(TBROK, NULL, "-n option arg is less than 1"); + } + + /* initialize test value */ + val.size = value_size; + val.buf = SAFE_MALLOC(NULL, value_size); + + tst_sig(FORK, DEF_HANDLER, cleanup); + + tst_tmpdir(); + + if (!mount_cgroup()) + tst_brkm(TCONF, cleanup, "Nothing mounted"); +} + +static void test_run(void) +{ + int i, set_res = 0, get_res = 0; + + for (i = 0; i < cgrp_opt_num; ++i) { + if (!cgrp_opt[i].mounted) + continue; + + SAFE_CHDIR(cleanup, cgrp_opt[i].dir); + /* reset value */ + id = 0; + /* set xattr to each file in cgroup fs */ + set_res |= cgrp_files_walking(".", set_xattrs); + + id = 0; + /* get & check xattr */ + get_res |= cgrp_files_walking(".", get_xattrs); + SAFE_CHDIR(cleanup, ".."); + } + + /* final results */ + tst_resm(TINFO, "All test-cases have been completed, summary:"); + tst_resm(TINFO, "Set tests result: %s", (set_res) ? "FAIL" : "PASS"); + tst_resm(TINFO, "Get tests result: %s", (get_res) ? "FAIL" : "PASS"); +} + +static void cleanup(void) +{ + if (val.buf != NULL) + free(val.buf); + + if (skip_cleanup) + return; + + /* + * Kernels 3.7 can crash while unmounting cgroups with xattr, + * call tst_flush() to make sure all buffered data written + * before it happens + */ + tst_flush(); + + int i; + for (i = 0; i < odir_num; ++i) { + if (closedir(odir[i]) == -1) + tst_brkm(TBROK, NULL, "Failed to close dir\n"); + } + + char *cwd = get_tst_tmpdir(); + SAFE_CHDIR(NULL, cwd); + free(cwd); + + for (i = 0; i < cgrp_opt_num; ++i) { + if (cgrp_opt[i].subdir) { + SAFE_CHDIR(NULL, cgrp_opt[i].dir); + if (rmdir(subdir_name) == -1) { + tst_brkm(TBROK | TERRNO, NULL, + "Can't remove dir"); + } + SAFE_CHDIR(NULL, ".."); + } + if (cgrp_opt[i].mounted) { + if (umount(cgrp_opt[i].dir) == -1) { + tst_brkm(TBROK | TERRNO, NULL, + "Can't unmount: %s", cgrp_opt[i].dir); + } + } + } + + tst_rmdir(); + TEST_CLEANUP; +} + +int mount_cgroup(void) +{ + FILE *fd = fopen("/proc/cgroups", "r"); + if (fd == NULL) + tst_brkm(TBROK, cleanup, "Failed to read /proc/cgroups"); + char str[MAX_DIR_NAME], name[MAX_DIR_NAME]; + int hier = 0, num = 0, enabled = 0, first = 1; + /* make mount options */ + while ((fgets(str, MAX_DIR_NAME, fd)) != NULL) { + /* skip first line */ + if (first) { + first = 0; + continue; + } + if (sscanf(str, "%s\t%d\t%d\t%d", + name, &hier, &num, &enabled) != 4) + tst_brkm(TBROK, cleanup, "Can't parse /proc/cgroups"); + if (!enabled) + continue; + + /* BUG WORKAROUND + * Only mount those subsystems, which are not mounted yet. + * It's a workaround to a bug when mount doesn't return any err + * code while mounting already mounted subsystems, but with + * additional "xattr" option. In that case, mount will succeed, + * but xattr won't be supported in the new mount anyway. + * Should be removed as soon as a fix committed to upstream. + */ + if (hier != 0) + continue; + + int i, found = 0; + for (i = 0; i < cgrp_opt_num; ++i) { + if (cgrp_opt[i].hier == hier) { + found = 1; + break; + } + } + if (!found) { + i = cgrp_opt_num++; + cgrp_opt[i].hier = hier; + } + char *str = cgrp_opt[i].str; + if (str[0] == '\0') + strcpy(str, "xattr"); + snprintf(str + strlen(str), MAX_OPTIONS_LEN - strlen(str), + ",%s", name); + } + fclose(fd); + + int i, any_mounted = 0; + for (i = 0; i < cgrp_opt_num; ++i) { + char dir[MAX_DIR_NAME]; + struct cgrp_option *opt = &cgrp_opt[i]; + tst_resm(TINFO, "mount options %d: %s (hier = %d)", + i, opt->str, opt->hier); + snprintf(opt->dir, MAX_DIR_NAME, "cgx_%d", opt->hier); + SAFE_MKDIR(cleanup, opt->dir, 0755); + + if (mount(opt->dir, opt->dir, "cgroup", 0, opt->str) == -1) { + tst_resm(TINFO, "Can't mount: %s", dir); + continue; + } + + any_mounted = 1; + opt->mounted = 1; + + /* create new hierarchy */ + SAFE_CHDIR(cleanup, opt->dir); + SAFE_MKDIR(cleanup, subdir_name, 0755); + opt->subdir = 1; + SAFE_CHDIR(cleanup, ".."); + } + return any_mounted; +} + +static int set_xattrs(const char *file) +{ + int i, err, fail, res = 0; + + for (i = 0; i < ARRAY_SIZE(tkeys); ++i) { + err = setxattr(file, tkeys[i].name, + (const void *)val.buf, val.size, 0) == -1; + + fail = err && tkeys[i].good; + res |= fail; + + tst_resm((fail) ? TFAIL : TPASS, + "Expect: %s set xattr key '%s' to file '%s'", + (tkeys[i].good) ? "can" : "can't", + tkeys[i].name, file); + + if (verbose && tkeys[i].good) + tst_resm_hexd(TINFO, val.buf, val.size, "value:"); + } + return res; +} + +static int get_xattrs(const char *file) +{ + int i, fail, res = 0; + + for (i = 0; i < ARRAY_SIZE(tkeys); ++i) { + /* get value size */ + ssize_t size = getxattr(file, tkeys[i].name, NULL, 0); + fail = (size == -1 && tkeys[i].good); + res |= fail; + + tst_resm((fail) ? TFAIL : TPASS, + "Expect: %s read xattr %s of file '%s'", + (tkeys[i].good) ? "can" : "can't", + tkeys[i].name, file); + + if (fail || size == -1) + continue; + + /* get xattr value */ + char xval[size]; + if (getxattr(file, tkeys[i].name, xval, size) == -1) { + tst_brkm(TBROK, cleanup, + "Can't get buffer of key %s", + tkeys[i].name); + } + fail = val.size != size || + strncmp(val.buf, xval, val.size) != 0; + res |= fail; + + tst_resm((fail) ? TFAIL : TPASS, "Expect: values equal"); + + if (verbose && fail) { + tst_resm_hexd(TINFO, xval, size, + "Read xattr value:"); + tst_resm_hexd(TINFO, val.buf, val.size, + "Expect xattr value:"); + } + } + return res; +} + +static int cgrp_files_walking(const char *path, + int (*xattr_operation)(const char *)) +{ + int res = 0; + struct dirent *entry; + DIR *dir = opendir(path); + + odir[odir_num] = dir; + if (++odir_num >= MAX_OPEN_DIR) { + tst_brkm(TBROK, cleanup, + "Unexpected num of open dirs, max: %d", MAX_OPEN_DIR); + } + + SAFE_CHDIR(cleanup, path); + + tst_resm(TINFO, "In dir %s", path); + + errno = 0; + while ((entry = readdir(dir)) != NULL) { + const char *file = entry->d_name; + /* skip current and up directories */ + if (!strcmp(file, "..") || !strcmp(file, ".")) + continue; + struct stat stat_buf; + TEST(lstat(file, &stat_buf)); + if (TEST_RETURN != -1 && S_ISDIR(stat_buf.st_mode)) { + /* proceed to subdir */ + res |= cgrp_files_walking(file, xattr_operation); + tst_resm(TINFO, "In dir %s", path); + } + memset(val.buf, id++, val.size); + res |= xattr_operation(file); + errno = 0; + } + if (errno && !entry) { + tst_brkm(TWARN | TERRNO, cleanup, + "Error while reading dir '%s'", path); + } + if (closedir(dir) == -1) + tst_brkm(TWARN, cleanup, "Failed to close dir '%s'", path); + else + odir[--odir_num] = NULL; + + if (strcmp(path, ".")) + SAFE_CHDIR(cleanup, ".."); + return res; +} -- 1.7.1 |
From: <ch...@su...> - 2013-05-30 18:54:30
|
Hi! > Support of extended attributes in cgroups was added in Linux 3.7. > > Signed-off-by: Alexey Kodanev <ale...@or...> > --- > runtest/controllers | 1 + > .../kernel/controllers/cgroup_xattr/.gitignore | 1 + > testcases/kernel/controllers/cgroup_xattr/Makefile | 19 + > testcases/kernel/controllers/cgroup_xattr/README | 25 ++ > .../kernel/controllers/cgroup_xattr/cgroup_xattr.c | 435 ++++++++++++++++++++ > 5 files changed, 481 insertions(+), 0 deletions(-) > create mode 100644 testcases/kernel/controllers/cgroup_xattr/.gitignore > create mode 100644 testcases/kernel/controllers/cgroup_xattr/Makefile > create mode 100644 testcases/kernel/controllers/cgroup_xattr/README > create mode 100644 testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c Pushed after removing trailing whitespace from the Makefile, thanks. -- Cyril Hrubis ch...@su... |
From: <ch...@su...> - 2013-05-15 12:37:21
|
Hi! > +++ b/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c > @@ -0,0 +1,343 @@ > +/* > + * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License as > + * published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it would be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write the Free Software Foundation, > + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > + * > + * Author: > + * Alexey Kodanev <ale...@or...> > + * > + * Test checks following preconditions: > + * since Linux kernel 3.7 it is possible to set extended attributes > + * to cgroup files. > + */ > + > +#include <sys/stat.h> > +#include <sys/mount.h> > +#include <sys/types.h> > +#include <sys/xattr.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <fcntl.h> > +#include <dirent.h> > +#include <unistd.h> > +#include <string.h> > +#include <errno.h> > + > +#include "test.h" > +#include "usctest.h" > +#include "safe_macros.h" > + > +char *TCID = "cgroup_xattr"; > + > +static const char cgrp_point[] = "/sys/fs/cgroup"; > +static const char cgrp_name[] = "cgrp_test"; > +static const char subdir_name[] = "test"; > + > +struct tst_key { > + const char *name; > + int good; > +}; > + > +/* only security.* & trusted.* is valid key names */ ^ are > +static const struct tst_key tkeys[] = { > + { .name = "trusted.test", .good = 1, }, > + { .name = "security.", .good = 1, }, > + { .name = "user.", .good = 0, }, > + { .name = "system.", .good = 0, }, > +}; > + > +#define VALUE_SIZE 8 > + > +/* > + * values that can be written to xattr keys, > + * their can be anything ^ their value? > + */ > +struct tst_val { > + char buf[VALUE_SIZE]; > + size_t size; > +}; > + > +/* cleanup flags */ > +static int cgrp_mounted; > +static int subdir_created; > + > +/* test options */ > +static int skip_cleanup; > +static int verbose; > +static const option_t options[] = { > + {"s", &skip_cleanup, NULL}, > + {"v", &verbose, NULL}, > + {NULL, NULL, NULL} > +}; > + > +/* save to change back in the end */ > +static char start_work_dir[PATH_MAX]; > + > +static void help(void); > +static void setup(int argc, char *argv[]); > +static void test_run(void); > +static void cleanup(void); > + > +static int set_xattrs(const char *file, const struct tst_val *val); > +static char *get_xattr(const char *file, const char *key_name, size_t *size); > +static int get_xattrs(const char *file, const struct tst_val *val); > +/* > + * set or get xattr recursively > + * > + * @path: start directory > + * @id: start char to fill in value > + * @xattr_operation: can be set_xattrs() or get_xattrs() > + */ > +static int cgrp_files_walking(const char *path, char *id, > + int (*xattr_operation)(const char *, const struct tst_val *)); > +static char *get_hex_value(const char *value, size_t size); > +static void fill_test_value(char *id, struct tst_val *val); > + > + > +int main(int argc, char *argv[]) > +{ > + setup(argc, argv); > + > + test_run(); > + > + cleanup(); > + > + tst_exit(); > +} > + > +static void help(void) > +{ > + printf(" -s Skip cleanup\n"); > + printf(" -v Verbose\n"); > +} > + > +void setup(int argc, char *argv[]) > +{ > + char *msg; > + msg = parse_opts(argc, argv, options, help); > + if (msg != NULL) > + tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); > + > + tst_require_root(NULL); > + > + if (access("/proc/cgroups", F_OK) == -1) > + tst_brkm(TCONF, cleanup, "Kernel doesn't support for cgroups"); > + > + if (tst_kvercmp(3, 7, 0) < 0) { > + tst_brkm(TCONF, cleanup, > + "Test must be run with kernel 3.7 or newer"); > + } > + > + tst_sig(FORK, DEF_HANDLER, cleanup); > + > + /* mount all available subsystems (cpu, cpuset, memory, ...) */ > + if (mount(cgrp_name, cgrp_point, "cgroup", 0, "xattr") == -1) > + tst_brkm(TBROK, cleanup, "Can't mount: %s", cgrp_point); Here we mount cgroups under /sys/fs/cgroup which later causes problems as you need to switch to another directory to unmount. I'm not that much familiar with cgroups, is mounting it to /sys/ required or is it customary? Wouldn't that interfere with rest of the system? What about calling tst_tmpdir(), creating a directory to mount the cgroups there and then simply doing chdir to test temporary directory before the unmount. Would that work? > + cgrp_mounted = 1; > + > + /* save current working directory */ > + SAFE_GETCWD(cleanup, start_work_dir, PATH_MAX); > + SAFE_CHDIR(cleanup, cgrp_point); > + > + /* create new hierarchy */ > + SAFE_MKDIR(cleanup, subdir_name, 0755); > + subdir_created = 1; > +} > + > +static void test_run() void in params is missing > +{ > + char id; > + /* set xattr to each file in cgroup fs */ > + id = 0; > + int set_res = cgrp_files_walking(cgrp_point, &id, set_xattrs); Here you are passing pointer to the value, modify it by the function but never use the resulting value. > + /* get & check xattr from each file in cgroup fs */ > + id = 0; > + int get_res = cgrp_files_walking(cgrp_point, &id, get_xattrs); > + > + /* final results */ > + tst_resm(TINFO, "All test-cases have been completed, summary:"); > + tst_resm(TINFO, "Set tests result: %s", (set_res) ? "FAIL" : "PASS"); > + tst_resm(TINFO, "Get tests result: %s", (get_res) ? "FAIL" : "PASS"); > +} > + > +static void cleanup(void) > +{ > + if (skip_cleanup) > + return; > + > + fflush(stdout); What is this fflush() for? > + if (subdir_created) { > + SAFE_CHDIR(NULL, cgrp_point); > + if (rmdir(subdir_name) == -1) { > + tst_brkm(TBROK, NULL, "Can't remove dir, error: %s", > + strerror(errno)); > + } > + } > + > + if (strlen(start_work_dir) > 0) > + SAFE_CHDIR(NULL, start_work_dir); > + > + if (cgrp_mounted) { > + if (umount(cgrp_point) == -1) > + tst_brkm(TBROK, NULL, "Can't unmount: %s", cgrp_point); > + } > + > + TEST_CLEANUP; > +} > + > +static int set_xattrs(const char *file, const struct tst_val *val) > +{ > + int i, res; > + res = 0; > + for (i = 0; i < ARRAY_SIZE(tkeys); ++i) { > + int good = setxattr(file, tkeys[i].name, > + (const void *)val->buf, val->size, 0) != -1; > + > + int fail = good != tkeys[i].good; > + res |= fail; > + > + tst_resm((fail) ? TFAIL : TPASS, > + "Expect: %s set xattr key '%s' to file '%s'", > + (tkeys[i].good) ? "can" : "can't", > + tkeys[i].name, file); > + > + if (verbose && tkeys[i].good) { > + char *rval = get_hex_value(val->buf, val->size); > + tst_resm(TINFO, "value =%s", rval); > + free(rval); > + } > + } > + return res; > +} > + > +static char *get_xattr(const char *file, const char *key_name, size_t *size) > +{ > + char *xval = NULL; > + /* get value size */ > + ssize_t xval_size = getxattr(file, key_name, (void *)xval, 0); Pass NULL here, instead of the xval initialized to NULL. > + if (xval_size == -1) > + return NULL; > + > + xval = SAFE_MALLOC(cleanup, xval_size); > + > + if (getxattr(file, key_name, (void *)xval, xval_size) == -1) { > + free(xval); > + return NULL; > + } > + *size = xval_size; > + return xval; > +} > + > +static int get_xattrs(const char *file, const struct tst_val *val) > +{ > + int i, fail, res; > + res = 0; > + for (i = 0; i < ARRAY_SIZE(tkeys); ++i) { > + size_t xval_size = 0; > + char *xval; > + > + xval = get_xattr(file, tkeys[i].name, &xval_size); > + fail = (xval == NULL && tkeys[i].good); > + res |= fail; > + > + tst_resm((fail) ? TFAIL : TPASS, > + "Expect: %s read xattr %s of file '%s'", > + (xval == NULL) ? "can't" : "can", tkeys[i].name, file); > + if (xval == NULL) > + continue; > + > + if (fail) { > + free(xval); > + continue; > + } > + > + fail |= val->size != xval_size || > + strncmp(val->buf, xval, val->size) != 0; > + res |= fail; > + > + tst_resm((fail) ? TFAIL : TPASS, "Expect: values equal"); > + > + if (verbose && fail) { > + char *rval = get_hex_value(xval, xval_size); > + tst_resm(TINFO, "Read xattr value:%s", rval); > + free(rval); > + char *cval = get_hex_value(val->buf, val->size); > + tst_resm(TINFO, "Expected value:%s", cval); > + free(cval); > + } > + free(xval); > + } > + return res; > +} > + > +static int cgrp_files_walking(const char *path, char *id, > + int (*xattr_operation)(const char *, const struct tst_val *)) > +{ > + int res = 0; > + struct dirent *entry; > + DIR *dir; > + dir = opendir(path); > + SAFE_CHDIR(cleanup, path); > + tst_resm(TINFO, "In dir %s", path); > + errno = 0; > + while ((entry = readdir(dir)) != NULL) { > + const char *file = entry->d_name; > + /* skip current and up directories */ > + if (!strcmp(file, "..") || !strcmp(file, ".")) > + continue; > + > + struct stat stat_buf; > + TEST(lstat(file, &stat_buf)); > + if (TEST_RETURN != -1 && S_ISDIR(stat_buf.st_mode)) { > + /* proceed to subdir */ > + res |= cgrp_files_walking(file, id, xattr_operation); > + /* change directory back */ > + SAFE_CHDIR(cleanup, ".."); > + tst_resm(TINFO, "In dir %s", path); > + } > + struct tst_val val; > + fill_test_value(id, &val); > + res |= xattr_operation(file, &val); > + errno = 0; > + } > + if (errno && !entry) > + tst_brkm(TWARN, cleanup, "%s", strerror(errno)); Use TERRNO and also describe the warning more verbosely. > + if (closedir(dir) == -1) > + tst_brkm(TWARN, cleanup, "Failed to close dir '%s'", path); > + > + return res; > +} > + > +static char *get_hex_value(const char *value, size_t size) > +{ > + const size_t symb_num = 5; /* space + 0xXX*/ > + char *buf = SAFE_MALLOC(cleanup, size * symb_num + 1); > + size_t i; > + for (i = 0; i < size; ++i) { > + sprintf(buf + i * symb_num, " 0x%02X", > + (unsigned char) *(value++)); > + } > + return buf; > +} This function is too ugly. You should rather do something like print_xattr() that would both preprare the string and call the tst_resm() so that you don't need to pass allocated buffers around. void print_xattr(const char *msg, const char *val, size_t size) { char buf[size + sybm_num + 1]; ... tst_resm(TINFO, "%s%s", msg, buf); } Or we can create a tst_xxx function to print a buffer of bytes, I guess that there are more cases where such function could be used. But that would require a little more work to desing the interface right. > +static void fill_test_value(char *id, struct tst_val *val) > +{ > + int i; > + for (i = 0; i < VALUE_SIZE; ++i) > + val->buf[i] = *id; What about using memset()? > + val->size = VALUE_SIZE; > + ++(*id); This is quite cryptic, why aren't you modifying the id in the loop that calls this function? What about just defining the id in the walk functions and incrementing it each time fill_test_value() was called? > +} -- Cyril Hrubis ch...@su... |
From: Jan S. <jst...@re...> - 2013-05-15 13:10:34
|
----- Original Message ----- > From: ch...@su... > To: "Alexey Kodanev" <ale...@or...> > Cc: "vasily isaenko" <vas...@or...>, ltp...@li... > Sent: Wednesday, 15 May, 2013 2:38:26 PM > Subject: Re: [LTP] [PATCH] New core test of cgroups and extended attributes > > Hi! > > +++ b/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c > > @@ -0,0 +1,343 @@ > > +/* > > + * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. > > + * > > + * This program is free software; you can redistribute it and/or > > + * modify it under the terms of the GNU General Public License as > > + * published by the Free Software Foundation. > > + * > > + * This program is distributed in the hope that it would be useful, > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > + * GNU General Public License for more details. > > + * > > + * You should have received a copy of the GNU General Public License > > + * along with this program; if not, write the Free Software Foundation, > > + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > > + * > > + * Author: > > + * Alexey Kodanev <ale...@or...> > > + * > > + * Test checks following preconditions: > > + * since Linux kernel 3.7 it is possible to set extended attributes > > + * to cgroup files. > > + */ > > + > > +#include <sys/stat.h> > > +#include <sys/mount.h> > > +#include <sys/types.h> > > +#include <sys/xattr.h> > > +#include <stdio.h> > > +#include <stdlib.h> > > +#include <fcntl.h> > > +#include <dirent.h> > > +#include <unistd.h> > > +#include <string.h> > > +#include <errno.h> > > + > > +#include "test.h" > > +#include "usctest.h" > > +#include "safe_macros.h" > > + > > +char *TCID = "cgroup_xattr"; > > + > > +static const char cgrp_point[] = "/sys/fs/cgroup"; > > +static const char cgrp_name[] = "cgrp_test"; > > +static const char subdir_name[] = "test"; > > + > > +struct tst_key { > > + const char *name; > > + int good; > > +}; > > + > > +/* only security.* & trusted.* is valid key names */ > ^ are > > +static const struct tst_key tkeys[] = { > > + { .name = "trusted.test", .good = 1, }, > > + { .name = "security.", .good = 1, }, > > + { .name = "user.", .good = 0, }, > > + { .name = "system.", .good = 0, }, > > +}; > > + > > +#define VALUE_SIZE 8 > > + > > +/* > > + * values that can be written to xattr keys, > > + * their can be anything > ^ their value? > > + */ > > +struct tst_val { > > + char buf[VALUE_SIZE]; > > + size_t size; > > +}; > > + > > +/* cleanup flags */ > > +static int cgrp_mounted; > > +static int subdir_created; > > + > > +/* test options */ > > +static int skip_cleanup; > > +static int verbose; > > +static const option_t options[] = { > > + {"s", &skip_cleanup, NULL}, > > + {"v", &verbose, NULL}, > > + {NULL, NULL, NULL} > > +}; > > + > > +/* save to change back in the end */ > > +static char start_work_dir[PATH_MAX]; > > + > > +static void help(void); > > +static void setup(int argc, char *argv[]); > > +static void test_run(void); > > +static void cleanup(void); > > + > > +static int set_xattrs(const char *file, const struct tst_val *val); > > +static char *get_xattr(const char *file, const char *key_name, size_t > > *size); > > +static int get_xattrs(const char *file, const struct tst_val *val); > > +/* > > + * set or get xattr recursively > > + * > > + * @path: start directory > > + * @id: start char to fill in value > > + * @xattr_operation: can be set_xattrs() or get_xattrs() > > + */ > > +static int cgrp_files_walking(const char *path, char *id, > > + int (*xattr_operation)(const char *, const struct tst_val *)); > > +static char *get_hex_value(const char *value, size_t size); > > +static void fill_test_value(char *id, struct tst_val *val); > > + > > + > > +int main(int argc, char *argv[]) > > +{ > > + setup(argc, argv); > > + > > + test_run(); > > + > > + cleanup(); > > + > > + tst_exit(); > > +} > > + > > +static void help(void) > > +{ > > + printf(" -s Skip cleanup\n"); > > + printf(" -v Verbose\n"); > > +} > > + > > +void setup(int argc, char *argv[]) > > +{ > > + char *msg; > > + msg = parse_opts(argc, argv, options, help); > > + if (msg != NULL) > > + tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); > > + > > + tst_require_root(NULL); > > + > > + if (access("/proc/cgroups", F_OK) == -1) > > + tst_brkm(TCONF, cleanup, "Kernel doesn't support for cgroups"); > > + > > + if (tst_kvercmp(3, 7, 0) < 0) { > > + tst_brkm(TCONF, cleanup, > > + "Test must be run with kernel 3.7 or newer"); > > + } > > + > > + tst_sig(FORK, DEF_HANDLER, cleanup); > > + > > + /* mount all available subsystems (cpu, cpuset, memory, ...) */ > > + if (mount(cgrp_name, cgrp_point, "cgroup", 0, "xattr") == -1) > > + tst_brkm(TBROK, cleanup, "Can't mount: %s", cgrp_point); > > Here we mount cgroups under /sys/fs/cgroup which later causes problems > as you need to switch to another directory to unmount. > > I'm not that much familiar with cgroups, is mounting it to /sys/ > required or is it customary? Wouldn't that interfere with rest of the > system? It's customary, but It could interfere with systemd, which mounts some subsytems at boot: cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd) cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset) cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu) cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices) cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer) cgroup on /sys/fs/cgroup/net_cls type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls) cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio) cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event) Trying to mount different combination of subsystems will likely fail: # mount -t cgroup -ocpu none /mnt/test mount: none is already mounted or /mnt/test busy Quoting Documentation/cgroups/cgroups.txt: " If an active hierarchy with exactly the same set of subsystems already exists, it will be reused for the new mount. If no existing hierarchy matches, and any of the requested subsystems are in use in an existing hierarchy, the mount will fail with -EBUSY. Otherwise, a new hierarchy is activated, associated with the requested subsystems. " Regards, Jan > > What about calling tst_tmpdir(), creating a directory to mount the > cgroups there and then simply doing chdir to test temporary directory > before the unmount. Would that work? > > > + cgrp_mounted = 1; > > + > > + /* save current working directory */ > > + SAFE_GETCWD(cleanup, start_work_dir, PATH_MAX); > > + SAFE_CHDIR(cleanup, cgrp_point); > > + > > + /* create new hierarchy */ > > + SAFE_MKDIR(cleanup, subdir_name, 0755); > > + subdir_created = 1; > > +} > > + > > +static void test_run() > > void in params is missing > > > +{ > > + char id; > > + /* set xattr to each file in cgroup fs */ > > + id = 0; > > + int set_res = cgrp_files_walking(cgrp_point, &id, set_xattrs); > > Here you are passing pointer to the value, modify it by the function but > never use the resulting value. > > > + /* get & check xattr from each file in cgroup fs */ > > + id = 0; > > + int get_res = cgrp_files_walking(cgrp_point, &id, get_xattrs); > > + > > + /* final results */ > > + tst_resm(TINFO, "All test-cases have been completed, summary:"); > > + tst_resm(TINFO, "Set tests result: %s", (set_res) ? "FAIL" : "PASS"); > > + tst_resm(TINFO, "Get tests result: %s", (get_res) ? "FAIL" : "PASS"); > > +} > > + > > +static void cleanup(void) > > +{ > > + if (skip_cleanup) > > + return; > > + > > + fflush(stdout); > > What is this fflush() for? > > > + if (subdir_created) { > > + SAFE_CHDIR(NULL, cgrp_point); > > + if (rmdir(subdir_name) == -1) { > > + tst_brkm(TBROK, NULL, "Can't remove dir, error: %s", > > + strerror(errno)); > > + } > > + } > > + > > + if (strlen(start_work_dir) > 0) > > + SAFE_CHDIR(NULL, start_work_dir); > > + > > + if (cgrp_mounted) { > > + if (umount(cgrp_point) == -1) > > + tst_brkm(TBROK, NULL, "Can't unmount: %s", cgrp_point); > > + } > > + > > + TEST_CLEANUP; > > +} > > + > > +static int set_xattrs(const char *file, const struct tst_val *val) > > +{ > > + int i, res; > > + res = 0; > > + for (i = 0; i < ARRAY_SIZE(tkeys); ++i) { > > + int good = setxattr(file, tkeys[i].name, > > + (const void *)val->buf, val->size, 0) != -1; > > + > > + int fail = good != tkeys[i].good; > > + res |= fail; > > + > > + tst_resm((fail) ? TFAIL : TPASS, > > + "Expect: %s set xattr key '%s' to file '%s'", > > + (tkeys[i].good) ? "can" : "can't", > > + tkeys[i].name, file); > > + > > + if (verbose && tkeys[i].good) { > > + char *rval = get_hex_value(val->buf, val->size); > > + tst_resm(TINFO, "value =%s", rval); > > + free(rval); > > + } > > + } > > + return res; > > +} > > + > > +static char *get_xattr(const char *file, const char *key_name, size_t > > *size) > > +{ > > + char *xval = NULL; > > + /* get value size */ > > + ssize_t xval_size = getxattr(file, key_name, (void *)xval, 0); > > Pass NULL here, instead of the xval initialized to NULL. > > > + if (xval_size == -1) > > + return NULL; > > + > > + xval = SAFE_MALLOC(cleanup, xval_size); > > + > > + if (getxattr(file, key_name, (void *)xval, xval_size) == -1) { > > + free(xval); > > + return NULL; > > + } > > + *size = xval_size; > > + return xval; > > +} > > + > > +static int get_xattrs(const char *file, const struct tst_val *val) > > +{ > > + int i, fail, res; > > + res = 0; > > + for (i = 0; i < ARRAY_SIZE(tkeys); ++i) { > > + size_t xval_size = 0; > > + char *xval; > > + > > + xval = get_xattr(file, tkeys[i].name, &xval_size); > > + fail = (xval == NULL && tkeys[i].good); > > + res |= fail; > > + > > + tst_resm((fail) ? TFAIL : TPASS, > > + "Expect: %s read xattr %s of file '%s'", > > + (xval == NULL) ? "can't" : "can", tkeys[i].name, file); > > + if (xval == NULL) > > + continue; > > + > > + if (fail) { > > + free(xval); > > + continue; > > + } > > + > > + fail |= val->size != xval_size || > > + strncmp(val->buf, xval, val->size) != 0; > > + res |= fail; > > + > > + tst_resm((fail) ? TFAIL : TPASS, "Expect: values equal"); > > + > > + if (verbose && fail) { > > + char *rval = get_hex_value(xval, xval_size); > > + tst_resm(TINFO, "Read xattr value:%s", rval); > > + free(rval); > > + char *cval = get_hex_value(val->buf, val->size); > > + tst_resm(TINFO, "Expected value:%s", cval); > > + free(cval); > > + } > > + free(xval); > > + } > > + return res; > > +} > > + > > +static int cgrp_files_walking(const char *path, char *id, > > + int (*xattr_operation)(const char *, const struct tst_val *)) > > +{ > > + int res = 0; > > + struct dirent *entry; > > + DIR *dir; > > + dir = opendir(path); > > + SAFE_CHDIR(cleanup, path); > > + tst_resm(TINFO, "In dir %s", path); > > + errno = 0; > > + while ((entry = readdir(dir)) != NULL) { > > + const char *file = entry->d_name; > > + /* skip current and up directories */ > > + if (!strcmp(file, "..") || !strcmp(file, ".")) > > + continue; > > + > > + struct stat stat_buf; > > + TEST(lstat(file, &stat_buf)); > > + if (TEST_RETURN != -1 && S_ISDIR(stat_buf.st_mode)) { > > + /* proceed to subdir */ > > + res |= cgrp_files_walking(file, id, xattr_operation); > > + /* change directory back */ > > + SAFE_CHDIR(cleanup, ".."); > > + tst_resm(TINFO, "In dir %s", path); > > + } > > + struct tst_val val; > > + fill_test_value(id, &val); > > + res |= xattr_operation(file, &val); > > + errno = 0; > > + } > > + if (errno && !entry) > > + tst_brkm(TWARN, cleanup, "%s", strerror(errno)); > > Use TERRNO and also describe the warning more verbosely. > > > + if (closedir(dir) == -1) > > + tst_brkm(TWARN, cleanup, "Failed to close dir '%s'", path); > > + > > + return res; > > +} > > + > > +static char *get_hex_value(const char *value, size_t size) > > +{ > > + const size_t symb_num = 5; /* space + 0xXX*/ > > + char *buf = SAFE_MALLOC(cleanup, size * symb_num + 1); > > + size_t i; > > + for (i = 0; i < size; ++i) { > > + sprintf(buf + i * symb_num, " 0x%02X", > > + (unsigned char) *(value++)); > > + } > > + return buf; > > +} > > This function is too ugly. You should rather do something like > print_xattr() that would both preprare the string and call the > tst_resm() so that you don't need to pass allocated buffers around. > > void print_xattr(const char *msg, const char *val, size_t size) > { > char buf[size + sybm_num + 1]; > ... > > tst_resm(TINFO, "%s%s", msg, buf); > } > > Or we can create a tst_xxx function to print a buffer of bytes, I guess > that there are more cases where such function could be used. But that > would require a little more work to desing the interface right. > > > +static void fill_test_value(char *id, struct tst_val *val) > > +{ > > + int i; > > + for (i = 0; i < VALUE_SIZE; ++i) > > + val->buf[i] = *id; > > What about using memset()? > > > + val->size = VALUE_SIZE; > > + ++(*id); > > This is quite cryptic, why aren't you modifying the id in the loop that > calls this function? > > What about just defining the id in the walk functions and incrementing > it each time fill_test_value() was called? > > > +} > > -- > Cyril Hrubis > ch...@su... > > ------------------------------------------------------------------------------ > AlienVault Unified Security Management (USM) platform delivers complete > security visibility with the essential security capabilities. Easily and > efficiently configure, manage, and operate all of your security controls > from a single console and one unified framework. Download a free trial. > http://p.sf.net/sfu/alienvault_d2d > _______________________________________________ > Ltp-list mailing list > Ltp...@li... > https://lists.sourceforge.net/lists/listinfo/ltp-list > |
From: <ale...@or...> - 2013-05-15 14:33:59
|
Hi! On 05/15/2013 04:38 PM, ch...@su... wrote: > Hi! >> +++ b/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c >> @@ -0,0 +1,343 @@ >> +/* >> + * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. >> + * >> + * This program is free software; you can redistribute it and/or >> + * modify it under the terms of the GNU General Public License as >> + * published by the Free Software Foundation. >> + * >> + * This program is distributed in the hope that it would be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + * >> + * You should have received a copy of the GNU General Public License >> + * along with this program; if not, write the Free Software Foundation, >> + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA >> + * >> + * Author: >> + * Alexey Kodanev<ale...@or...> >> + * >> + * Test checks following preconditions: >> + * since Linux kernel 3.7 it is possible to set extended attributes >> + * to cgroup files. >> + */ >> + >> +#include<sys/stat.h> >> +#include<sys/mount.h> >> +#include<sys/types.h> >> +#include<sys/xattr.h> >> +#include<stdio.h> >> +#include<stdlib.h> >> +#include<fcntl.h> >> +#include<dirent.h> >> +#include<unistd.h> >> +#include<string.h> >> +#include<errno.h> >> + >> +#include "test.h" >> +#include "usctest.h" >> +#include "safe_macros.h" >> + >> +char *TCID = "cgroup_xattr"; >> + >> +static const char cgrp_point[] = "/sys/fs/cgroup"; >> +static const char cgrp_name[] = "cgrp_test"; >> +static const char subdir_name[] = "test"; >> + >> +struct tst_key { >> + const char *name; >> + int good; >> +}; >> + >> +/* only security.*& trusted.* is valid key names */ > ^ are >> +static const struct tst_key tkeys[] = { >> + { .name = "trusted.test", .good = 1, }, >> + { .name = "security.", .good = 1, }, >> + { .name = "user.", .good = 0, }, >> + { .name = "system.", .good = 0, }, >> +}; >> + >> +#define VALUE_SIZE 8 >> + >> +/* >> + * values that can be written to xattr keys, >> + * their can be anything > ^ their value? Yes >> + */ >> +struct tst_val { >> + char buf[VALUE_SIZE]; >> + size_t size; >> +}; >> + >> +/* cleanup flags */ >> +static int cgrp_mounted; >> +static int subdir_created; >> + >> +/* test options */ >> +static int skip_cleanup; >> +static int verbose; >> +static const option_t options[] = { >> + {"s",&skip_cleanup, NULL}, >> + {"v",&verbose, NULL}, >> + {NULL, NULL, NULL} >> +}; >> + >> +/* save to change back in the end */ >> +static char start_work_dir[PATH_MAX]; >> + >> +static void help(void); >> +static void setup(int argc, char *argv[]); >> +static void test_run(void); >> +static void cleanup(void); >> + >> +static int set_xattrs(const char *file, const struct tst_val *val); >> +static char *get_xattr(const char *file, const char *key_name, size_t *size); >> +static int get_xattrs(const char *file, const struct tst_val *val); >> +/* >> + * set or get xattr recursively >> + * >> + * @path: start directory >> + * @id: start char to fill in value >> + * @xattr_operation: can be set_xattrs() or get_xattrs() >> + */ >> +static int cgrp_files_walking(const char *path, char *id, >> + int (*xattr_operation)(const char *, const struct tst_val *)); >> +static char *get_hex_value(const char *value, size_t size); >> +static void fill_test_value(char *id, struct tst_val *val); >> + >> + >> +int main(int argc, char *argv[]) >> +{ >> + setup(argc, argv); >> + >> + test_run(); >> + >> + cleanup(); >> + >> + tst_exit(); >> +} >> + >> +static void help(void) >> +{ >> + printf(" -s Skip cleanup\n"); >> + printf(" -v Verbose\n"); >> +} >> + >> +void setup(int argc, char *argv[]) >> +{ >> + char *msg; >> + msg = parse_opts(argc, argv, options, help); >> + if (msg != NULL) >> + tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); >> + >> + tst_require_root(NULL); >> + >> + if (access("/proc/cgroups", F_OK) == -1) >> + tst_brkm(TCONF, cleanup, "Kernel doesn't support for cgroups"); >> + >> + if (tst_kvercmp(3, 7, 0)< 0) { >> + tst_brkm(TCONF, cleanup, >> + "Test must be run with kernel 3.7 or newer"); >> + } >> + >> + tst_sig(FORK, DEF_HANDLER, cleanup); >> + >> + /* mount all available subsystems (cpu, cpuset, memory, ...) */ >> + if (mount(cgrp_name, cgrp_point, "cgroup", 0, "xattr") == -1) >> + tst_brkm(TBROK, cleanup, "Can't mount: %s", cgrp_point); > Here we mount cgroups under /sys/fs/cgroup which later causes problems > as you need to switch to another directory to unmount. > > I'm not that much familiar with cgroups, is mounting it to /sys/ > required or is it customary? Wouldn't that interfere with rest of the > system? It's standard, but in the test better use another one. > What about calling tst_tmpdir(), creating a directory to mount the > cgroups there and then simply doing chdir to test temporary directory > before the unmount. Would that work? Yes, it's possible, I'll change it to tmp dir. >> + cgrp_mounted = 1; >> + >> + /* save current working directory */ >> + SAFE_GETCWD(cleanup, start_work_dir, PATH_MAX); >> + SAFE_CHDIR(cleanup, cgrp_point); >> + >> + /* create new hierarchy */ >> + SAFE_MKDIR(cleanup, subdir_name, 0755); >> + subdir_created = 1; >> +} >> + >> +static void test_run() > void in params is missing > >> +{ >> + char id; >> + /* set xattr to each file in cgroup fs */ >> + id = 0; >> + int set_res = cgrp_files_walking(cgrp_point,&id, set_xattrs); > Here you are passing pointer to the value, modify it by the function but > never use the resulting value. > >> + /* get& check xattr from each file in cgroup fs */ >> + id = 0; >> + int get_res = cgrp_files_walking(cgrp_point,&id, get_xattrs); >> + >> + /* final results */ >> + tst_resm(TINFO, "All test-cases have been completed, summary:"); >> + tst_resm(TINFO, "Set tests result: %s", (set_res) ? "FAIL" : "PASS"); >> + tst_resm(TINFO, "Get tests result: %s", (get_res) ? "FAIL" : "PASS"); >> +} >> + >> +static void cleanup(void) >> +{ >> + if (skip_cleanup) >> + return; >> + >> + fflush(stdout); > What is this fflush() for? > kernels 3.7 can crash while unmounting cgroups with xattr, so just to make sure all buffered data written before it happens >> + if (subdir_created) { >> + SAFE_CHDIR(NULL, cgrp_point); >> + if (rmdir(subdir_name) == -1) { >> + tst_brkm(TBROK, NULL, "Can't remove dir, error: %s", >> + strerror(errno)); >> + } >> + } >> + >> + if (strlen(start_work_dir)> 0) >> + SAFE_CHDIR(NULL, start_work_dir); >> + >> + if (cgrp_mounted) { >> + if (umount(cgrp_point) == -1) >> + tst_brkm(TBROK, NULL, "Can't unmount: %s", cgrp_point); >> + } >> + >> + TEST_CLEANUP; >> +} >> + >> +static int set_xattrs(const char *file, const struct tst_val *val) >> +{ >> + int i, res; >> + res = 0; >> + for (i = 0; i< ARRAY_SIZE(tkeys); ++i) { >> + int good = setxattr(file, tkeys[i].name, >> + (const void *)val->buf, val->size, 0) != -1; >> + >> + int fail = good != tkeys[i].good; >> + res |= fail; >> + >> + tst_resm((fail) ? TFAIL : TPASS, >> + "Expect: %s set xattr key '%s' to file '%s'", >> + (tkeys[i].good) ? "can" : "can't", >> + tkeys[i].name, file); >> + >> + if (verbose&& tkeys[i].good) { >> + char *rval = get_hex_value(val->buf, val->size); >> + tst_resm(TINFO, "value =%s", rval); >> + free(rval); >> + } >> + } >> + return res; >> +} >> + >> +static char *get_xattr(const char *file, const char *key_name, size_t *size) >> +{ >> + char *xval = NULL; >> + /* get value size */ >> + ssize_t xval_size = getxattr(file, key_name, (void *)xval, 0); > > Pass NULL here, instead of the xval initialized to NULL. Ok >> + if (xval_size == -1) >> + return NULL; >> + >> + xval = SAFE_MALLOC(cleanup, xval_size); >> + >> + if (getxattr(file, key_name, (void *)xval, xval_size) == -1) { >> + free(xval); >> + return NULL; >> + } >> + *size = xval_size; >> + return xval; >> +} >> + >> +static int get_xattrs(const char *file, const struct tst_val *val) >> +{ >> + int i, fail, res; >> + res = 0; >> + for (i = 0; i< ARRAY_SIZE(tkeys); ++i) { >> + size_t xval_size = 0; >> + char *xval; >> + >> + xval = get_xattr(file, tkeys[i].name,&xval_size); >> + fail = (xval == NULL&& tkeys[i].good); >> + res |= fail; >> + >> + tst_resm((fail) ? TFAIL : TPASS, >> + "Expect: %s read xattr %s of file '%s'", >> + (xval == NULL) ? "can't" : "can", tkeys[i].name, file); >> + if (xval == NULL) >> + continue; >> + >> + if (fail) { >> + free(xval); >> + continue; >> + } >> + >> + fail |= val->size != xval_size || >> + strncmp(val->buf, xval, val->size) != 0; >> + res |= fail; >> + >> + tst_resm((fail) ? TFAIL : TPASS, "Expect: values equal"); >> + >> + if (verbose&& fail) { >> + char *rval = get_hex_value(xval, xval_size); >> + tst_resm(TINFO, "Read xattr value:%s", rval); >> + free(rval); >> + char *cval = get_hex_value(val->buf, val->size); >> + tst_resm(TINFO, "Expected value:%s", cval); >> + free(cval); >> + } >> + free(xval); >> + } >> + return res; >> +} >> + >> +static int cgrp_files_walking(const char *path, char *id, >> + int (*xattr_operation)(const char *, const struct tst_val *)) >> +{ >> + int res = 0; >> + struct dirent *entry; >> + DIR *dir; >> + dir = opendir(path); >> + SAFE_CHDIR(cleanup, path); >> + tst_resm(TINFO, "In dir %s", path); >> + errno = 0; >> + while ((entry = readdir(dir)) != NULL) { >> + const char *file = entry->d_name; >> + /* skip current and up directories */ >> + if (!strcmp(file, "..") || !strcmp(file, ".")) >> + continue; >> + >> + struct stat stat_buf; >> + TEST(lstat(file,&stat_buf)); >> + if (TEST_RETURN != -1&& S_ISDIR(stat_buf.st_mode)) { >> + /* proceed to subdir */ >> + res |= cgrp_files_walking(file, id, xattr_operation); >> + /* change directory back */ >> + SAFE_CHDIR(cleanup, ".."); >> + tst_resm(TINFO, "In dir %s", path); >> + } >> + struct tst_val val; >> + fill_test_value(id,&val); >> + res |= xattr_operation(file,&val); >> + errno = 0; >> + } >> + if (errno&& !entry) >> + tst_brkm(TWARN, cleanup, "%s", strerror(errno)); > Use TERRNO and also describe the warning more verbosely. Ok >> + if (closedir(dir) == -1) >> + tst_brkm(TWARN, cleanup, "Failed to close dir '%s'", path); >> + >> + return res; >> +} >> + >> +static char *get_hex_value(const char *value, size_t size) >> +{ >> + const size_t symb_num = 5; /* space + 0xXX*/ >> + char *buf = SAFE_MALLOC(cleanup, size * symb_num + 1); >> + size_t i; >> + for (i = 0; i< size; ++i) { >> + sprintf(buf + i * symb_num, " 0x%02X", >> + (unsigned char) *(value++)); >> + } >> + return buf; >> +} > This function is too ugly. You should rather do something like > print_xattr() that would both preprare the string and call the > tst_resm() so that you don't need to pass allocated buffers around. > > void print_xattr(const char *msg, const char *val, size_t size) > { > char buf[size + sybm_num + 1]; > ... > > tst_resm(TINFO, "%s%s", msg, buf); > } > > Or we can create a tst_xxx function to print a buffer of bytes, I guess > that there are more cases where such function could be used. But that > would require a little more work to desing the interface right. > I agree, let's do that >> +static void fill_test_value(char *id, struct tst_val *val) >> +{ >> + int i; >> + for (i = 0; i< VALUE_SIZE; ++i) >> + val->buf[i] = *id; > What about using memset()? Ok :) further may be remove this function completely and just use memset instead? >> + val->size = VALUE_SIZE; >> + ++(*id); > This is quite cryptic, why aren't you modifying the id in the loop that > calls this function? > > What about just defining the id in the walk functions and incrementing > it each time fill_test_value() was called? > The point is to make unique key's values among all hierarchies (there was a bug when files with the same name share the same struct) Would it be better to use global id that is incrementing each time when fill_test_value() called, and resetting to init value between set and get walk functions? >> +} |
From: <ch...@su...> - 2013-05-15 14:55:47
|
Hi! > > What is this fflush() for? > > > kernels 3.7 can crash while unmounting cgroups with xattr, > so just to make sure all buffered data written before it happens Ok, let's add simlilar comment to the code as well. And it would be better to call tst_flush() as you are using tst_resm() interface. > > This function is too ugly. You should rather do something like > > print_xattr() that would both preprare the string and call the > > tst_resm() so that you don't need to pass allocated buffers around. > > > > void print_xattr(const char *msg, const char *val, size_t size) > > { > > char buf[size + sybm_num + 1]; > > ... > > > > tst_resm(TINFO, "%s%s", msg, buf); > > } > > > > Or we can create a tst_xxx function to print a buffer of bytes, I guess > > that there are more cases where such function could be used. But that > > would require a little more work to desing the interface right. > > > I agree, let's do that Ok, lets address that in separate patch. I would go for simple function that would take prefix in printf() format and would dump the hex values after that. Something like tst_resm_buf(flags, buf, buf_len, fmt, ...) but perhaps we can come up with better name and/or API. I do not want to add custom printf() format strings on purpose, as that would: 1) create confusion 2) possibly break the checks on the format done by compiler Anybody else has better idea? > >> +static void fill_test_value(char *id, struct tst_val *val) > >> +{ > >> + int i; > >> + for (i = 0; i< VALUE_SIZE; ++i) > >> + val->buf[i] = *id; > > What about using memset()? > Ok :) further may be remove this function completely and just use memset > instead? Sounds good. > > What about just defining the id in the walk functions and incrementing > > it each time fill_test_value() was called? > > > The point is to make unique key's values among all hierarchies (there > was a bug when files with the same name share the same struct) > Would it be better to use global id that is incrementing each time when > fill_test_value() called, > and resetting to init value between set and get walk functions? Global id sounds better to me. -- Cyril Hrubis ch...@su... |
From: <ale...@or...> - 2013-05-16 06:59:06
|
Hi! On 05/15/2013 06:56 PM, ch...@su... wrote: > Ok, lets address that in separate patch. I would go for simple function > that would take prefix in printf() format and would dump the hex values > after that. Something like tst_resm_buf(flags, buf, buf_len, fmt, ...) > but perhaps we can come up with better name and/or API. > > I do not want to add custom printf() format strings on purpose, as that > would: > > 1) create confusion > > 2) possibly break the checks on the format done by compiler > > Anybody else has better idea? Since we're going to print buf in hex, may be we can use tst_resm_hex(flags, buf, buf_len, fmt, ...) to make it more descriptive? Thanks, Alexey |