All of lore.kernel.org
 help / color / mirror / Atom feed
* [LTP] [PATCH 0/4] Introduce a concept of test runtime cap
@ 2021-06-09 11:46 Cyril Hrubis
  2021-06-09 11:46 ` [LTP] [PATCH 1/4] lib: tst_supported_fs_types: Add tst_fs_max_types() Cyril Hrubis
                   ` (4 more replies)
  0 siblings, 5 replies; 14+ messages in thread
From: Cyril Hrubis @ 2021-06-09 11:46 UTC (permalink / raw)
  To: ltp

Recently we had a problem with tests that use .all_filesystems and
tst_timeout_remaining().

The problem is that the tst_timeout_remaining() function wasn't aware of
the loop over fork_testrun() that executes a testrun for each supported
filesystem, which caused such test, e.g. writev03, to run for
unpredictable and long periods.

This is an attempt how to solve this properly by uncoupling timeouts
from test runtime so that test never runs for longer than its timeout.

Cyril Hrubis (4):
  lib: tst_supported_fs_types: Add tst_fs_max_types()
  lib: tst_test: Move timeout scaling out of fork_testrun()
  lib: Introduce concept of max_test_runtime
  syscalls/writev03: Adjust test runtime

 include/tst_fs.h                              |  5 ++
 include/tst_fuzzy_sync.h                      |  4 +-
 include/tst_test.h                            |  7 +-
 lib/newlib_tests/.gitignore                   |  3 +-
 .../{test18.c => test_runtime01.c}            |  7 +-
 lib/newlib_tests/test_runtime02.c             | 31 +++++++
 lib/tst_supported_fs_types.c                  |  5 ++
 lib/tst_test.c                                | 87 ++++++++++++++++---
 testcases/kernel/crypto/af_alg02.c            |  2 +-
 testcases/kernel/crypto/pcrypt_aead01.c       |  2 +-
 testcases/kernel/mem/mtest01/mtest01.c        |  6 +-
 testcases/kernel/mem/mtest06/mmap1.c          | 13 ++-
 .../kernel/syscalls/move_pages/move_pages12.c |  4 +-
 testcases/kernel/syscalls/writev/writev03.c   |  1 +
 14 files changed, 141 insertions(+), 36 deletions(-)
 rename lib/newlib_tests/{test18.c => test_runtime01.c} (59%)
 create mode 100644 lib/newlib_tests/test_runtime02.c

-- 
2.31.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [LTP] [PATCH 1/4] lib: tst_supported_fs_types: Add tst_fs_max_types()
  2021-06-09 11:46 [LTP] [PATCH 0/4] Introduce a concept of test runtime cap Cyril Hrubis
@ 2021-06-09 11:46 ` Cyril Hrubis
  2021-06-09 11:46 ` [LTP] [PATCH 2/4] lib: tst_test: Move timeout scaling out of fork_testrun() Cyril Hrubis
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 14+ messages in thread
From: Cyril Hrubis @ 2021-06-09 11:46 UTC (permalink / raw)
  To: ltp

The tst_fs_max_types() returns an upper bound on the number of supported
filesystems.

Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
---
 include/tst_fs.h             | 5 +++++
 lib/tst_supported_fs_types.c | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/include/tst_fs.h b/include/tst_fs.h
index 36d4b46f0..d1ed78cb5 100644
--- a/include/tst_fs.h
+++ b/include/tst_fs.h
@@ -195,6 +195,11 @@ const char **tst_get_supported_fs_types(const char *const *skiplist);
  */
 int tst_fs_in_skiplist(const char *fs_type, const char *const *skiplist);
 
+/*
+ * Returns maximal number of supported filesystem types.
+ */
+unsigned int tst_fs_max_types(void);
+
 /*
  * Check whether device supports FS quotas. Negative return value means that
  * quotas appear to be broken.
diff --git a/lib/tst_supported_fs_types.c b/lib/tst_supported_fs_types.c
index 592a526ae..1de0f30b2 100644
--- a/lib/tst_supported_fs_types.c
+++ b/lib/tst_supported_fs_types.c
@@ -29,6 +29,11 @@ static const char *const fs_type_whitelist[] = {
 
 static const char *fs_types[ARRAY_SIZE(fs_type_whitelist)];
 
+unsigned int tst_fs_max_types(void)
+{
+	return ARRAY_SIZE(fs_type_whitelist) - 1;
+}
+
 static int has_mkfs(const char *fs_type)
 {
 	char buf[128];
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [LTP] [PATCH 2/4] lib: tst_test: Move timeout scaling out of fork_testrun()
  2021-06-09 11:46 [LTP] [PATCH 0/4] Introduce a concept of test runtime cap Cyril Hrubis
  2021-06-09 11:46 ` [LTP] [PATCH 1/4] lib: tst_supported_fs_types: Add tst_fs_max_types() Cyril Hrubis
@ 2021-06-09 11:46 ` Cyril Hrubis
  2021-06-09 11:46 ` [LTP] [PATCH 3/4] lib: Introduce concept of max_test_runtime Cyril Hrubis
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 14+ messages in thread
From: Cyril Hrubis @ 2021-06-09 11:46 UTC (permalink / raw)
  To: ltp

There is no point in setting the results->timeout in each iteration of
the tests (either for .all_filesystems or .test_variants) and it's cleaner to
do this once at the start of the testrun.

This also fixes a case where .all_filesystems or .test_variants would
call tst_set_timeout() in a test setup() that would be changed back on
second and subsequent runs because we called the tst_set_timeout()
incorrectly at the start of the fork_testrun().

Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
---
 lib/tst_test.c | 23 +++++++++++++----------
 1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/lib/tst_test.c b/lib/tst_test.c
index 36a4809c7..7c9061d6d 100644
--- a/lib/tst_test.c
+++ b/lib/tst_test.c
@@ -1296,7 +1296,7 @@ unsigned int tst_multiply_timeout(unsigned int timeout)
 	return timeout * timeout_mul;
 }
 
-void tst_set_timeout(int timeout)
+static void set_timeout(int timeout)
 {
 	if (timeout == -1) {
 		tst_res(TINFO, "Timeout per run is disabled");
@@ -1311,24 +1311,22 @@ void tst_set_timeout(int timeout)
 	tst_res(TINFO, "Timeout per run is %uh %02um %02us",
 		results->timeout/3600, (results->timeout%3600)/60,
 		results->timeout % 60);
+}
 
-	if (getpid() == lib_pid)
-		alarm(results->timeout);
-	else
-		heartbeat();
+void tst_set_timeout(int timeout)
+{
+	set_timeout(timeout);
+	heartbeat();
 }
 
 static int fork_testrun(void)
 {
 	int status;
 
-	if (tst_test->timeout)
-		tst_set_timeout(tst_test->timeout);
-	else
-		tst_set_timeout(300);
-
 	SAFE_SIGNAL(SIGINT, sigint_handler);
 
+	alarm(results->timeout);
+
 	test_pid = fork();
 	if (test_pid < 0)
 		tst_brk(TBROK | TERRNO, "fork()");
@@ -1417,6 +1415,11 @@ void tst_run_tcases(int argc, char *argv[], struct tst_test *self)
 	SAFE_SIGNAL(SIGALRM, alarm_handler);
 	SAFE_SIGNAL(SIGUSR1, heartbeat_handler);
 
+	if (tst_test->timeout)
+		set_timeout(tst_test->timeout);
+	else
+		set_timeout(300);
+
 	if (tst_test->test_variants)
 		test_variants = tst_test->test_variants;
 
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [LTP] [PATCH 3/4] lib: Introduce concept of max_test_runtime
  2021-06-09 11:46 [LTP] [PATCH 0/4] Introduce a concept of test runtime cap Cyril Hrubis
  2021-06-09 11:46 ` [LTP] [PATCH 1/4] lib: tst_supported_fs_types: Add tst_fs_max_types() Cyril Hrubis
  2021-06-09 11:46 ` [LTP] [PATCH 2/4] lib: tst_test: Move timeout scaling out of fork_testrun() Cyril Hrubis
@ 2021-06-09 11:46 ` Cyril Hrubis
  2021-06-09 13:24   ` [LTP] [Automated-testing] " Petr Vorel
  2021-06-09 14:44   ` [LTP] " Richard Palethorpe
  2021-06-09 11:46 ` [LTP] [PATCH 4/4] syscalls/writev03: Adjust test runtime Cyril Hrubis
  2021-06-09 14:54 ` [LTP] [PATCH 0/4] Introduce a concept of test runtime cap Richard Palethorpe
  4 siblings, 2 replies; 14+ messages in thread
From: Cyril Hrubis @ 2021-06-09 11:46 UTC (permalink / raw)
  To: ltp

This is an attempt on how to handle a cap on a test runtime correctly it
consists of several pieces namely:

* The idea of test maximal runtime is uncoupled from  test timeout

  - the maximal runtime is simply a cap for how long should an instance
    of a test run, it's mainly used by CVE reproducers that attempt to
    trigger a race until they run out of time, such test may exit sooner
    but must not run longer than the cap

  - the tst_timeout_remaining() is replaced with tst_remaining_runtime()
    which accounts correctly for .test_variants and .all_filesystems

* The default value for a test max_runtime is computed from test timeout

  - we scale the timeout down so that the there is some room for test to
    properly exit once test runtime was exhausted, this is our base for
    a test max_runtime

  - the scaled value is then divided, if needed, so that we end up a
    correct maximal runtime for an instance of a test, i.e. we have
    max runtime for an instance fork_testrun() that is inside of
    .test_variants and .all_filesystems loops

  - this also allows us to controll the test max runtime by setting a
    test timeout

* The maximal runtime, per whole test, can be passed down to the test

  - If LTP_MAX_TEST_RUNTIME is set in test environment it's used as a
    base for max_runtime instead of the scaled down timeout, it's still
    divided into pieces so that we have correct runtime cap for an
    fork_testrun() instance

  - We also make sure that test timeout is adjusted, if needed, to
    accomodate for the new test runtime cap, i.e. if upscaled runtime is
    greater than timeout, the test timeout is adjusted

Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
---
 include/tst_fuzzy_sync.h                      |  4 +-
 include/tst_test.h                            |  7 +-
 lib/newlib_tests/.gitignore                   |  3 +-
 .../{test18.c => test_runtime01.c}            |  7 +-
 lib/newlib_tests/test_runtime02.c             | 31 +++++++++
 lib/tst_test.c                                | 64 ++++++++++++++++++-
 testcases/kernel/crypto/af_alg02.c            |  2 +-
 testcases/kernel/crypto/pcrypt_aead01.c       |  2 +-
 testcases/kernel/mem/mtest01/mtest01.c        |  6 +-
 testcases/kernel/mem/mtest06/mmap1.c          | 13 ++--
 .../kernel/syscalls/move_pages/move_pages12.c |  4 +-
 11 files changed, 117 insertions(+), 26 deletions(-)
 rename lib/newlib_tests/{test18.c => test_runtime01.c} (59%)
 create mode 100644 lib/newlib_tests/test_runtime02.c

diff --git a/include/tst_fuzzy_sync.h b/include/tst_fuzzy_sync.h
index 8f97bb8f6..93adbb909 100644
--- a/include/tst_fuzzy_sync.h
+++ b/include/tst_fuzzy_sync.h
@@ -319,7 +319,7 @@ static void tst_fzsync_pair_reset(struct tst_fzsync_pair *pair,
 		SAFE_PTHREAD_CREATE(&pair->thread_b, 0, tst_fzsync_thread_wrapper, &wrap_run_b);
 	}
 
-	pair->exec_time_start = (float)tst_timeout_remaining();
+	pair->exec_time_start = (float)tst_remaining_runtime();
 }
 
 /**
@@ -663,7 +663,7 @@ static inline void tst_fzsync_wait_b(struct tst_fzsync_pair *pair)
 static inline int tst_fzsync_run_a(struct tst_fzsync_pair *pair)
 {
 	int exit = 0;
-	float rem_p = 1 - tst_timeout_remaining() / pair->exec_time_start;
+	float rem_p = 1 - tst_remaining_runtime() / pair->exec_time_start;
 
 	if ((pair->exec_time_p * SAMPLING_SLICE < rem_p)
 		&& (pair->sampling > 0)) {
diff --git a/include/tst_test.h b/include/tst_test.h
index 6ad355506..491fedc3e 100644
--- a/include/tst_test.h
+++ b/include/tst_test.h
@@ -290,7 +290,12 @@ const char *tst_strsig(int sig);
  */
 const char *tst_strstatus(int status);
 
