[LTP] [PATCH 1/3] shell/lib: refactor checkpoint with shared path for exec() support

Li Wang liwang@redhat.com
Mon Jun 16 12:26:17 CEST 2025


This patch refactors shell checkpoint mechanism to support exec()
based process synchronization by introducing support for a shared
futex region via a configurable path.

Key changes:

 - Introduce LTP_IPC_PATH environment variable to specify the futex
   shared memory file used for checkpoint synchronization.
 - Add magic header "LTPM" to validate the shared memory file content.
 - Add checkpoint_reinit() to re-attach futex mapping for wait/wake
   operations in exec()'ed processes.
 - Update tst_checkpoint CLI tool to support "init", "wait", and "wake"
   commands with a unified interface.

Signed-off-by: Li Wang <liwang@redhat.com>
---
 include/tst_checkpoint_fn.h    | 20 +++++++++-
 lib/tst_checkpoint.c           | 70 +++++++++++++++++++++++-----------
 testcases/lib/tst_checkpoint.c | 27 +++++++------
 testcases/lib/tst_test.sh      |  2 +
 4 files changed, 83 insertions(+), 36 deletions(-)

diff --git a/include/tst_checkpoint_fn.h b/include/tst_checkpoint_fn.h
index 3a010d616..209296fe0 100644
--- a/include/tst_checkpoint_fn.h
+++ b/include/tst_checkpoint_fn.h
@@ -6,13 +6,29 @@
 #define TST_CHECKPOINT_FN__
 
 /*
- * Checkpoint initializaton, must be done first.
+ * Checkpoint initialization.
  *
- * NOTE: tst_tmpdir() must be called beforehand.
+ * This function sets up the shared memory region used for process
+ * synchronization via futexes. It must be called before any checkpoint
+ * operations such as tst_checkpoint_wait() or tst_checkpoint_wake().
  */
 void tst_checkpoint_init(const char *file, const int lineno,
 			 void (*cleanup_fn)(void));
 
