diff options
| author | Mark Brown <broonie@kernel.org> | 2020-12-11 17:47:55 +0000 | 
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2020-12-11 17:47:55 +0000 | 
| commit | 031616c434db05ce766f76c62865f55698e0924f (patch) | |
| tree | 7f29aa1ff3e7b51a8058cd570fb785c6e769b245 /tools/testing/selftests/rseq/param_test.c | |
| parent | 064841ccfc49b2315dc0b797239862d3a343aa07 (diff) | |
| parent | 85a7555575a0e48f9b73db310d0d762a08a46d63 (diff) | |
Merge remote-tracking branch 'asoc/for-5.10' into asoc-linus
Diffstat (limited to 'tools/testing/selftests/rseq/param_test.c')
| -rw-r--r-- | tools/testing/selftests/rseq/param_test.c | 223 | 
1 files changed, 222 insertions, 1 deletions
diff --git a/tools/testing/selftests/rseq/param_test.c b/tools/testing/selftests/rseq/param_test.c index e8a657a5f48a..384589095864 100644 --- a/tools/testing/selftests/rseq/param_test.c +++ b/tools/testing/selftests/rseq/param_test.c @@ -1,8 +1,10 @@  // SPDX-License-Identifier: LGPL-2.1  #define _GNU_SOURCE  #include <assert.h> +#include <linux/membarrier.h>  #include <pthread.h>  #include <sched.h> +#include <stdatomic.h>  #include <stdint.h>  #include <stdio.h>  #include <stdlib.h> @@ -1131,6 +1133,220 @@ static int set_signal_handler(void)  	return ret;  } +struct test_membarrier_thread_args { +	int stop; +	intptr_t percpu_list_ptr; +}; + +/* Worker threads modify data in their "active" percpu lists. */ +void *test_membarrier_worker_thread(void *arg) +{ +	struct test_membarrier_thread_args *args = +		(struct test_membarrier_thread_args *)arg; +	const int iters = opt_reps; +	int i; + +	if (rseq_register_current_thread()) { +		fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n", +			errno, strerror(errno)); +		abort(); +	} + +	/* Wait for initialization. */ +	while (!atomic_load(&args->percpu_list_ptr)) {} + +	for (i = 0; i < iters; ++i) { +		int ret; + +		do { +			int cpu = rseq_cpu_start(); + +			ret = rseq_offset_deref_addv(&args->percpu_list_ptr, +				sizeof(struct percpu_list_entry) * cpu, 1, cpu); +		} while (rseq_unlikely(ret)); +	} + +	if (rseq_unregister_current_thread()) { +		fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n", +			errno, strerror(errno)); +		abort(); +	} +	return NULL; +} + +void test_membarrier_init_percpu_list(struct percpu_list *list) +{ +	int i; + +	memset(list, 0, sizeof(*list)); +	for (i = 0; i < CPU_SETSIZE; i++) { +		struct percpu_list_node *node; + +		node = malloc(sizeof(*node)); +		assert(node); +		node->data = 0; +		node->next = NULL; +		list->c[i].head = node; +	} +} + +void test_membarrier_free_percpu_list(struct percpu_list *list) +{ +	int i; + +	for (i = 0; i < CPU_SETSIZE; i++) +		free(list->c[i].head); +} + +static int sys_membarrier(int cmd, int flags, int cpu_id) +{ +	return syscall(__NR_membarrier, cmd, flags, cpu_id); +} + +/* + * The manager thread swaps per-cpu lists that worker threads see, + * and validates that there are no unexpected modifications. + */ +void *test_membarrier_manager_thread(void *arg) +{ +	struct test_membarrier_thread_args *args = +		(struct test_membarrier_thread_args *)arg; +	struct percpu_list list_a, list_b; +	intptr_t expect_a = 0, expect_b = 0; +	int cpu_a = 0, cpu_b = 0; + +	if (rseq_register_current_thread()) { +		fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n", +			errno, strerror(errno)); +		abort(); +	} + +	/* Init lists. */ +	test_membarrier_init_percpu_list(&list_a); +	test_membarrier_init_percpu_list(&list_b); + +	atomic_store(&args->percpu_list_ptr, (intptr_t)&list_a); + +	while (!atomic_load(&args->stop)) { +		/* list_a is "active". */ +		cpu_a = rand() % CPU_SETSIZE; +		/* +		 * As list_b is "inactive", we should never see changes +		 * to list_b. +		 */ +		if (expect_b != atomic_load(&list_b.c[cpu_b].head->data)) { +			fprintf(stderr, "Membarrier test failed\n"); +			abort(); +		} + +		/* Make list_b "active". */ +		atomic_store(&args->percpu_list_ptr, (intptr_t)&list_b); +		if (sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ, +					MEMBARRIER_CMD_FLAG_CPU, cpu_a) && +				errno != ENXIO /* missing CPU */) { +			perror("sys_membarrier"); +			abort(); +		} +		/* +		 * Cpu A should now only modify list_b, so the values +		 * in list_a should be stable. +		 */ +		expect_a = atomic_load(&list_a.c[cpu_a].head->data); + +		cpu_b = rand() % CPU_SETSIZE; +		/* +		 * As list_a is "inactive", we should never see changes +		 * to list_a. +		 */ +		if (expect_a != atomic_load(&list_a.c[cpu_a].head->data)) { +			fprintf(stderr, "Membarrier test failed\n"); +			abort(); +		} + +		/* Make list_a "active". */ +		atomic_store(&args->percpu_list_ptr, (intptr_t)&list_a); +		if (sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ, +					MEMBARRIER_CMD_FLAG_CPU, cpu_b) && +				errno != ENXIO /* missing CPU*/) { +			perror("sys_membarrier"); +			abort(); +		} +		/* Remember a value from list_b. */ +		expect_b = atomic_load(&list_b.c[cpu_b].head->data); +	} + +	test_membarrier_free_percpu_list(&list_a); +	test_membarrier_free_percpu_list(&list_b); + +	if (rseq_unregister_current_thread()) { +		fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n", +			errno, strerror(errno)); +		abort(); +	} +	return NULL; +} + +/* Test MEMBARRIER_CMD_PRIVATE_RESTART_RSEQ_ON_CPU membarrier command. */ +#ifdef RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV +void test_membarrier(void) +{ +	const int num_threads = opt_threads; +	struct test_membarrier_thread_args thread_args; +	pthread_t worker_threads[num_threads]; +	pthread_t manager_thread; +	int i, ret; + +	if (sys_membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ, 0, 0)) { +		perror("sys_membarrier"); +		abort(); +	} + +	thread_args.stop = 0; +	thread_args.percpu_list_ptr = 0; +	ret = pthread_create(&manager_thread, NULL, +			test_membarrier_manager_thread, &thread_args); +	if (ret) { +		errno = ret; +		perror("pthread_create"); +		abort(); +	} + +	for (i = 0; i < num_threads; i++) { +		ret = pthread_create(&worker_threads[i], NULL, +				test_membarrier_worker_thread, &thread_args); +		if (ret) { +			errno = ret; +			perror("pthread_create"); +			abort(); +		} +	} + + +	for (i = 0; i < num_threads; i++) { +		ret = pthread_join(worker_threads[i], NULL); +		if (ret) { +			errno = ret; +			perror("pthread_join"); +			abort(); +		} +	} + +	atomic_store(&thread_args.stop, 1); +	ret = pthread_join(manager_thread, NULL); +	if (ret) { +		errno = ret; +		perror("pthread_join"); +		abort(); +	} +} +#else /* RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV */ +void test_membarrier(void) +{ +	fprintf(stderr, "rseq_offset_deref_addv is not implemented on this architecture. " +			"Skipping membarrier test.\n"); +} +#endif +  static void show_usage(int argc, char **argv)  {  	printf("Usage : %s <OPTIONS>\n", @@ -1153,7 +1369,7 @@ static void show_usage(int argc, char **argv)  	printf("	[-r N] Number of repetitions per thread (default 5000)\n");  	printf("	[-d] Disable rseq system call (no initialization)\n");  	printf("	[-D M] Disable rseq for each M threads\n"); -	printf("	[-T test] Choose test: (s)pinlock, (l)ist, (b)uffer, (m)emcpy, (i)ncrement\n"); +	printf("	[-T test] Choose test: (s)pinlock, (l)ist, (b)uffer, (m)emcpy, (i)ncrement, membarrie(r)\n");  	printf("	[-M] Push into buffer and memcpy buffer with memory barriers.\n");  	printf("	[-v] Verbose output.\n");  	printf("	[-h] Show this help.\n"); @@ -1268,6 +1484,7 @@ int main(int argc, char **argv)  			case 'i':  			case 'b':  			case 'm': +			case 'r':  				break;  			default:  				show_usage(argc, argv); @@ -1320,6 +1537,10 @@ int main(int argc, char **argv)  		printf_verbose("counter increment\n");  		test_percpu_inc();  		break; +	case 'r': +		printf_verbose("membarrier\n"); +		test_membarrier(); +		break;  	}  	if (!opt_disable_rseq && rseq_unregister_current_thread())  		abort();  | 