-unsigned int tst_timeout_remaining(void);
+/*
+ * Returns remaining test runtime. Test that runs for more than a few seconds
+ * should check if they should exit by calling this function regularly.
+ */
+unsigned int tst_remaining_runtime(void);
+
 unsigned int tst_multiply_timeout(unsigned int timeout);
 void tst_set_timeout(int timeout);
 
diff --git a/lib/newlib_tests/.gitignore b/lib/newlib_tests/.gitignore
index b95ead2c2..464d98aed 100644
--- a/lib/newlib_tests/.gitignore
+++ b/lib/newlib_tests/.gitignore
@@ -23,7 +23,6 @@ tst_safe_fileops
 tst_res_hexd
 tst_strstatus
 test17
-test18
 test19
 test20
 test22
@@ -43,3 +42,5 @@ test_macros02
 test_macros03
 tst_fuzzy_sync01
 tst_fuzzy_sync02
+test_runtime01
+test_runtime02
diff --git a/lib/newlib_tests/test18.c b/lib/newlib_tests/test_runtime01.c
similarity index 59%
rename from lib/newlib_tests/test18.c
rename to lib/newlib_tests/test_runtime01.c
index 026435d7d..56f5ac44e 100644
--- a/lib/newlib_tests/test18.c
+++ b/lib/newlib_tests/test_runtime01.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 /*
- * Copyright (c) 2018, Linux Test Project
+ * Copyright (c) 2021, Linux Test Project
  */
 
 #include <stdlib.h>
@@ -9,11 +9,10 @@
 
 static void run(void)
 {
-	do {
+	while (tst_remaining_runtime())
 		sleep(1);
-	} while (tst_timeout_remaining() >= 4);
 
-	tst_res(TPASS, "Timeout remaining: %d", tst_timeout_remaining());
+	tst_res(TPASS, "Timeout remaining: %d", tst_remaining_runtime());
 }
 
 static struct tst_test test = {
diff --git a/lib/newlib_tests/test_runtime02.c b/lib/newlib_tests/test_runtime02.c
new file mode 100644
index 000000000..12e4813ef
--- /dev/null
+++ b/lib/newlib_tests/test_runtime02.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021, Linux Test Project
+ */
+/*
+ * This test is set up so that the timeout is not long enough to guarantee
+ * enough runtime for two iterations, i.e. the timeout without offset and after
+ * scaling is too small and the tests ends up with TBROK.
+ *
+ * You can fix this by exporting LTP_MAX_TEST_RUNTIME=10 before executing the
+ * test, in that case the runtime would be divided between iterations and timeout
+ * adjusted so that it provides enough safeguards for the test to finish.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include "tst_test.h"
+
+static void run(void)
+{
+	while (tst_remaining_runtime())
+		sleep(1);
+
+	tst_res(TPASS, "Timeout remaining: %d", tst_remaining_runtime());
+}
+
+static struct tst_test test = {
+	.test_all = run,
+	.timeout = 5,
+	.test_variants = 2
+};
diff --git a/lib/tst_test.c b/lib/tst_test.c
index 7c9061d6d..23b52583a 100644
--- a/lib/tst_test.c
+++ b/lib/tst_test.c
@@ -62,6 +62,7 @@ struct results {
 	int warnings;
 	int broken;
 	unsigned int timeout;
+	unsigned int max_runtime;
 };
 
 static struct results *results;
@@ -1255,17 +1256,74 @@ static void sigint_handler(int sig LTP_ATTRIBUTE_UNUSED)
 	}
 }
 
-unsigned int tst_timeout_remaining(void)
+#define RUNTIME_TIMEOUT_OFFSET 5
+#define RUNTIME_TIMEOUT_SCALE  0.9
+
+static unsigned int timeout_to_runtime(void)
+{
+	if (results->timeout <= RUNTIME_TIMEOUT_OFFSET) {
+		tst_res(TWARN, "Timeout too short for runtime offset %i!",
+		        RUNTIME_TIMEOUT_OFFSET);
+		return 1;
+	}
+
+	return (results->timeout - RUNTIME_TIMEOUT_OFFSET) * RUNTIME_TIMEOUT_SCALE;
+}
+
+static unsigned int runtime_to_timeout(unsigned int runtime)
+{
+	return runtime / RUNTIME_TIMEOUT_SCALE + RUNTIME_TIMEOUT_OFFSET;
+}
+
+static unsigned int divide_runtime(unsigned int runtime)
+{
+	if (tst_test->test_variants)
+		runtime = 1.00 * runtime / tst_test->test_variants;
+
+	if (tst_test->all_filesystems)
+		runtime = 1.00 * runtime / tst_fs_max_types();
+
+	return runtime;
+}
+
+unsigned int tst_remaining_runtime(void)
 {
 	static struct timespec now;
 	unsigned int elapsed;
 
+	if (!results->max_runtime) {
+		const char *runtime = getenv("LTP_MAX_TEST_RUNTIME");
+
+		if (runtime) {
+			results->max_runtime = atoi(runtime);
+		} else {
+			results->max_runtime = timeout_to_runtime();
+		}
+
+		if (!results->max_runtime)
+			tst_brk(TBROK, "Test runtime too small!");
+
+
+		if (runtime_to_timeout(results->max_runtime) > results->timeout) {
+			results->timeout = runtime_to_timeout(results->max_runtime);
+			tst_res(TINFO, "runtime > timeout, adjusting test timeout to %u", results->timeout);
+			heartbeat();
+		}
+
+		results->max_runtime = divide_runtime(results->max_runtime);
+
+		if (!results->max_runtime)
+			tst_brk(TBROK, "Test runtime too small!");
+
+		tst_res(TINFO, "Max runtime per iteration %us", results->max_runtime);
+	}
+
 	if (tst_clock_gettime(CLOCK_MONOTONIC, &now))
 		tst_res(TWARN | TERRNO, "tst_clock_gettime() failed");
 
 	elapsed = (tst_timespec_diff_ms(now, tst_start_time) + 500) / 1000;
-	if (results->timeout > elapsed)
-		return results->timeout - elapsed;
+	if (results->max_runtime > elapsed)
+		return results->max_runtime - elapsed;
 
 	return 0;
 }
diff --git a/testcases/kernel/crypto/af_alg02.c b/testcases/kernel/crypto/af_alg02.c
index 31d30777c..26f184854 100644
--- a/testcases/kernel/crypto/af_alg02.c
+++ b/testcases/kernel/crypto/af_alg02.c
@@ -61,7 +61,7 @@ static void run(void)
 	TST_CHECKPOINT_WAIT(0);
 
 	while (pthread_kill(thr, 0) != ESRCH) {
-		if (tst_timeout_remaining() <= 10) {
+		if (!tst_remaining_runtime()) {
 			pthread_cancel(thr);
 			tst_brk(TBROK,
 				"Timed out while reading from request socket.");
diff --git a/testcases/kernel/crypto/pcrypt_aead01.c b/testcases/kernel/crypto/pcrypt_aead01.c
index 0609af9f6..7fe6aed14 100644
--- a/testcases/kernel/crypto/pcrypt_aead01.c
+++ b/testcases/kernel/crypto/pcrypt_aead01.c
@@ -55,7 +55,7 @@ void run(void)
 		if (TST_RET)
 			tst_brk(TBROK | TRERRNO, "del_alg");
 
-		if (tst_timeout_remaining() < 10) {
+		if (!tst_remaining_runtime()) {
 			tst_res(TINFO, "Time limit reached, stopping@"
 				"%d iterations", i);
 			break;
diff --git a/testcases/kernel/mem/mtest01/mtest01.c b/testcases/kernel/mem/mtest01/mtest01.c
index 9676ea4b5..b205722e3 100644
--- a/testcases/kernel/mem/mtest01/mtest01.c
+++ b/testcases/kernel/mem/mtest01/mtest01.c
@@ -155,10 +155,10 @@ static void child_loop_alloc(unsigned long long alloc_bytes)
 	}
 	if (dowrite)
 		tst_res(TINFO, "... [t=%d] %lu bytes allocated and used in child %d",
-				tst_timeout_remaining(), bytecount, getpid());
+				tst_remaining_runtime(), bytecount, getpid());
 	else
 		tst_res(TINFO, "... [t=%d] %lu bytes allocated only in child %d",
-				tst_timeout_remaining(), bytecount, getpid());
+				tst_remaining_runtime(), bytecount, getpid());
 
 	kill(getppid(), SIGRTMIN);
 	raise(SIGSTOP);
@@ -195,7 +195,7 @@ static void mem_test(void)
 
 	/* wait in the loop for all children finish allocating */
 	while (children_done < pid_cntr) {
-		if (tst_timeout_remaining() < STOP_THRESHOLD) {
+		if (!tst_remaining_runtime()) {
 			tst_res(TWARN,
 				"the remaininig time is not enough for testing");
 
diff --git a/testcases/kernel/mem/mtest06/mmap1.c b/testcases/kernel/mem/mtest06/mmap1.c
index 10c47c35c..699c20988 100644
--- a/testcases/kernel/mem/mtest06/mmap1.c
+++ b/testcases/kernel/mem/mtest06/mmap1.c
@@ -35,9 +35,6 @@
 #define GIGABYTE (1L*1024*1024*1024)
 #define TEST_FILENAME "ashfile"
 
-/* seconds remaining before reaching timeout */
-#define STOP_THRESHOLD 10
-
 #define PROGRESS_SEC 3
 
 static int file_size = 1024;
@@ -224,8 +221,8 @@ static void run(void)
 	pthread_t thid[2];
 	int start, last_update;
 
-	start = last_update = tst_timeout_remaining();
-	while (tst_timeout_remaining() > STOP_THRESHOLD) {
+	start = last_update = tst_remaining_runtime();
+	while (tst_remaining_runtime()) {
 		int fd = mkfile(file_size);
 
 		tst_atomic_store(0, &mapcnt);
@@ -240,11 +237,11 @@ static void run(void)
 
 		close(fd);
 
-		if (last_update - tst_timeout_remaining() >= PROGRESS_SEC) {
-			last_update = tst_timeout_remaining();
+		if (last_update - tst_remaining_runtime() >= PROGRESS_SEC) {
+			last_update = tst_remaining_runtime();
 			tst_res(TINFO, "[%03d] mapped: %lu, sigsegv hit: %lu, "
 				"threads spawned: %lu",
-				start - tst_timeout_remaining(),
+				start - tst_remaining_runtime(),
 				map_count, mapped_sigsegv_count,
 				threads_spawned);
 			tst_res(TINFO, "      repeated_reads: %ld, "
diff --git a/testcases/kernel/syscalls/move_pages/move_pages12.c b/testcases/kernel/syscalls/move_pages/move_pages12.c
index 220130f4b..fa45c41a5 100644
--- a/testcases/kernel/syscalls/move_pages/move_pages12.c
+++ b/testcases/kernel/syscalls/move_pages/move_pages12.c
@@ -153,7 +153,7 @@ static void do_test(unsigned int n)
 	void *ptr;
 	pid_t cpid = -1;
 	int status;
-	unsigned int twenty_percent = (tst_timeout_remaining() / 5);
+	unsigned int twenty_percent = (tst_remaining_runtime() / 5);
 
 	addr = SAFE_MMAP(NULL, tcases[n].tpages * hpsz, PROT_READ | PROT_WRITE,
 		MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
@@ -198,7 +198,7 @@ static void do_test(unsigned int n)
 
 		SAFE_MUNMAP(addr, tcases[n].tpages * hpsz);
 
-		if (tst_timeout_remaining() < twenty_percent)
+		if (tst_remaining_runtime() < twenty_percent)
 			break;
 	}
 
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [LTP] [PATCH 4/4] syscalls/writev03: Adjust test runtime
  2021-06-09 11:46 [LTP] [PATCH 0/4] Introduce a concept of test runtime cap Cyril Hrubis
                   ` (2 preceding siblings ...)
  2021-06-09 11:46 ` [LTP] [PATCH 3/4] lib: Introduce concept of max_test_runtime Cyril Hrubis
@ 2021-06-09 11:46 ` Cyril Hrubis
  2021-06-09 14:54 ` [LTP] [PATCH 0/4] Introduce a concept of test runtime cap Richard Palethorpe
  4 siblings, 0 replies; 14+ messages in thread
From: Cyril Hrubis @ 2021-06-09 11:46 UTC (permalink / raw)
  To: ltp

Bump the test timeout to 600s which gives us about 1 minute of runtime
per iteration.

Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
---
 testcases/kernel/syscalls/writev/writev03.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/testcases/kernel/syscalls/writev/writev03.c b/testcases/kernel/syscalls/writev/writev03.c
index d0c64743b..5408d0279 100644
--- a/testcases/kernel/syscalls/writev/writev03.c
+++ b/testcases/kernel/syscalls/writev/writev03.c
@@ -144,6 +144,7 @@ static struct tst_test test = {
 	.mntpoint = MNTPOINT,
 	.all_filesystems = 1,
 	.min_cpus = 2,
+	.timeout = 600,
 	.setup = setup,
 	.cleanup = cleanup,
 	.tags = (const struct tst_tag[]) {
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [LTP] [Automated-testing] [PATCH 3/4] lib: Introduce concept of max_test_runtime
  2021-06-09 11:46 ` [LTP] [PATCH 3/4] lib: Introduce concept of max_test_runtime Cyril Hrubis
@ 2021-06-09 13:24   ` Petr Vorel
  2021-06-09 13:32     ` Cyril Hrubis
  2021-06-09 14:44   ` [LTP] " Richard Palethorpe
  1 sibling, 1 reply; 14+ messages in thread
From: Petr Vorel @ 2021-06-09 13:24 UTC (permalink / raw)
  To: ltp

Hi Cyril,

> This is an attempt on how to handle a cap on a test runtime correctly it
> consists of several pieces namely:

> * The idea of test maximal runtime is uncoupled from  test timeout

>   - the maximal runtime is simply a cap for how long should an instance
>     of a test run, it's mainly used by CVE reproducers that attempt to
>     trigger a race until they run out of time, such test may exit sooner
>     but must not run longer than the cap

>   - the tst_timeout_remaining() is replaced with tst_remaining_runtime()
>     which accounts correctly for .test_variants and .all_filesystems

> * The default value for a test max_runtime is computed from test timeout

>   - we scale the timeout down so that the there is some room for test to
>     properly exit once test runtime was exhausted, this is our base for
>     a test max_runtime

>   - the scaled value is then divided, if needed, so that we end up a
>     correct maximal runtime for an instance of a test, i.e. we have
>     max runtime for an instance fork_testrun() that is inside of
>     .test_variants and .all_filesystems loops
Now "Max runtime per iteration" can vary, right? I.e. on .all_filesystems
runtime for each filesystems depends on number of filesystems? E.g. writev03.c
with setup .timeout = 600 on 2 filesystems is 5 min (300s), but with all 9
filesystems is about 1 min. We should document that author should expect max
number of filesystems. What happen with these values in the (long) future, when
LTP support new filesystem (or drop some)? This was a reason for me to define in
the test value for "Max runtime per iteration", not whole run.

>   - this also allows us to controll the test max runtime by setting a
>     test timeout

> * The maximal runtime, per whole test, can be passed down to the test

>   - If LTP_MAX_TEST_RUNTIME is set in test environment it's used as a
>     base for max_runtime instead of the scaled down timeout, it's still
>     divided into pieces so that we have correct runtime cap for an
>     fork_testrun() instance
LTP_MAX_TEST_RUNTIME should go to doc/user-guide.txt. I suppose you waiting for
a feedback before writing docs.

>   - We also make sure that test timeout is adjusted, if needed, to
>     accomodate for the new test runtime cap, i.e. if upscaled runtime is
>     greater than timeout, the test timeout is adjusted

> Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
> ---
>  include/tst_fuzzy_sync.h                      |  4 +-
>  include/tst_test.h                            |  7 +-
>  lib/newlib_tests/.gitignore                   |  3 +-
>  .../{test18.c => test_runtime01.c}            |  7 +-
>  lib/newlib_tests/test_runtime02.c             | 31 +++++++++
>  lib/tst_test.c                                | 64 ++++++++++++++++++-
>  testcases/kernel/crypto/af_alg02.c            |  2 +-
>  testcases/kernel/crypto/pcrypt_aead01.c       |  2 +-
>  testcases/kernel/mem/mtest01/mtest01.c        |  6 +-
>  testcases/kernel/mem/mtest06/mmap1.c          | 13 ++--
>  .../kernel/syscalls/move_pages/move_pages12.c |  4 +-
>  11 files changed, 117 insertions(+), 26 deletions(-)
>  rename lib/newlib_tests/{test18.c => test_runtime01.c} (59%)
+1 for test description instead of plain number.

...
> +++ b/lib/newlib_tests/test_runtime01.c
...
>  static void run(void)
>  {
> -	do {
> +	while (tst_remaining_runtime())
>  		sleep(1);
> -	} while (tst_timeout_remaining() >= 4);

> -	tst_res(TPASS, "Timeout remaining: %d", tst_timeout_remaining());
> +	tst_res(TPASS, "Timeout remaining: %d", tst_remaining_runtime());

There is a warning:
tst_test.c:1369: TINFO: Timeout per run is 0h 00m 05s
tst_test.c:1265: TWARN: Timeout too short for runtime offset 5!
tst_test.c:1309: TINFO: runtime > timeout, adjusting test timeout to 6
tst_test.c:1318: TINFO: Max runtime per iteration 1s
test_runtime01.c:15: TPASS: Timeout remaining: 0

Maybe test should use value without warning (i.e. 7).
Or is the warning intended to be the test output?

.timeout = 6 fails:

tst_test.c:1369: TINFO: Timeout per run is 0h 00m 06s
tst_test.c:1304: TBROK: Test runtime too small!

.timeout >= 7 is ok:
tst_test.c:1369: TINFO: Timeout per run is 0h 00m 07s
tst_test.c:1318: TINFO: Max runtime per iteration 1s
test_runtime01.c:15: TPASS: Timeout remaining: 0

...
> diff --git a/lib/tst_test.c b/lib/tst_test.c
> index 7c9061d6d..23b52583a 100644
> --- a/lib/tst_test.c
> +++ b/lib/tst_test.c

> -unsigned int tst_timeout_remaining(void)
> +#define RUNTIME_TIMEOUT_OFFSET 5
=> maybe define it to 6 to allow running with .timeout = 6?

> +#define RUNTIME_TIMEOUT_SCALE  0.9
> +
> +static unsigned int timeout_to_runtime(void)
> +{
> +	if (results->timeout <= RUNTIME_TIMEOUT_OFFSET) {
> +		tst_res(TWARN, "Timeout too short for runtime offset %i!",
> +		        RUNTIME_TIMEOUT_OFFSET);
> +		return 1;
> +	}
> +
> +	return (results->timeout - RUNTIME_TIMEOUT_OFFSET) * RUNTIME_TIMEOUT_SCALE;
> +}
> +
> +static unsigned int runtime_to_timeout(unsigned int runtime)
> +{
> +	return runtime / RUNTIME_TIMEOUT_SCALE + RUNTIME_TIMEOUT_OFFSET;
> +}
...

Also test_runtime02.c fails, is that intended?
tst_test.c:1374: TINFO: Timeout per run is 0h 00m 05s
tst_test.c:1265 timeout_to_runtime(): results->timeout: 5
tst_test.c:1266 timeout_to_runtime(): RUNTIME_TIMEOUT_OFFSET: 5
tst_test.c:1268: TWARN: Timeout too short for runtime offset 5!
tst_test.c:1314: TINFO: runtime > timeout, adjusting test timeout to 6
tst_test.c:1321: TBROK: Test runtime too small!

Kind regards,
Petr

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [LTP] [Automated-testing] [PATCH 3/4] lib: Introduce concept of max_test_runtime
  2021-06-09 13:24   ` [LTP] [Automated-testing] " Petr Vorel
@ 2021-06-09 13:32     ` Cyril Hrubis
  2021-06-09 14:05       ` Petr Vorel
  2021-06-11 15:07       ` Martin Doucha
  0 siblings, 2 replies; 14+ messages in thread
From: Cyril Hrubis @ 2021-06-09 13:32 UTC (permalink / raw)
  To: ltp

Hi!
> >   - the scaled value is then divided, if needed, so that we end up a
> >     correct maximal runtime for an instance of a test, i.e. we have
> >     max runtime for an instance fork_testrun() that is inside of
> >     .test_variants and .all_filesystems loops
> Now "Max runtime per iteration" can vary, right? I.e. on .all_filesystems
> runtime for each filesystems depends on number of filesystems? E.g. writev03.c
> with setup .timeout = 600 on 2 filesystems is 5 min (300s), but with all 9
> filesystems is about 1 min. We should document that author should expect max
> number of filesystems. What happen with these values in the (long) future, when
> LTP support new filesystem (or drop some)? This was a reason for me to define in
> the test value for "Max runtime per iteration", not whole run.

That's one of the downsides of this approach.

The reason why I choose this approach is that you can set upper cap for
the whole test run and not only for a single filesystem/variant.

Also this way the test timeout corresponds to the maximal test runtime.

Another option would be to redefine the timeout to be timeout per a
fork_testrun() instance, which would make the approach slightly easier
in some places, however that would mean either changing default test
timeout to much smaller value and annotating all long running tests.

Hmm, I guess that annotating all long running tests and changing default
timeout may be a good idea regardless this approach.

> >   - this also allows us to controll the test max runtime by setting a
> >     test timeout
> 
> > * The maximal runtime, per whole test, can be passed down to the test
> 
> >   - If LTP_MAX_TEST_RUNTIME is set in test environment it's used as a
> >     base for max_runtime instead of the scaled down timeout, it's still
> >     divided into pieces so that we have correct runtime cap for an
> >     fork_testrun() instance
> LTP_MAX_TEST_RUNTIME should go to doc/user-guide.txt. I suppose you waiting for
> a feedback before writing docs.

Yes I do not consider this to be finished patchset and I do expect that
it would need some changes.

> >   - We also make sure that test timeout is adjusted, if needed, to
> >     accomodate for the new test runtime cap, i.e. if upscaled runtime is
> >     greater than timeout, the test timeout is adjusted
> 
> > Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
> > ---
> >  include/tst_fuzzy_sync.h                      |  4 +-
> >  include/tst_test.h                            |  7 +-
> >  lib/newlib_tests/.gitignore                   |  3 +-
> >  .../{test18.c => test_runtime01.c}            |  7 +-
> >  lib/newlib_tests/test_runtime02.c             | 31 +++++++++
> >  lib/tst_test.c                                | 64 ++++++++++++++++++-
> >  testcases/kernel/crypto/af_alg02.c            |  2 +-
> >  testcases/kernel/crypto/pcrypt_aead01.c       |  2 +-
> >  testcases/kernel/mem/mtest01/mtest01.c        |  6 +-
> >  testcases/kernel/mem/mtest06/mmap1.c          | 13 ++--
> >  .../kernel/syscalls/move_pages/move_pages12.c |  4 +-
> >  11 files changed, 117 insertions(+), 26 deletions(-)
> >  rename lib/newlib_tests/{test18.c => test_runtime01.c} (59%)
> +1 for test description instead of plain number.
> 
> ...
> > +++ b/lib/newlib_tests/test_runtime01.c
> ...
> >  static void run(void)
> >  {
> > -	do {
> > +	while (tst_remaining_runtime())
> >  		sleep(1);
> > -	} while (tst_timeout_remaining() >= 4);
> 
> > -	tst_res(TPASS, "Timeout remaining: %d", tst_timeout_remaining());
> > +	tst_res(TPASS, "Timeout remaining: %d", tst_remaining_runtime());
> 
> There is a warning:
> tst_test.c:1369: TINFO: Timeout per run is 0h 00m 05s
> tst_test.c:1265: TWARN: Timeout too short for runtime offset 5!
> tst_test.c:1309: TINFO: runtime > timeout, adjusting test timeout to 6
> tst_test.c:1318: TINFO: Max runtime per iteration 1s
> test_runtime01.c:15: TPASS: Timeout remaining: 0

This is expected.

> Maybe test should use value without warning (i.e. 7).
> Or is the warning intended to be the test output?
> 
> .timeout = 6 fails:
> 
> tst_test.c:1369: TINFO: Timeout per run is 0h 00m 06s
> tst_test.c:1304: TBROK: Test runtime too small!

This is one of the corner cases that probably needs to be handled
differently.

> .timeout >= 7 is ok:
> tst_test.c:1369: TINFO: Timeout per run is 0h 00m 07s
> tst_test.c:1318: TINFO: Max runtime per iteration 1s
> test_runtime01.c:15: TPASS: Timeout remaining: 0
> 
> ...
> > diff --git a/lib/tst_test.c b/lib/tst_test.c
> > index 7c9061d6d..23b52583a 100644
> > --- a/lib/tst_test.c
> > +++ b/lib/tst_test.c
> 
> > -unsigned int tst_timeout_remaining(void)
> > +#define RUNTIME_TIMEOUT_OFFSET 5
> => maybe define it to 6 to allow running with .timeout = 6?
> 
> > +#define RUNTIME_TIMEOUT_SCALE  0.9
> > +
> > +static unsigned int timeout_to_runtime(void)
> > +{
> > +	if (results->timeout <= RUNTIME_TIMEOUT_OFFSET) {
> > +		tst_res(TWARN, "Timeout too short for runtime offset %i!",
> > +		        RUNTIME_TIMEOUT_OFFSET);
> > +		return 1;
> > +	}
> > +
> > +	return (results->timeout - RUNTIME_TIMEOUT_OFFSET) * RUNTIME_TIMEOUT_SCALE;
> > +}
> > +
> > +static unsigned int runtime_to_timeout(unsigned int runtime)
> > +{
> > +	return runtime / RUNTIME_TIMEOUT_SCALE + RUNTIME_TIMEOUT_OFFSET;
> > +}
> ...
> 
> Also test_runtime02.c fails, is that intended?
> tst_test.c:1374: TINFO: Timeout per run is 0h 00m 05s
> tst_test.c:1265 timeout_to_runtime(): results->timeout: 5
> tst_test.c:1266 timeout_to_runtime(): RUNTIME_TIMEOUT_OFFSET: 5
> tst_test.c:1268: TWARN: Timeout too short for runtime offset 5!
> tst_test.c:1314: TINFO: runtime > timeout, adjusting test timeout to 6
> tst_test.c:1321: TBROK: Test runtime too small!

Yes, this is also supposed to fail, it's written in the test comment as
well...

-- 
Cyril Hrubis
chrubis@suse.cz

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [LTP] [Automated-testing] [PATCH 3/4] lib: Introduce concept of max_test_runtime
  2021-06-09 14:05       ` Petr Vorel
@ 2021-06-09 13:43         ` Cyril Hrubis
  0 siblings, 0 replies; 14+ messages in thread
From: Cyril Hrubis @ 2021-06-09 13:43 UTC (permalink / raw)
  To: ltp

Hi!
> > Another option would be to redefine the timeout to be timeout per a
> > fork_testrun() instance, which would make the approach slightly easier
> > in some places, however that would mean either changing default test
> > timeout to much smaller value and annotating all long running tests.
> IMHO slightly better approach to me.
> 
> > Hmm, I guess that annotating all long running tests and changing default
> > timeout may be a good idea regardless this approach.
> +1

I can send a v2 if this approach ends up being prefered...

-- 
Cyril Hrubis
chrubis@suse.cz

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [LTP] [Automated-testing] [PATCH 3/4] lib: Introduce concept of max_test_runtime
  2021-06-09 13:32     ` Cyril Hrubis
@ 2021-06-09 14:05       ` Petr Vorel
  2021-06-09 13:43         ` Cyril Hrubis
  2021-06-11 15:07       ` Martin Doucha
  1 sibling, 1 reply; 14+ messages in thread
From: Petr Vorel @ 2021-06-09 14:05 UTC (permalink / raw)
  To: ltp

Hi Cyril,

> Hi!
> > >   - the scaled value is then divided, if needed, so that we end up a
> > >     correct maximal runtime for an instance of a test, i.e. we have
> > >     max runtime for an instance fork_testrun() that is inside of
> > >     .test_variants and .all_filesystems loops
> > Now "Max runtime per iteration" can vary, right? I.e. on .all_filesystems
> > runtime for each filesystems depends on number of filesystems? E.g. writev03.c
> > with setup .timeout = 600 on 2 filesystems is 5 min (300s), but with all 9
> > filesystems is about 1 min. We should document that author should expect max
> > number of filesystems. What happen with these values in the (long) future, when
> > LTP support new filesystem (or drop some)? This was a reason for me to define in
> > the test value for "Max runtime per iteration", not whole run.

> That's one of the downsides of this approach.

> The reason why I choose this approach is that you can set upper cap for
> the whole test run and not only for a single filesystem/variant.

> Also this way the test timeout corresponds to the maximal test runtime.

> Another option would be to redefine the timeout to be timeout per a
> fork_testrun() instance, which would make the approach slightly easier
> in some places, however that would mean either changing default test
> timeout to much smaller value and annotating all long running tests.
IMHO slightly better approach to me.

> Hmm, I guess that annotating all long running tests and changing default
> timeout may be a good idea regardless this approach.
+1

> > >   - this also allows us to controll the test max runtime by setting a
> > >     test timeout

> > > * The maximal runtime, per whole test, can be passed down to the test

> > >   - If LTP_MAX_TEST_RUNTIME is set in test environment it's used as a
> > >     base for max_runtime instead of the scaled down timeout, it's still
> > >     divided into pieces so that we have correct runtime cap for an
> > >     fork_testrun() instance
> > LTP_MAX_TEST_RUNTIME should go to doc/user-guide.txt. I suppose you waiting for
> > a feedback before writing docs.

> Yes I do not consider this to be finished patchset and I do expect that
> it would need some changes.
Sure.

> > >   - We also make sure that test timeout is adjusted, if needed, to
> > >     accomodate for the new test runtime cap, i.e. if upscaled runtime is
> > >     greater than timeout, the test timeout is adjusted

> > > Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
> > > ---
> > >  include/tst_fuzzy_sync.h                      |  4 +-
> > >  include/tst_test.h                            |  7 +-
> > >  lib/newlib_tests/.gitignore                   |  3 +-
> > >  .../{test18.c => test_runtime01.c}            |  7 +-
> > >  lib/newlib_tests/test_runtime02.c             | 31 +++++++++
> > >  lib/tst_test.c                                | 64 ++++++++++++++++++-
> > >  testcases/kernel/crypto/af_alg02.c            |  2 +-
> > >  testcases/kernel/crypto/pcrypt_aead01.c       |  2 +-
> > >  testcases/kernel/mem/mtest01/mtest01.c        |  6 +-
> > >  testcases/kernel/mem/mtest06/mmap1.c          | 13 ++--
> > >  .../kernel/syscalls/move_pages/move_pages12.c |  4 +-
> > >  11 files changed, 117 insertions(+), 26 deletions(-)
> > >  rename lib/newlib_tests/{test18.c => test_runtime01.c} (59%)
> > +1 for test description instead of plain number.

> > ...
> > > +++ b/lib/newlib_tests/test_runtime01.c
> > ...
> > >  static void run(void)
> > >  {
> > > -	do {
> > > +	while (tst_remaining_runtime())
> > >  		sleep(1);
> > > -	} while (tst_timeout_remaining() >= 4);

> > > -	tst_res(TPASS, "Timeout remaining: %d", tst_timeout_remaining());
> > > +	tst_res(TPASS, "Timeout remaining: %d", tst_remaining_runtime());

> > There is a warning:
> > tst_test.c:1369: TINFO: Timeout per run is 0h 00m 05s
> > tst_test.c:1265: TWARN: Timeout too short for runtime offset 5!
> > tst_test.c:1309: TINFO: runtime > timeout, adjusting test timeout to 6
> > tst_test.c:1318: TINFO: Max runtime per iteration 1s
> > test_runtime01.c:15: TPASS: Timeout remaining: 0

> This is expected.

> > Maybe test should use value without warning (i.e. 7).
> > Or is the warning intended to be the test output?

> > .timeout = 6 fails:

> > tst_test.c:1369: TINFO: Timeout per run is 0h 00m 06s
> > tst_test.c:1304: TBROK: Test runtime too small!

> This is one of the corner cases that probably needs to be handled
> differently.
+1

...
> > Also test_runtime02.c fails, is that intended?
> > tst_test.c:1374: TINFO: Timeout per run is 0h 00m 05s
> > tst_test.c:1265 timeout_to_runtime(): results->timeout: 5
> > tst_test.c:1266 timeout_to_runtime(): RUNTIME_TIMEOUT_OFFSET: 5
> > tst_test.c:1268: TWARN: Timeout too short for runtime offset 5!
> > tst_test.c:1314: TINFO: runtime > timeout, adjusting test timeout to 6
> > tst_test.c:1321: TBROK: Test runtime too small!

> Yes, this is also supposed to fail, it's written in the test comment as
> well...
I'm sorry to overlook this. Hope I'll finish test-c-run soon, so that we can
continue with expected test output for API tests.

Kind regards,
Petr

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [LTP] [PATCH 3/4] lib: Introduce concept of max_test_runtime
  2021-06-09 11:46 ` [LTP] [PATCH 3/4] lib: Introduce concept of max_test_runtime Cyril Hrubis
  2021-06-09 13:24   ` [LTP] [Automated-testing] " Petr Vorel
@ 2021-06-09 14:44   ` Richard Palethorpe
  1 sibling, 0 replies; 14+ messages in thread
From: Richard Palethorpe @ 2021-06-09 14:44 UTC (permalink / raw)
  To: ltp

Hello Cyril,

Cyril Hrubis <chrubis@suse.cz> writes:

> This is an attempt on how to handle a cap on a test runtime correctly it
> consists of several pieces namely:
>
> * The idea of test maximal runtime is uncoupled from  test timeout
>
>   - the maximal runtime is simply a cap for how long should an instance
>     of a test run, it's mainly used by CVE reproducers that attempt to
>     trigger a race until they run out of time, such test may exit sooner
>     but must not run longer than the cap
>
>   - the tst_timeout_remaining() is replaced with tst_remaining_runtime()
>     which accounts correctly for .test_variants and .all_filesystems
>
> * The default value for a test max_runtime is computed from test timeout
>
>   - we scale the timeout down so that the there is some room for test to
>     properly exit once test runtime was exhausted, this is our base for
>     a test max_runtime
>
>   - the scaled value is then divided, if needed, so that we end up a
>     correct maximal runtime for an instance of a test, i.e. we have
>     max runtime for an instance fork_testrun() that is inside of
>     .test_variants and .all_filesystems loops
>
>   - this also allows us to controll the test max runtime by setting a
>     test timeout
>
> * The maximal runtime, per whole test, can be passed down to the test
>
>   - If LTP_MAX_TEST_RUNTIME is set in test environment it's used as a
>     base for max_runtime instead of the scaled down timeout, it's still
>     divided into pieces so that we have correct runtime cap for an
>     fork_testrun() instance
>
>   - We also make sure that test timeout is adjusted, if needed, to
>     accomodate for the new test runtime cap, i.e. if upscaled runtime is
>     greater than timeout, the test timeout is adjusted
>
> Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
> ---
>  include/tst_fuzzy_sync.h                      |  4 +-
>  include/tst_test.h                            |  7 +-
>  lib/newlib_tests/.gitignore                   |  3 +-
>  .../{test18.c => test_runtime01.c}            |  7 +-
>  lib/newlib_tests/test_runtime02.c             | 31 +++++++++
>  lib/tst_test.c                                | 64 ++++++++++++++++++-
>  testcases/kernel/crypto/af_alg02.c            |  2 +-
>  testcases/kernel/crypto/pcrypt_aead01.c       |  2 +-
>  testcases/kernel/mem/mtest01/mtest01.c        |  6 +-
>  testcases/kernel/mem/mtest06/mmap1.c          | 13 ++--
>  .../kernel/syscalls/move_pages/move_pages12.c |  4 +-
>  11 files changed, 117 insertions(+), 26 deletions(-)
>  rename lib/newlib_tests/{test18.c => test_runtime01.c} (59%)
>  create mode 100644 lib/newlib_tests/test_runtime02.c
>
> diff --git a/include/tst_fuzzy_sync.h b/include/tst_fuzzy_sync.h
> index 8f97bb8f6..93adbb909 100644
> --- a/include/tst_fuzzy_sync.h
> +++ b/include/tst_fuzzy_sync.h
> @@ -319,7 +319,7 @@ static void tst_fzsync_pair_reset(struct tst_fzsync_pair *pair,
>  		SAFE_PTHREAD_CREATE(&pair->thread_b, 0, tst_fzsync_thread_wrapper, &wrap_run_b);
>  	}
>  
> -	pair->exec_time_start = (float)tst_timeout_remaining();
> +	pair->exec_time_start = (float)tst_remaining_runtime();
>  }
>  
>  /**
> @@ -663,7 +663,7 @@ static inline void tst_fzsync_wait_b(struct tst_fzsync_pair *pair)
>  static inline int tst_fzsync_run_a(struct tst_fzsync_pair *pair)
>  {
>  	int exit = 0;
> -	float rem_p = 1 - tst_timeout_remaining() / pair->exec_time_start;
> +	float rem_p = 1 - tst_remaining_runtime() / pair->exec_time_start;
>  
>  	if ((pair->exec_time_p * SAMPLING_SLICE < rem_p)
>  		&& (pair->sampling > 0)) {
> diff --git a/include/tst_test.h b/include/tst_test.h
> index 6ad355506..491fedc3e 100644
> --- a/include/tst_test.h
> +++ b/include/tst_test.h
> @@ -290,7 +290,12 @@ const char *tst_strsig(int sig);
>   */
>  const char *tst_strstatus(int status);
>  
> -unsigned int tst_timeout_remaining(void);
> +/*
> + * Returns remaining test runtime. Test that runs for more than a few seconds
> + * should check if they should exit by calling this function regularly.
> + */
> +unsigned int tst_remaining_runtime(void);
> +
>  unsigned int tst_multiply_timeout(unsigned int timeout);
>  void tst_set_timeout(int timeout);
>  
> diff --git a/lib/newlib_tests/.gitignore b/lib/newlib_tests/.gitignore
> index b95ead2c2..464d98aed 100644
> --- a/lib/newlib_tests/.gitignore
> +++ b/lib/newlib_tests/.gitignore
> @@ -23,7 +23,6 @@ tst_safe_fileops
>  tst_res_hexd
>  tst_strstatus
>  test17
> -test18
>  test19
>  test20
>  test22
> @@ -43,3 +42,5 @@ test_macros02
>  test_macros03
>  tst_fuzzy_sync01
>  tst_fuzzy_sync02
> +test_runtime01
> +test_runtime02
> diff --git a/lib/newlib_tests/test18.c b/lib/newlib_tests/test_runtime01.c
> similarity index 59%
> rename from lib/newlib_tests/test18.c
> rename to lib/newlib_tests/test_runtime01.c
> index 026435d7d..56f5ac44e 100644
> --- a/lib/newlib_tests/test18.c
> +++ b/lib/newlib_tests/test_runtime01.c
> @@ -1,6 +1,6 @@
>  // SPDX-License-Identifier: GPL-2.0-or-later
>  /*
> - * Copyright (c) 2018, Linux Test Project
> + * Copyright (c) 2021, Linux Test Project
>   */
>  
>  #include <stdlib.h>
> @@ -9,11 +9,10 @@
>  
>  static void run(void)
>  {
> -	do {
> +	while (tst_remaining_runtime())
>  		sleep(1);
> -	} while (tst_timeout_remaining() >= 4);
>  
> -	tst_res(TPASS, "Timeout remaining: %d", tst_timeout_remaining());
> +	tst_res(TPASS, "Timeout remaining: %d", tst_remaining_runtime());
>  }
>  
>  static struct tst_test test = {
> diff --git a/lib/newlib_tests/test_runtime02.c b/lib/newlib_tests/test_runtime02.c
> new file mode 100644
> index 000000000..12e4813ef
> --- /dev/null
> +++ b/lib/newlib_tests/test_runtime02.c
> @@ -0,0 +1,31 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (c) 2021, Linux Test Project
> + */
> +/*
> + * This test is set up so that the timeout is not long enough to guarantee
> + * enough runtime for two iterations, i.e. the timeout without offset and after
> + * scaling is too small and the tests ends up with TBROK.
> + *
> + * You can fix this by exporting LTP_MAX_TEST_RUNTIME=10 before executing the
> + * test, in that case the runtime would be divided between iterations and timeout
> + * adjusted so that it provides enough safeguards for the test to finish.
> + */
> +
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include "tst_test.h"
> +
> +static void run(void)
> +{
> +	while (tst_remaining_runtime())
> +		sleep(1);
> +
> +	tst_res(TPASS, "Timeout remaining: %d", tst_remaining_runtime());
> +}
> +
> +static struct tst_test test = {
> +	.test_all = run,
> +	.timeout = 5,
> +	.test_variants = 2
> +};
> diff --git a/lib/tst_test.c b/lib/tst_test.c
> index 7c9061d6d..23b52583a 100644
> --- a/lib/tst_test.c
> +++ b/lib/tst_test.c
> @@ -62,6 +62,7 @@ struct results {
>  	int warnings;
>  	int broken;
>  	unsigned int timeout;
> +	unsigned int max_runtime;
>  };
>  
>  static struct results *results;
> @@ -1255,17 +1256,74 @@ static void sigint_handler(int sig LTP_ATTRIBUTE_UNUSED)
>  	}
>  }
>  
> -unsigned int tst_timeout_remaining(void)
> +#define RUNTIME_TIMEOUT_OFFSET 5
> +#define RUNTIME_TIMEOUT_SCALE  0.9
> +
> +static unsigned int timeout_to_runtime(void)
> +{
> +	if (results->timeout <= RUNTIME_TIMEOUT_OFFSET) {
> +		tst_res(TWARN, "Timeout too short for runtime offset %i!",
> +		        RUNTIME_TIMEOUT_OFFSET);
> +		return 1;
> +	}
> +
> +	return (results->timeout - RUNTIME_TIMEOUT_OFFSET) * RUNTIME_TIMEOUT_SCALE;
> +}
> +
> +static unsigned int runtime_to_timeout(unsigned int runtime)
> +{
> +	return runtime / RUNTIME_TIMEOUT_SCALE + RUNTIME_TIMEOUT_OFFSET;
> +}
> +
> +static unsigned int divide_runtime(unsigned int runtime)
> +{
> +	if (tst_test->test_variants)
> +		runtime = 1.00 * runtime / tst_test->test_variants;
> +
> +	if (tst_test->all_filesystems)
> +		runtime = 1.00 * runtime / tst_fs_max_types();
> +
> +	return runtime;
> +}
> +
> +unsigned int tst_remaining_runtime(void)
>  {
>  	static struct timespec now;
>  	unsigned int elapsed;
>  
> +	if (!results->max_runtime) {
> +		const char *runtime = getenv("LTP_MAX_TEST_RUNTIME");
> +
> +		if (runtime) {
> +			results->max_runtime = atoi(runtime);

POSIX says atoi is deprecated. It should probably be strtoul().

> +		} else {
> +			results->max_runtime = timeout_to_runtime();
> +		}
> +
> +		if (!results->max_runtime)
> +			tst_brk(TBROK, "Test runtime too small!");
> +
> +
> +		if (runtime_to_timeout(results->max_runtime) >
> results->timeout) {

Maybe should rename the "results" struct?

It is turning into general shared test state.

-- 
Thank you,
Richard.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [LTP] [PATCH 0/4] Introduce a concept of test runtime cap
  2021-06-09 11:46 [LTP] [PATCH 0/4] Introduce a concept of test runtime cap Cyril Hrubis
                   ` (3 preceding siblings ...)
  2021-06-09 11:46 ` [LTP] [PATCH 4/4] syscalls/writev03: Adjust test runtime Cyril Hrubis
@ 2021-06-09 14:54 ` Richard Palethorpe
  4 siblings, 0 replies; 14+ messages in thread
From: Richard Palethorpe @ 2021-06-09 14:54 UTC (permalink / raw)
  To: ltp

Hello Cyril,

Cyril Hrubis <chrubis@suse.cz> writes:

> Recently we had a problem with tests that use .all_filesystems and
> tst_timeout_remaining().
>
> The problem is that the tst_timeout_remaining() function wasn't aware of
> the loop over fork_testrun() that executes a testrun for each supported
> filesystem, which caused such test, e.g. writev03, to run for
> unpredictable and long periods.
>
> This is an attempt how to solve this properly by uncoupling timeouts
> from test runtime so that test never runs for longer than its timeout.
>
> Cyril Hrubis (4):
>   lib: tst_supported_fs_types: Add tst_fs_max_types()
>   lib: tst_test: Move timeout scaling out of fork_testrun()
>   lib: Introduce concept of max_test_runtime
>   syscalls/writev03: Adjust test runtime
>
>  include/tst_fs.h                              |  5 ++
>  include/tst_fuzzy_sync.h                      |  4 +-
>  include/tst_test.h                            |  7 +-
>  lib/newlib_tests/.gitignore                   |  3 +-
>  .../{test18.c => test_runtime01.c}            |  7 +-
>  lib/newlib_tests/test_runtime02.c             | 31 +++++++
>  lib/tst_supported_fs_types.c                  |  5 ++
>  lib/tst_test.c                                | 87 ++++++++++++++++---
>  testcases/kernel/crypto/af_alg02.c            |  2 +-
>  testcases/kernel/crypto/pcrypt_aead01.c       |  2 +-
>  testcases/kernel/mem/mtest01/mtest01.c        |  6 +-
>  testcases/kernel/mem/mtest06/mmap1.c          | 13 ++-
>  .../kernel/syscalls/move_pages/move_pages12.c |  4 +-
>  testcases/kernel/syscalls/writev/writev03.c   |  1 +
>  14 files changed, 141 insertions(+), 36 deletions(-)
>  rename lib/newlib_tests/{test18.c => test_runtime01.c} (59%)
>  create mode 100644 lib/newlib_tests/test_runtime02.c
>
> -- 
> 2.31.1

The changes generally look good. I think the new environment variable
needs documenting in the user guide though.

-- 
Thank you,
Richard.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [LTP] [Automated-testing] [PATCH 3/4] lib: Introduce concept of max_test_runtime
  2021-06-09 13:32     ` Cyril Hrubis
  2021-06-09 14:05       ` Petr Vorel
@ 2021-06-11 15:07       ` Martin Doucha
  2021-06-13 19:44         ` Petr Vorel
  1 sibling, 1 reply; 14+ messages in thread
From: Martin Doucha @ 2021-06-11 15:07 UTC (permalink / raw)
  To: ltp

On 09. 06. 21 15:32, Cyril Hrubis wrote:
> Hi!
>>>   - the scaled value is then divided, if needed, so that we end up a
>>>     correct maximal runtime for an instance of a test, i.e. we have
>>>     max runtime for an instance fork_testrun() that is inside of
>>>     .test_variants and .all_filesystems loops
>> Now "Max runtime per iteration" can vary, right? I.e. on .all_filesystems
>> runtime for each filesystems depends on number of filesystems? E.g. writev03.c
>> with setup .timeout = 600 on 2 filesystems is 5 min (300s), but with all 9
>> filesystems is about 1 min. We should document that author should expect max
>> number of filesystems. What happen with these values in the (long) future, when
>> LTP support new filesystem (or drop some)? This was a reason for me to define in
>> the test value for "Max runtime per iteration", not whole run.
> 
> That's one of the downsides of this approach.
> 
> The reason why I choose this approach is that you can set upper cap for
> the whole test run and not only for a single filesystem/variant.
> 
> Also this way the test timeout corresponds to the maximal test runtime.
> 
> Another option would be to redefine the timeout to be timeout per a
> fork_testrun() instance, which would make the approach slightly easier
> in some places, however that would mean either changing default test
> timeout to much smaller value and annotating all long running tests.
> 
> Hmm, I guess that annotating all long running tests and changing default
> timeout may be a good idea regardless this approach.

Some fuzzysync tests have long run time by design because running too
few loops on broken systems will not trigger the bug. Limiting maximum
program execution time may be useful for quick smoke tests but it's not
usable for real test runs where we want reliable reproducibility.

I'd prefer adding a command line option to tst_test (e.g. -m) that would
just print test metadata, including total timeout of all fork_testrun()
subtests, and exit. Static metadata is not a sufficient solution for
this because the same test binary may have different runtimes on
different system configurations, for example because the list of
available filesystems may change arbitrarily between test runs. It'd be
great if test runners other than runltp-ng could get a straighforward
timeout number without reimplementing a calculation that may change in
future versions of LTP.

-- 
Martin Doucha   mdoucha@suse.cz
QA Engineer for Software Maintenance
SUSE LINUX, s.r.o.
CORSO IIa
Krizikova 148/34
186 00 Prague 8
Czech Republic

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [LTP] [Automated-testing] [PATCH 3/4] lib: Introduce concept of max_test_runtime
  2021-06-11 15:07       ` Martin Doucha
@ 2021-06-13 19:44         ` Petr Vorel
  2021-06-14  8:02           ` Richard Palethorpe
  0 siblings, 1 reply; 14+ messages in thread
From: Petr Vorel @ 2021-06-13 19:44 UTC (permalink / raw)
  To: ltp

Hi all,

> On 09. 06. 21 15:32, Cyril Hrubis wrote:
> > Hi!
> >>>   - the scaled value is then divided, if needed, so that we end up a
> >>>     correct maximal runtime for an instance of a test, i.e. we have
> >>>     max runtime for an instance fork_testrun() that is inside of
> >>>     .test_variants and .all_filesystems loops
> >> Now "Max runtime per iteration" can vary, right? I.e. on .all_filesystems
> >> runtime for each filesystems depends on number of filesystems? E.g. writev03.c
> >> with setup .timeout = 600 on 2 filesystems is 5 min (300s), but with all 9
> >> filesystems is about 1 min. We should document that author should expect max
> >> number of filesystems. What happen with these values in the (long) future, when
> >> LTP support new filesystem (or drop some)? This was a reason for me to define in
> >> the test value for "Max runtime per iteration", not whole run.

> > That's one of the downsides of this approach.

> > The reason why I choose this approach is that you can set upper cap for
> > the whole test run and not only for a single filesystem/variant.

> > Also this way the test timeout corresponds to the maximal test runtime.

> > Another option would be to redefine the timeout to be timeout per a
> > fork_testrun() instance, which would make the approach slightly easier
> > in some places, however that would mean either changing default test
> > timeout to much smaller value and annotating all long running tests.

> > Hmm, I guess that annotating all long running tests and changing default
> > timeout may be a good idea regardless this approach.

> Some fuzzysync tests have long run time by design because running too
> few loops on broken systems will not trigger the bug. Limiting maximum
> program execution time may be useful for quick smoke tests but it's not
> usable for real test runs where we want reliable reproducibility.
Interesting.

> I'd prefer adding a command line option to tst_test (e.g. -m) that would
> just print test metadata, including total timeout of all fork_testrun()
> subtests, and exit. Static metadata is not a sufficient solution for
FYI I suggested this some time ago with private chat with Cyril, he mentioned
that there were some problems with it. IMHO it'd be great to implement it.

> this because the same test binary may have different runtimes on
> different system configurations, for example because the list of
> available filesystems may change arbitrarily between test runs. It'd be
> great if test runners other than runltp-ng could get a straighforward
> timeout number without reimplementing a calculation that may change in
> future versions of LTP.
+1

Kind regards,
Petr

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [LTP] [Automated-testing] [PATCH 3/4] lib: Introduce concept of max_test_runtime
  2021-06-13 19:44         ` Petr Vorel
@ 2021-06-14  8:02           ` Richard Palethorpe
  0 siblings, 0 replies; 14+ messages in thread
From: Richard Palethorpe @ 2021-06-14  8:02 UTC (permalink / raw)
  To: ltp

Hello,

Petr Vorel <pvorel@suse.cz> writes:

> Hi all,
>
>> On 09. 06. 21 15:32, Cyril Hrubis wrote:
>> > Hi!
>> >>>   - the scaled value is then divided, if needed, so that we end up a
>> >>>     correct maximal runtime for an instance of a test, i.e. we have
>> >>>     max runtime for an instance fork_testrun() that is inside of
>> >>>     .test_variants and .all_filesystems loops
>> >> Now "Max runtime per iteration" can vary, right? I.e. on .all_filesystems
>> >> runtime for each filesystems depends on number of filesystems? E.g. writev03.c
>> >> with setup .timeout = 600 on 2 filesystems is 5 min (300s), but with all 9
>> >> filesystems is about 1 min. We should document that author should expect max
>> >> number of filesystems. What happen with these values in the (long) future, when
>> >> LTP support new filesystem (or drop some)? This was a reason for me to define in
>> >> the test value for "Max runtime per iteration", not whole run.
>
>> > That's one of the downsides of this approach.
>
>> > The reason why I choose this approach is that you can set upper cap for
>> > the whole test run and not only for a single filesystem/variant.
>
>> > Also this way the test timeout corresponds to the maximal test runtime.
>
>> > Another option would be to redefine the timeout to be timeout per a
>> > fork_testrun() instance, which would make the approach slightly easier
>> > in some places, however that would mean either changing default test
>> > timeout to much smaller value and annotating all long running tests.
>
>> > Hmm, I guess that annotating all long running tests and changing default
>> > timeout may be a good idea regardless this approach.
>
>> Some fuzzysync tests have long run time by design because running too
>> few loops on broken systems will not trigger the bug. Limiting maximum
>> program execution time may be useful for quick smoke tests but it's not
>> usable for real test runs where we want reliable reproducibility.
> Interesting.
>
>> I'd prefer adding a command line option to tst_test (e.g. -m) that would
>> just print test metadata, including total timeout of all fork_testrun()
>> subtests, and exit. Static metadata is not a sufficient solution for
> FYI I suggested this some time ago with private chat with Cyril, he mentioned
> that there were some problems with it. IMHO it'd be great to implement
> it.

Yes, it has been debated before. It may be an issue when cross
compiling. Also verifying whether a test should really produce TCONF. I
don't think it can be the primary way of extracting meta data. OTOH, it
really makes sense for the test to report some info to the test
runner. Including expected runtime and what environment it can see.

The test runner can compare this data with its expectations. For
example, if the test reports there is X NUMA nodes, but the runner
thinks there should be Y NUMA nodes. This can help to verify people's
configuration.

>
>> this because the same test binary may have different runtimes on
>> different system configurations, for example because the list of
>> available filesystems may change arbitrarily between test runs. It'd be
>> great if test runners other than runltp-ng could get a straighforward
>> timeout number without reimplementing a calculation that may change in
>> future versions of LTP.

Other possibilities are that a test takes much longer to run on single
core or larger page size. I have also theorised before that fuzzysync
could measure the first few loops and tune the timeouts based on that. I
don't think it is necessary, but that can change.

-- 
Thank you,
Richard.

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2021-06-14  8:02 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-09 11:46 [LTP] [PATCH 0/4] Introduce a concept of test runtime cap Cyril Hrubis
2021-06-09 11:46 ` [LTP] [PATCH 1/4] lib: tst_supported_fs_types: Add tst_fs_max_types() Cyril Hrubis
2021-06-09 11:46 ` [LTP] [PATCH 2/4] lib: tst_test: Move timeout scaling out of fork_testrun() Cyril Hrubis
2021-06-09 11:46 ` [LTP] [PATCH 3/4] lib: Introduce concept of max_test_runtime Cyril Hrubis
2021-06-09 13:24   ` [LTP] [Automated-testing] " Petr Vorel
2021-06-09 13:32     ` Cyril Hrubis
2021-06-09 14:05       ` Petr Vorel
2021-06-09 13:43         ` Cyril Hrubis
2021-06-11 15:07       ` Martin Doucha
2021-06-13 19:44         ` Petr Vorel
2021-06-14  8:02           ` Richard Palethorpe
2021-06-09 14:44   ` [LTP] " Richard Palethorpe
2021-06-09 11:46 ` [LTP] [PATCH 4/4] syscalls/writev03: Adjust test runtime Cyril Hrubis
2021-06-09 14:54 ` [LTP] [PATCH 0/4] Introduce a concept of test runtime cap Richard Palethorpe

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.