+/*
+ * Checkpoint reinitialization.
+ *
+ * This function re-attaches to an existing shared memory checkpoint region
+ * pointed to by the LTP_IPC_PATH environment variable. It is typically used
+ * in child processes (e.g., shell scripts) to synchronize with the main test.
+ *
+ * The function verifies the magic header in the shared memory file and maps
+ * the futex array into memory. It must be called before using checkpoint
+ * operations in a process that did not perform the original initialization.
+ */
+void tst_checkpoint_reinit(const char *file, const int lineno,
+			   void (*cleanup_fn)(void));
+
 /*
  * Waits for wakeup.
  *
diff --git a/lib/tst_checkpoint.c b/lib/tst_checkpoint.c
index 6a294b28b..5efbf98d2 100644
--- a/lib/tst_checkpoint.c
+++ b/lib/tst_checkpoint.c
@@ -38,8 +38,9 @@ unsigned int tst_max_futexes;
 void tst_checkpoint_init(const char *file, const int lineno,
                          void (*cleanup_fn)(void))
 {
+	char *path = getenv("LTP_IPC_PATH");
+	size_t page_size = getpagesize();
 	int fd;
-	unsigned int page_size;
 
 	if (tst_futexes) {
 		tst_brkm_(file, lineno, TBROK, cleanup_fn,
@@ -47,35 +48,58 @@ void tst_checkpoint_init(const char *file, const int lineno,
 		return;
 	}
 
-	/*
-	 * The parent test process is responsible for creating the temporary
-	 * directory and therefore must pass non-zero cleanup (to remove the
-	 * directory if something went wrong).
-	 *
-	 * We cannot do this check unconditionally because if we need to init
-	 * the checkpoint from a binary that was started by exec() the
-	 * tst_tmpdir_created() will return false because the tmpdir was
-	 * created by parent. In this case we expect the subprogram can call
-	 * the init as a first function with NULL as cleanup function.
-	 */
-	if (cleanup_fn && !tst_tmpdir_created()) {
-		tst_brkm_(file, lineno, TBROK, cleanup_fn,
-			"You have to create test temporary directory "
-			"first (call tst_tmpdir())");
-		return;
+	if (!path) {
+		char *tmp_path = NULL;
+
+		if (!tst_tmpdir_created())
+			tst_tmpdir();
+
+		safe_asprintf(__FILE__, __LINE__, cleanup_fn, &tmp_path,
+				"%s/ltp_checkpoint", tst_get_tmpdir());
+		path = tmp_path;
 	}
 
-	page_size = getpagesize();
+	fd = SAFE_OPEN(cleanup_fn, path, O_RDWR | O_CREAT, 0666);
+	SAFE_WRITE(cleanup_fn, 1, fd, "LTPM", 4);
+
+	tst_futexes = SAFE_MMAP(cleanup_fn, NULL, page_size,
+				PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+	tst_futexes = (futex_t *)((char *)tst_futexes + 4);
+	tst_max_futexes = (page_size - 4) / sizeof(futex_t);
+
+	SAFE_CLOSE(cleanup_fn, fd);
+}
+
+void tst_checkpoint_reinit(const char *file, const int lineno,
+			   void (*cleanup_fn)(void))
+{
+	const char *path = getenv("LTP_IPC_PATH");
+	size_t page_size = getpagesize();
+	int fd;
 
-	fd = SAFE_OPEN(cleanup_fn, "checkpoint_futex_base_file",
-	               O_RDWR | O_CREAT, 0666);
+	if (!path) {
+		tst_brkm_(file, lineno, TBROK, cleanup_fn,
+				"LTP_IPC_PATH is not defined");
+	}
 
-	SAFE_FTRUNCATE(cleanup_fn, fd, page_size);
+	if (access(path, F_OK)) {
+		tst_brkm_(file, lineno, TBROK, cleanup_fn,
+				"File %s does not exist!", path);
+	}
 
+	fd = SAFE_OPEN(cleanup_fn, path, O_RDWR);
 	tst_futexes = SAFE_MMAP(cleanup_fn, NULL, page_size,
-	                    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+			PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+	char *ptr = (char *)tst_futexes;
+	if (memcmp(ptr, "LTPM", 4) != 0) {
+		tst_brkm_(file, lineno, TBROK, cleanup_fn,
+				"Invalid shared memory region (bad magic)");
+	}
 
-	tst_max_futexes = page_size / sizeof(uint32_t);
+	tst_futexes = (futex_t *)((char *)tst_futexes + 4);
+	tst_max_futexes = (page_size - 4) / sizeof(futex_t);
 
 	SAFE_CLOSE(cleanup_fn, fd);
 }
diff --git a/testcases/lib/tst_checkpoint.c b/testcases/lib/tst_checkpoint.c
index c70c4e8e5..35a0c0dfa 100644
--- a/testcases/lib/tst_checkpoint.c
+++ b/testcases/lib/tst_checkpoint.c
@@ -9,11 +9,14 @@
 #include <stdlib.h>
 #define TST_NO_DEFAULT_MAIN
 #include "tst_test.h"
+#include "tst_checkpoint.h"
 
 static void print_help(void)
 {
-	printf("Usage: tst_checkpoint wait TIMEOUT ID\n");
+	printf("Usage: tst_checkpoint init\n");
+	printf("   or: tst_checkpoint wait TIMEOUT ID\n");
 	printf("   or: tst_checkpoint wake TIMEOUT ID NR_WAKE\n");
+	printf("Arguments:\n");
 	printf("       TIMEOUT - timeout in ms\n");
 	printf("       ID - checkpoint id\n");
 	printf("       NR_WAKE - number of processes to wake up\n");
@@ -43,8 +46,13 @@ int main(int argc, char *argv[])
 	int ret;
 	int type;
 
-	if (argc < 3)
-		goto help;
+	if (!strcmp(argv[1], "init")) {
+		if (argc != 2)
+			goto help;
+
+		tst_checkpoint_init(__FILE__, __LINE__, NULL);
+		return 0;
+	}
 
 	if (!strcmp(argv[1], "wait")) {
 		type = 0;
@@ -70,17 +78,14 @@ int main(int argc, char *argv[])
 		goto help;
 	}
 
-	tst_reinit();
+	tst_checkpoint_reinit(__FILE__, __LINE__, NULL);
 
-	if (type)
-		ret = tst_checkpoint_wake(id, nr_wake, timeout);
-	else
+	if (type == 0)
 		ret = tst_checkpoint_wait(id, timeout);
-
-	if (ret)
-		return 1;
 	else
-		return 0;
+		ret = tst_checkpoint_wake(id, nr_wake, timeout);
+
+	return ret ? 1 : 0;
 
 help:
 	print_help();
diff --git a/testcases/lib/tst_test.sh b/testcases/lib/tst_test.sh
index c32bd8b19..e5e76067b 100644
--- a/testcases/lib/tst_test.sh
+++ b/testcases/lib/tst_test.sh
@@ -629,6 +629,8 @@ _tst_init_checkpoints()
 	ROD_SILENT dd if=/dev/zero of="$LTP_IPC_PATH" bs="$pagesize" count=1
 	ROD_SILENT chmod 600 "$LTP_IPC_PATH"
 	export LTP_IPC_PATH
+
+	tst_checkpoint init
 }
 
 _prepare_device()
-- 
2.49.0



More information about the ltp mailing list