Re: [PATCHv2 2/2] lib/percpu_test: Add extra tests in percpu_test

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello,

On Tue, Aug 24, 2021 at 02:12:30AM +0530, Ritesh Harjani wrote:
> While debugging a issue, we needed to stress test the percpu alloc/free
> path. Hence added some tests in lib/percpu_test to stress test
> percpu subsystem for allocation with different sizes.
> 

Can you explain more about the problem you are trying to debug and why
it required stressing the percpu allocator?

> This patch keeps the default behavior of insmod module same for default
> test. But when given insmod with different option, it can run a
> percpu_stressd daemon (percpu_test_num=2) which does a stress test
> evey 10secs unless the module is unloaded.
> 
> We found this to be helpful in our testing, since with this we could
> easily excercise percpu allo/free path. Hence cleaned this up for
> inclusion in percpu_test module.
> 
> Logs
> ======
> qemu-> sudo insmod /mnt/percpu_test.ko percpu_test_num=0
> [  334.362973] percpu_test: INIT, interval: 1000, max_shift: 13, run_tests: percpu_verify
> [  334.364946] TEST Starts: percpu_verify
> [  334.365601] TEST Completed: percpu_verify
> insmod: ERROR: could not insert module /mnt/percpu_test.ko: Resource temporarily unavailable

^ this seems wrong? What am I missing.

> 
> qemu-> sudo insmod /mnt/percpu_test.ko percpu_test_num=1
> [  336.556464] percpu_test: INIT, interval: 1000, max_shift: 13, run_tests: percpu_stress
> [  336.558388] TEST Starts: percpu_stress
> [  336.560611] TEST Completed: percpu_stress
> insmod: ERROR: could not insert module /mnt/percpu_test.ko: Resource temporarily unavailable
> 
> qemu-> sudo insmod /mnt/percpu_test.ko percpu_test_num=2
> [  339.164406] percpu_test: INIT, interval: 1000, max_shift: 13, run_tests: percpu_stressd
> [  339.165935] TEST Starts: percpu_stressd
> [  339.167033] TEST Completed: percpu_stressd
> [  339.167082] DAEMON: starts percpu_stressd
> [  339.168498] TEST Starts: percpu_stressd: iter (1)
> [  339.182530] TEST Completed: percpu_stressd: iter (1)
> [  349.341109] TEST Starts: percpu_stressd: iter (2)
> [  349.344447] TEST Completed: percpu_stressd: iter (2)
> [  359.580829] TEST Starts: percpu_stressd: iter (3)
> [  359.584315] TEST Completed: percpu_stressd: iter (3)
> [  369.820471] TEST Starts: percpu_stressd: iter (4)
> [  369.844402] TEST Completed: percpu_stressd: iter (4)
> 
> qemu-> sudo rmmod percpu_test
> [  375.001098] percpu_test: EXIT
> [qemu][~]
> 
> Cc: Aneesh Kumar K.V <aneesh.kumar@xxxxxxxxxxxxx>
> Cc: Vaibhav Jain <vaibhav@xxxxxxxxxxxxx>
> Reported-by: kernel test robot <lkp@xxxxxxxxx>
> Signed-off-by: Ritesh Harjani <riteshh@xxxxxxxxxxxxx>
> ---
> [v1 -> v2]: Fix warnings from kernel test robot
> 
>  lib/percpu_test.c | 240 ++++++++++++++++++++++++++++++++++++----------
>  1 file changed, 191 insertions(+), 49 deletions(-)
> 
> diff --git a/lib/percpu_test.c b/lib/percpu_test.c
> index 4a3d70bbc1a0..68c57c288dc6 100644
> --- a/lib/percpu_test.c
> +++ b/lib/percpu_test.c
> @@ -1,4 +1,7 @@
>  // SPDX-License-Identifier: GPL-2.0-only
> +#include <linux/workqueue.h>
> +#include <linux/kthread.h>
> +#include <linux/cpu.h>
>  #include <linux/module.h>
> 
>  /* validate @native and @pcp counter values match @expected */
> @@ -14,10 +17,25 @@
>  		     (long long)(expected), (long long)(expected));	\
>  	} while (0)
> 
> -static DEFINE_PER_CPU(long, long_counter);
> -static DEFINE_PER_CPU(unsigned long, ulong_counter);

