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

Avinesh Kumar akumar@suse.de
Tue Jun 17 14:18:22 CEST 2025


Hi Li,

Thank you for this refactoring and fixing the pec test regression,
I tested this patchset.

Tested-by: Avinesh Kumar <akumar@suse.de>


Regards,
Avinesh

On Monday, June 16, 2025 12:26:17 PM CEST Li Wang via ltp wrote:
> 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()
> 






More information about the ltp mailing list