@@ -1602,35 +1602,39 @@ LTP library can be instructed to save and restore value of specified
field 'save_restore'. It is a NULL-terminated array of struct
'tst_path_val' where each tst_path_val.path represents a file, whose
value is saved at the beginning and restored at the end of the test.
-If non-NULL value is passed it is written to the respective file at
-the beginning of the test. Only the first line of a specified file
-is saved and restored.
+If non-NULL string is passed in tst_path_val.val, it is written
+to the respective file at the beginning of the test. Only the first line
+of a specified file is saved and restored.
-Pathnames can be optionally prefixed to specify how strictly (during
-'store') are handled errors:
+By default, the test will end with TCONF if the file is read-only or
+does not exist. If the optional write of new value fails, the test will end
+with 'TBROK'. This behavior can be changed using tst_path_val.flags:
-* (no prefix) - test ends with 'TCONF', if file doesn't exist
-* '?' - test prints info message and continues,
- if file doesn't exist or open/read fails
-* '!' - test ends with 'TBROK', if file doesn't exist
+* 'TST_SR_TBROK_MISSING' – End test with 'TBROK' if the file does not exist
+* 'TST_SR_TCONF_MISSING' – End test with 'TCONF' if the file does not exist
+* 'TST_SR_SKIP_MISSING' – Continue without saving the file if it does not exist
+* 'TST_SR_TBROK_RO' – End test with 'TBROK' if the file is read-only
+* 'TST_SR_TCONF_RO' – End test with 'TCONF' if the file is read-only
+* 'TST_SR_SKIP_RO' – Continue without saving the file if it is read-only
+* 'TST_SR_IGNORE_ERR' – Ignore errors when writing new value into the file
+
+Common flag combinations also have shortcuts:
+
+* 'TST_SR_TCONF' – Equivalent to 'TST_SR_TCONF_MISSING | TST_SR_TCONF_RO'
+* 'TST_SR_TBROK' – Equivalent to 'TST_SR_TBROK_MISSING | TST_SR_TBROK_RO'
+* 'TST_SR_SKIP' – Equivalent to 'TST_SR_SKIP_MISSING | TST_SR_SKIP_RO'
'restore' is always strict and will TWARN if it encounters any error.
[source,c]
-------------------------------------------------------------------------------
-static void setup(void)
-{
- FILE_PRINTF("/proc/sys/kernel/core_pattern", "/mypath");
- SAFE_TRY_FILE_PRINTF("/proc/sys/user/max_user_namespaces", "%d", 10);
-}
-
static struct tst_test test = {
...
.setup = setup,
.save_restore = (const struct tst_path_val[]) {
- {"/proc/sys/kernel/core_pattern", NULL},
- {"?/proc/sys/user/max_user_namespaces", NULL},
- {"!/sys/kernel/mm/ksm/run", "1"},
+ {"/proc/sys/kernel/core_pattern", NULL, TST_SR_TCONF},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
+ {"/sys/kernel/mm/ksm/run", "1", TST_SR_TBROK},
{}
},
};
@@ -5,14 +5,26 @@
#ifndef TST_SYS_CONF_H__
#define TST_SYS_CONF_H__
+#define TST_SR_TCONF_MISSING 0x0
+#define TST_SR_TBROK_MISSING 0x1
+#define TST_SR_SKIP_MISSING 0x2
+#define TST_SR_TCONF_RO 0x0
+#define TST_SR_TBROK_RO 0x4
+#define TST_SR_SKIP_RO 0x8
+#define TST_SR_IGNORE_ERR 0x10
+
+#define TST_SR_TCONF (TST_SR_TCONF_MISSING | TST_SR_TCONF_RO)
+#define TST_SR_TBROK (TST_SR_TBROK_MISSING | TST_SR_TBROK_RO)
+#define TST_SR_SKIP (TST_SR_SKIP_MISSING | TST_SR_SKIP_RO)
+
struct tst_path_val {
const char *path;
const char *val;
+ unsigned int flags;
};
-int tst_sys_conf_save_str(const char *path, const char *value);
-int tst_sys_conf_save(const char *path);
-void tst_sys_conf_set(const char *path, const char *value);
+void tst_sys_conf_save_str(const char *path, const char *value);
+int tst_sys_conf_save(const struct tst_path_val *conf);
void tst_sys_conf_restore(int verbose);
void tst_sys_conf_dump(void);
@@ -24,9 +24,9 @@ static struct tst_test test = {
.test_all = run,
.setup = setup,
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/nonexistent", NULL},
- {"!/proc/sys/kernel/numa_balancing", NULL},
- {"/proc/sys/kernel/core_pattern", NULL},
+ {"/proc/nonexistent", NULL, TST_SR_SKIP},
+ {"/proc/sys/kernel/numa_balancing", NULL, TST_SR_TBROK},
+ {"/proc/sys/kernel/core_pattern", NULL, TST_SR_TCONF},
{}
},
};
@@ -39,7 +39,7 @@ static struct tst_test test = {
.test_all = do_test,
.hugepages = {2, TST_NEEDS},
.save_restore = (const struct tst_path_val[]) {
- {"!/proc/sys/kernel/numa_balancing", "0"},
+ {"/proc/sys/kernel/numa_balancing", "0", TST_SR_TBROK},
{}
},
};
@@ -23,6 +23,11 @@ size_t tst_get_hugepage_size(void)
unsigned long tst_reserve_hugepages(struct tst_hugepage *hp)
{
unsigned long val, max_hpages;
+ struct tst_path_val pvl = {
+ .path = PATH_NR_HPAGES,
+ .val = NULL,
+ .flags = TST_SR_SKIP_MISSING | TST_SR_TCONF_RO
+ };
if (access(PATH_HUGEPAGES, F_OK)) {
if (hp->policy == TST_NEEDS)
@@ -59,7 +64,7 @@ unsigned long tst_reserve_hugepages(struct tst_hugepage *hp)
}
set_hugepages:
- tst_sys_conf_save("?/proc/sys/vm/nr_hugepages");
+ tst_sys_conf_save(&pvl);
SAFE_FILE_PRINTF(PATH_NR_HPAGES, "%lu", tst_hugepages);
SAFE_FILE_SCANF(PATH_NR_HPAGES, "%lu", &val);
if (val != tst_hugepages)
@@ -20,6 +20,15 @@ struct tst_sys_conf {
static struct tst_sys_conf *save_restore_data;
+static void print_error(const int lineno, int info_only, const char *err,
+ const char *path)
+{
+ if (info_only)
+ tst_res_(__FILE__, lineno, TINFO | TERRNO, err, path);
+ else
+ tst_brk_(__FILE__, lineno, TBROK | TERRNO, err, path);
+}
+
void tst_sys_conf_dump(void)
{
struct tst_sys_conf *i;
@@ -28,7 +37,7 @@ void tst_sys_conf_dump(void)
tst_res(TINFO, "%s = %s", i->path, i->value);
}
-int tst_sys_conf_save_str(const char *path, const char *value)
+void tst_sys_conf_save_str(const char *path, const char *value)
{
struct tst_sys_conf *n = SAFE_MALLOC(sizeof(*n));
@@ -40,45 +49,45 @@ int tst_sys_conf_save_str(const char *path, const char *value)
n->next = save_restore_data;
save_restore_data = n;
-
- return 0;
}
-int tst_sys_conf_save(const char *path)
+int tst_sys_conf_save(const struct tst_path_val *conf)
{
char line[PATH_MAX];
+ int ttype, iret;
FILE *fp;
void *ret;
- char flag;
- if (!path)
+ if (!conf || !conf->path)
tst_brk(TBROK, "path is empty");
- flag = path[0];
- if (flag == '?' || flag == '!')
- path++;
-
- if (access(path, F_OK) != 0) {
- switch (flag) {
- case '?':
- tst_res(TINFO, "Path not found: '%s'", path);
- break;
- case '!':
- tst_brk(TBROK|TERRNO, "Path not found: '%s'", path);
- break;
- default:
- tst_brk(TCONF|TERRNO, "Path not found: '%s'", path);
+ if (access(conf->path, F_OK) != 0) {
+ if (conf->flags & TST_SR_SKIP_MISSING) {
+ tst_res(TINFO | TERRNO, "Path not found: %s",
+ conf->path);
+ return 1;
}
- return 1;
+
+ ttype = (conf->flags & TST_SR_TBROK_MISSING) ? TBROK : TCONF;
+ tst_brk(ttype | TERRNO, "Path not found: %s", conf->path);
}
- fp = fopen(path, "r");
- if (fp == NULL) {
- if (flag == '?')
+ if (access(conf->path, W_OK) != 0) {
+ if (conf->flags & TST_SR_SKIP_RO) {
+ tst_res(TINFO | TERRNO, "Path is not writable: %s",
+ conf->path);
return 1;
+ }
- tst_brk(TBROK | TERRNO, "Failed to open FILE '%s' for reading",
- path);
+ ttype = (conf->flags & TST_SR_TBROK_RO) ? TBROK : TCONF;
+ tst_brk(ttype | TERRNO, "Path is not writable: %s", conf->path);
+ }
+
+ fp = fopen(conf->path, "r");
+
+ if (fp == NULL) {
+ print_error(__LINE__, conf->flags & TST_SR_IGNORE_ERR,
+ "Failed to open '%s' for reading", conf->path);
return 1;
}
@@ -86,24 +95,41 @@ int tst_sys_conf_save(const char *path)
fclose(fp);
if (ret == NULL) {
- if (flag == '?')
+ if (conf->flags & TST_SR_IGNORE_ERR)
return 1;
tst_brk(TBROK | TERRNO, "Failed to read anything from '%s'",
- path);
+ conf->path);
}
- return tst_sys_conf_save_str(path, line);
-}
+ tst_sys_conf_save_str(conf->path, line);
-void tst_sys_conf_set(const char *path, const char *value)
-{
- char flag = path[0];
- if (flag == '?' || flag == '!')
- path++;
+ if (!conf->val)
+ return 0;
+
+ fp = fopen(conf->path, "w");
+
+ if (fp == NULL) {
+ print_error(__LINE__, conf->flags & TST_SR_IGNORE_ERR,
+ "Failed to open '%s' for writing", conf->path);
+ return 0;
+ }
+
+ iret = fputs(conf->val, fp);
- if (value)
- SAFE_FILE_PRINTF(path, "%s", value);
+ if (iret < 0) {
+ print_error(__LINE__, conf->flags & TST_SR_IGNORE_ERR,
+ "Failed to write into '%s'", conf->path);
+ }
+
+ iret = fclose(fp);
+
+ if (iret < 0) {
+ print_error(__LINE__, conf->flags & TST_SR_IGNORE_ERR,
+ "Failed to close '%s'", conf->path);
+ }
+
+ return 0;
}
void tst_sys_conf_restore(int verbose)
@@ -1211,8 +1211,7 @@ static void do_setup(int argc, char *argv[])
const struct tst_path_val *pvl = tst_test->save_restore;
while (pvl->path) {
- if (!tst_sys_conf_save(pvl->path))
- tst_sys_conf_set(pvl->path, pvl->val);
+ tst_sys_conf_save(pvl);
pvl++;
}
}
@@ -269,7 +269,7 @@ static struct tst_test test = {
NULL
},
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/user/max_user_namespaces", NULL},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
{}
},
.tags = (const struct tst_tag[]) {
@@ -135,7 +135,7 @@ static struct tst_test test = {
NULL
},
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/user/max_user_namespaces", NULL},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
{}
},
.tags = (const struct tst_tag[]) {
@@ -217,7 +217,8 @@ static struct tst_test test = {
.cleanup = tst_kvm_cleanup,
.needs_root = 1,
.save_restore = (const struct tst_path_val[]) {
- {"?/sys/module/kvm/parameters/tdp_mmu", "0"},
+ {"/sys/module/kvm/parameters/tdp_mmu", "0",
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
{}
},
.supported_archs = (const char *const []) {
@@ -131,7 +131,7 @@ static struct tst_test test = {
.needs_hugetlbfs = 1,
.forks_child = 1,
.save_restore = (const struct tst_path_val[]) {
- {PATH_OC_HPAGES, NULL},
+ {PATH_OC_HPAGES, NULL, TST_SR_TCONF},
{}
},
.tcnt = 2,
@@ -80,10 +80,12 @@ static struct tst_test test = {
},
.setup = setup,
.save_restore = (const struct tst_path_val[]) {
- {"!/sys/kernel/mm/ksm/run", NULL},
- {"!/sys/kernel/mm/ksm/sleep_millisecs", NULL},
- {"?/sys/kernel/mm/ksm/max_page_sharing", NULL},
- {"?/sys/kernel/mm/ksm/merge_across_nodes", "1"},
+ {"/sys/kernel/mm/ksm/run", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/sleep_millisecs", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/max_page_sharing", NULL,
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
+ {"/sys/kernel/mm/ksm/merge_across_nodes", "1",
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
{}
},
.needs_kconfigs = (const char *const[]){
@@ -101,10 +101,12 @@ static struct tst_test test = {
},
.setup = setup,
.save_restore = (const struct tst_path_val[]) {
- {"!/sys/kernel/mm/ksm/run", NULL},
- {"!/sys/kernel/mm/ksm/sleep_millisecs", NULL},
- {"?/sys/kernel/mm/ksm/max_page_sharing", NULL},
- {"?/sys/kernel/mm/ksm/merge_across_nodes", "1"},
+ {"/sys/kernel/mm/ksm/run", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/sleep_millisecs", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/max_page_sharing", NULL,
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
+ {"/sys/kernel/mm/ksm/merge_across_nodes", "1",
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
{}
},
.needs_kconfigs = (const char *const[]){
@@ -83,10 +83,12 @@ static struct tst_test test = {
},
.setup = setup,
.save_restore = (const struct tst_path_val[]) {
- {"!/sys/kernel/mm/ksm/run", NULL},
- {"!/sys/kernel/mm/ksm/sleep_millisecs", NULL},
- {"?/sys/kernel/mm/ksm/max_page_sharing", NULL},
- {"?/sys/kernel/mm/ksm/merge_across_nodes", "1"},
+ {"/sys/kernel/mm/ksm/run", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/sleep_millisecs", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/max_page_sharing", NULL,
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
+ {"/sys/kernel/mm/ksm/merge_across_nodes", "1",
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
{}
},
.needs_kconfigs = (const char *const[]){
@@ -103,10 +103,12 @@ static struct tst_test test = {
},
.setup = setup,
.save_restore = (const struct tst_path_val[]) {
- {"!/sys/kernel/mm/ksm/run", NULL},
- {"!/sys/kernel/mm/ksm/sleep_millisecs", NULL},
- {"?/sys/kernel/mm/ksm/max_page_sharing", NULL},
- {"?/sys/kernel/mm/ksm/merge_across_nodes", "1"},
+ {"/sys/kernel/mm/ksm/run", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/sleep_millisecs", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/max_page_sharing", NULL,
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
+ {"/sys/kernel/mm/ksm/merge_across_nodes", "1",
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
{}
},
.needs_kconfigs = (const char *const[]){
@@ -89,7 +89,7 @@ static struct tst_test test = {
.test_all = test_ksm,
.min_kver = "2.6.32",
.save_restore = (const struct tst_path_val[]) {
- {"!/sys/kernel/mm/ksm/run", "1"},
+ {"/sys/kernel/mm/ksm/run", "1", TST_SR_TBROK},
{}
},
.needs_kconfigs = (const char *const[]){
@@ -137,10 +137,11 @@ static struct tst_test test = {
},
.setup = setup,
.save_restore = (const struct tst_path_val[]) {
- {"?/sys/kernel/mm/ksm/max_page_sharing", NULL},
- {"!/sys/kernel/mm/ksm/run", NULL},
- {"!/sys/kernel/mm/ksm/sleep_millisecs", NULL},
- {"/sys/kernel/mm/ksm/merge_across_nodes", NULL},
+ {"/sys/kernel/mm/ksm/max_page_sharing", NULL,
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
+ {"/sys/kernel/mm/ksm/run", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/sleep_millisecs", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/merge_across_nodes", NULL, TST_SR_TCONF},
{}
},
.needs_kconfigs = (const char *const[]){
@@ -220,9 +220,12 @@ static struct tst_test test = {
.forks_child = 1,
.cleanup = cleanup,
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/kernel/keys/gc_delay", "1"},
- {"?/proc/sys/kernel/keys/maxkeys", "200"},
- {"?/proc/sys/kernel/keys/maxbytes", "20000"},
+ {"/proc/sys/kernel/keys/gc_delay", "1",
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
+ {"/proc/sys/kernel/keys/maxkeys", "200",
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
+ {"/proc/sys/kernel/keys/maxbytes", "20000",
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
{}
},
.bufs = (struct tst_buffers []) {
@@ -110,7 +110,7 @@ static struct tst_test test = {
NULL
},
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/user/max_user_namespaces", NULL},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
{}
},
.tags = (const struct tst_tag[]) {
@@ -113,7 +113,7 @@ static struct tst_test test = {
.max_runtime = 600,
.test_all = check,
.save_restore = (const struct tst_path_val[]) {
- {"!/proc/sys/kernel/pid_max", PID_MAX_STR},
+ {"/proc/sys/kernel/pid_max", PID_MAX_STR, TST_SR_TBROK},
{}
},
.tags = (const struct tst_tag[]) {
@@ -76,7 +76,7 @@ static struct tst_test test = {
.cleanup = cleanup,
.test_all = verify_msgget,
.save_restore = (const struct tst_path_val[]){
- {"/proc/sys/kernel/msgmni", NULL},
+ {"/proc/sys/kernel/msgmni", NULL, TST_SR_TCONF},
{}
}
};
@@ -239,7 +239,8 @@ static struct tst_test test = {
.needs_tmpdir = 1,
.needs_root = 1,
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/vm/swappiness", NULL},
+ {"/proc/sys/vm/swappiness", NULL,
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
{}
},
.needs_cgroup_ctrls = (const char *const []){ "memory", NULL },
@@ -213,7 +213,7 @@ static struct tst_test test = {
.needs_root = 1,
.forks_child = 1,
.save_restore = (const struct tst_path_val[]) {
- {CORE_PATTERN, NULL},
+ {CORE_PATTERN, NULL, TST_SR_TCONF},
{}
},
};
@@ -327,7 +327,8 @@ static struct tst_test test = {
.test_all = run,
.setup = setup,
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/kernel/numa_balancing", "0"},
+ {"/proc/sys/kernel/numa_balancing", "0",
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
{}
},
};
@@ -218,7 +218,7 @@ static struct tst_test test = {
NULL
},
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/user/max_user_namespaces", NULL},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
{}
},
.tags = (const struct tst_tag[]) {
@@ -102,7 +102,7 @@ static struct tst_test test = {
NULL
},
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/user/max_user_namespaces", NULL},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
{}
},
.tags = (const struct tst_tag[]) {
@@ -130,7 +130,7 @@ static struct tst_test test = {
NULL
},
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/user/max_user_namespaces", NULL},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
{}
},
.tags = (const struct tst_tag[]) {
@@ -143,7 +143,7 @@ static struct tst_test test = {
NULL
},
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/user/max_user_namespaces", NULL},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
{}
},
.tags = (const struct tst_tag[]) {
@@ -159,7 +159,7 @@ static struct tst_test test = {
NULL
},
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/user/max_user_namespaces", NULL},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
{}
},
.tags = (const struct tst_tag[]) {
@@ -124,7 +124,7 @@ static struct tst_test test = {
NULL
},
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/user/max_user_namespaces", NULL},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
{}
},
.tags = (const struct tst_tag[]) {
@@ -58,7 +58,7 @@ static void run(unsigned int n)
static struct tst_test test = {
.test = run,
.save_restore = (const struct tst_path_val[]) {
- {"!/proc/sys/kernel/printk", NULL},
+ {"/proc/sys/kernel/printk", NULL, TST_SR_TBROK},
{}
},
.needs_root = 1,