^ These are good to keep fwiw as it's one of the few instances of using
the reserved region of the first chunk of percpu memory.

> +/* upto max alloc size tests for percpu var */
> +static char __percpu *counters[1 << PAGE_SHIFT];
> +static struct task_struct *percpu_stressd_thread;
> 
> -static int __init percpu_test_init(void)
> +/* let's not trigger OOM */
> +int percpu_alloc_max_size_shift = PAGE_SHIFT - 3;
> +module_param(percpu_alloc_max_size_shift, int, 0644);
> +MODULE_PARM_DESC(percpu_alloc_max_size_shift, "max size of allocation in stress test will be upto 1 << percpu_alloc_max_size_shift");
> +
> +static long percpu_stressd_interval = 1 * 10 * HZ;
> +module_param(percpu_stressd_interval, long, 0644);
> +MODULE_PARM_DESC(percpu_stressd_interval, "percpu_stressd internal");
> +
> +/* keep the default test same */
> +static int percpu_test_num;
> +module_param(percpu_test_num, int, 0644);
> +MODULE_PARM_DESC(percpu_test_num, "Test number percpu_test_num");
> +
> +static int percpu_test_verify(void)
>  {
>  	/*
>  	 * volatile prevents compiler from optimizing it uses, otherwise the
> @@ -26,109 +44,233 @@ static int __init percpu_test_init(void)
>  	volatile unsigned int ui_one = 1;
>  	long l = 0;
>  	unsigned long ul = 0;
> +	long __percpu *long_counter = alloc_percpu(long);
> +	unsigned long __percpu *ulong_counter = alloc_percpu(unsigned long);
> 
> -	pr_info("percpu test start\n");
> +	if (!long_counter || !ulong_counter)
> +		goto out;
> +
> +	pr_debug("percpu_test: %s start cpu: %d\n", __func__, smp_processor_id());
> 
>  	preempt_disable();
> 
>  	l += -1;
> -	__this_cpu_add(long_counter, -1);
> -	CHECK(l, long_counter, -1);
> +	__this_cpu_add(*long_counter, -1);
> +	CHECK(l, *long_counter, -1);
> 
>  	l += 1;
> -	__this_cpu_add(long_counter, 1);
> -	CHECK(l, long_counter, 0);
> +	__this_cpu_add(*long_counter, 1);
> +	CHECK(l, *long_counter, 0);
> 
>  	ul = 0;
> -	__this_cpu_write(ulong_counter, 0);
> +	__this_cpu_write(*ulong_counter, 0);
> 
>  	ul += 1UL;
> -	__this_cpu_add(ulong_counter, 1UL);
> -	CHECK(ul, ulong_counter, 1);
> +	__this_cpu_add(*ulong_counter, 1UL);
> +	CHECK(ul, *ulong_counter, 1);
> 
>  	ul += -1UL;
> -	__this_cpu_add(ulong_counter, -1UL);
> -	CHECK(ul, ulong_counter, 0);
> +	__this_cpu_add(*ulong_counter, -1UL);
> +	CHECK(ul, *ulong_counter, 0);
> 
>  	ul += -(unsigned long)1;
> -	__this_cpu_add(ulong_counter, -(unsigned long)1);
> -	CHECK(ul, ulong_counter, -1);
> +	__this_cpu_add(*ulong_counter, -(unsigned long)1);
> +	CHECK(ul, *ulong_counter, -1);
> 
>  	ul = 0;
> -	__this_cpu_write(ulong_counter, 0);
> +	__this_cpu_write(*ulong_counter, 0);
> 
>  	ul -= 1;
> -	__this_cpu_dec(ulong_counter);
> -	CHECK(ul, ulong_counter, -1);
> -	CHECK(ul, ulong_counter, ULONG_MAX);
> +	__this_cpu_dec(*ulong_counter);
> +	CHECK(ul, *ulong_counter, -1);
> +	CHECK(ul, *ulong_counter, ULONG_MAX);
> 
>  	l += -ui_one;
> -	__this_cpu_add(long_counter, -ui_one);
> -	CHECK(l, long_counter, 0xffffffff);
> +	__this_cpu_add(*long_counter, -ui_one);
> +	CHECK(l, *long_counter, 0xffffffff);
> 
>  	l += ui_one;
> -	__this_cpu_add(long_counter, ui_one);
> -	CHECK(l, long_counter, (long)0x100000000LL);
> +	__this_cpu_add(*long_counter, ui_one);
> +	CHECK(l, *long_counter, (long)0x100000000LL);
> 
> 
>  	l = 0;
> -	__this_cpu_write(long_counter, 0);
> +	__this_cpu_write(*long_counter, 0);
> 
>  	l -= ui_one;
> -	__this_cpu_sub(long_counter, ui_one);
> -	CHECK(l, long_counter, -1);
> +	__this_cpu_sub(*long_counter, ui_one);
> +	CHECK(l, *long_counter, -1);
> 
>  	l = 0;
> -	__this_cpu_write(long_counter, 0);
> +	__this_cpu_write(*long_counter, 0);
> 
>  	l += ui_one;
> -	__this_cpu_add(long_counter, ui_one);
> -	CHECK(l, long_counter, 1);
> +	__this_cpu_add(*long_counter, ui_one);
> +	CHECK(l, *long_counter, 1);
> 
>  	l += -ui_one;
> -	__this_cpu_add(long_counter, -ui_one);
> -	CHECK(l, long_counter, (long)0x100000000LL);
> +	__this_cpu_add(*long_counter, -ui_one);
> +	CHECK(l, *long_counter, (long)0x100000000LL);
> 
>  	l = 0;
> -	__this_cpu_write(long_counter, 0);
> +	__this_cpu_write(*long_counter, 0);
> 
>  	l -= ui_one;
> -	this_cpu_sub(long_counter, ui_one);
> -	CHECK(l, long_counter, -1);
> -	CHECK(l, long_counter, ULONG_MAX);
> +	this_cpu_sub(*long_counter, ui_one);
> +	CHECK(l, *long_counter, -1);
> +	CHECK(l, *long_counter, ULONG_MAX);
> 
>  	ul = 0;
> -	__this_cpu_write(ulong_counter, 0);
> +	__this_cpu_write(*ulong_counter, 0);
> 
>  	ul += ui_one;
> -	__this_cpu_add(ulong_counter, ui_one);
> -	CHECK(ul, ulong_counter, 1);
> +	__this_cpu_add(*ulong_counter, ui_one);
> +	CHECK(ul, *ulong_counter, 1);
> 
>  	ul = 0;
> -	__this_cpu_write(ulong_counter, 0);
> +	__this_cpu_write(*ulong_counter, 0);
> 
>  	ul -= ui_one;
> -	__this_cpu_sub(ulong_counter, ui_one);
> -	CHECK(ul, ulong_counter, -1);
> -	CHECK(ul, ulong_counter, ULONG_MAX);
> +	__this_cpu_sub(*ulong_counter, ui_one);
> +	CHECK(ul, *ulong_counter, -1);
> +	CHECK(ul, *ulong_counter, ULONG_MAX);
> 
>  	ul = 3;
> -	__this_cpu_write(ulong_counter, 3);
> +	__this_cpu_write(*ulong_counter, 3);
> 
> -	ul = this_cpu_sub_return(ulong_counter, ui_one);
> -	CHECK(ul, ulong_counter, 2);
> +	ul = this_cpu_sub_return(*ulong_counter, ui_one);
> +	CHECK(ul, *ulong_counter, 2);
> 
> -	ul = __this_cpu_sub_return(ulong_counter, ui_one);
> -	CHECK(ul, ulong_counter, 1);
> +	ul = __this_cpu_sub_return(*ulong_counter, ui_one);
> +	CHECK(ul, *ulong_counter, 1);
> 
>  	preempt_enable();
> 
> -	pr_info("percpu test done\n");
> -	return -EAGAIN;  /* Fail will directly unload the module */
> +out:
> +	free_percpu(long_counter);
> +	free_percpu(ulong_counter);
> +	pr_debug("percpu_test: %s done cpu: %d\n", __func__, smp_processor_id());
> +
> +	/*
> +	 * Keep the default functionality same.
> +	 * Fail will directly unload this module.
> +	 */
> +	return -EAGAIN;
> +}
> +
> +static void percpu_test_verify_work(struct work_struct *work)
> +{
> +	percpu_test_verify();
> +}
> +
> +static int percpu_test_stress(void)
> +{
> +	int i;
> +
> +	for (i = 1; i < (1 << percpu_alloc_max_size_shift); i++) {
> +		size_t size = i;
> +
> +		if (size > PCPU_MIN_ALLOC_SIZE)
> +			break;
> +		counters[i] = (char __percpu *)__alloc_percpu(size, __alignof__(char));
> +		if (!counters[i])
> +			break;
> +		cond_resched();
> +	}
> +
> +	schedule_on_each_cpu(percpu_test_verify_work);

I'm not understanding the value of scheduling on each cpu here? Each
CHECK() call is accessing the value via __this_cpu_read() meaning it has
nothing to do with any other cpu's copy of the variable?

> +
> +	for (i = 0; i < (1 << percpu_alloc_max_size_shift); i++) {
> +		free_percpu(counters[i]);
> +		cond_resched();
> +	}
> +	return -EAGAIN;
> +}
> +
> +static int percpu_stressd(void *v)
> +{
> +	int iter = 0;
> +
> +	pr_info("DAEMON: starts %s\n", __func__);
> +	do {
> +		if (kthread_should_stop())
> +			break;
> +		iter++;
> +		pr_info("TEST Starts: %s: iter (%d)\n", __func__, iter);
> +		percpu_test_stress();
> +		pr_info("TEST Completed: %s: iter (%d)\n", __func__, iter);
> +
> +		set_current_state(TASK_INTERRUPTIBLE);
> +		schedule_timeout(percpu_stressd_interval);
> +	} while (1);
> +
> +	return 0;
> +}
> +
> +static int percpu_test_stressd(void)
> +{
> +	percpu_stressd_thread = kthread_run(percpu_stressd, NULL, "percpu_stressd");
> +	if (IS_ERR(percpu_stressd_thread))
> +		percpu_stressd_thread = NULL;
> +	return 0;
> +}
> +
> +enum test_type {
> +	PERCPU_VERIFY,
> +	PERCPU_STRESS,
> +	PERCPU_STRESSD,
> +	NR_TESTS,
> +};
> +
> +const char *test_names[NR_TESTS] = {
> +	[PERCPU_VERIFY] = "percpu_verify",
> +	[PERCPU_STRESS] = "percpu_stress",
> +	[PERCPU_STRESSD] = "percpu_stressd",
> +};
> +
> +static int __init percpu_test_init(void)
> +{
> +	int i, ret = 0;
> +	typedef int (*percpu_tests)(void);
> +	const percpu_tests test_funcs[NR_TESTS] = {
> +		[PERCPU_VERIFY] = percpu_test_verify,
> +		[PERCPU_STRESS] = percpu_test_stress,
> +		[PERCPU_STRESSD] = percpu_test_stressd,
> +	};
> +
> +	/* sanity checks */
> +	if (percpu_alloc_max_size_shift > PAGE_SHIFT)
> +		percpu_alloc_max_size_shift = PAGE_SHIFT;
> +	if (percpu_test_num > NR_TESTS)
> +		percpu_test_num = NR_TESTS;
> +
> +	pr_info("percpu_test: INIT, interval: %ld, max_shift: %d, run_tests: %s\n",
> +			percpu_stressd_interval, percpu_alloc_max_size_shift,
> +			percpu_test_num == NR_TESTS ? "run all tests" :
> +			test_names[percpu_test_num]);
> +
> +	/* run a given test */
> +	if (percpu_test_num < NR_TESTS) {
> +		pr_info("TEST Starts: %s\n", test_names[percpu_test_num]);
> +		ret = test_funcs[percpu_test_num]();
> +		pr_info("TEST Completed: %s\n", test_names[percpu_test_num]);
> +		goto out;
> +	}
> +
> +	for (i = 0; i < NR_TESTS; i++) {
> +		pr_info("TEST Starts: %s\n", test_names[i]);
> +		test_funcs[i]();
> +		pr_info("TEST Completed: %s\n", test_names[i]);
> +	}
> +out:
> +	return ret;
>  }
> 
>  static void __exit percpu_test_exit(void)
>  {
> +	if (percpu_stressd_thread)
> +		kthread_stop(percpu_stressd_thread);
> +	pr_info("percpu_test: EXIT\n");
>  }
> 
>  module_init(percpu_test_init)
> --
> 2.31.1
> 

Thanks,
Dennis




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux