Re: [PATCH 05/16] selftests/mm: add khugepaged completion barrier helper

Mike Rapoport <[email protected]> Mon, 03 Aug 2026 14:04:23 +0300
Newsgroups org.kernel.vger.linux-kselftest,org.kernel.vger.linux-kernel,org.kvack.linux-mm
Message-ID <178575506331.1561566.4863322190872953233.b4-review@b4>
> Race and functional tests need to drive khugepaged synchronously: set
> up a layout, let exactly one full scan pass over it, check the result.
> The khugepaged selftest already waits on full_scans advancing by two —
> a completion barrier for one pass that started after setup — but it
> relies on a short configured scan_sleep_millisecs to make progress.
> 
> Lift the pattern into a library helper, khugepaged_full_pass(), and
> drive it by the sysfs wake path: any store to scan_sleep_millisecs
> wakes the daemon, so the barrier completes promptly regardless of the
> configured scan cadence. Wake exactly once per missing pass:
> over-waking would queue a straggler pass behind the barrier that
> overlaps and perturbs whatever the caller sets up next. One wake
> completes one full pass only when the whole mm list fits in a single
> scan batch, so callers must pair the helper with a large pages_to_scan.
> 
> Settings pushes and pops must not start passes nobody asked for
> either, so thp_write_settings() now writes each khugepaged knob only
> when it changes. Switch the khugepaged selftest order-parameterized
> cases to the helper.
> 
> Assisted-by: Claude-Code:claude-opus-5
> Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]>
>
> diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/testing/selftests/mm/hugepage_settings.c
> index d7917dce3aba..a26a0cffa9c5 100644
> --- a/tools/testing/selftests/mm/hugepage_settings.c
> +++ b/tools/testing/selftests/mm/hugepage_settings.c
> @@ -183,6 +183,17 @@ void thp_read_settings(struct thp_settings *settings)
>  	}
>  }
>  
> +/*
> + * Write only on change: any store to a khugepaged sysfs knob wakes the
> + * daemon, and settings pushes/pops must not start scan passes nobody
> + * asked for — khugepaged_full_pass() is the only sanctioned wake.
> + */
> +static void thp_update_num(const char *name, unsigned long num)
> +{
> +	if (thp_read_num(name) != num)
> +		thp_write_num(name, num);
> +}

Don't you want to expose this to the users?

> +
>  void thp_write_settings(struct thp_settings *settings)
>  {
>  	struct khugepaged_settings *khugepaged = &settings->khugepaged;
> @@ -198,15 +209,15 @@ void thp_write_settings(struct thp_settings *settings)
>  			shmem_enabled_strings[settings->shmem_enabled]);
>  	thp_write_num("use_zero_page", settings->use_zero_page);
>  
> -	thp_write_num("khugepaged/defrag", khugepaged->defrag);
> -	thp_write_num("khugepaged/alloc_sleep_millisecs",
> -			khugepaged->alloc_sleep_millisecs);
> -	thp_write_num("khugepaged/scan_sleep_millisecs",
> -			khugepaged->scan_sleep_millisecs);
> -	thp_write_num("khugepaged/max_ptes_none", khugepaged->max_ptes_none);
> -	thp_write_num("khugepaged/max_ptes_swap", khugepaged->max_ptes_swap);
> -	thp_write_num("khugepaged/max_ptes_shared", khugepaged->max_ptes_shared);
> -	thp_write_num("khugepaged/pages_to_scan", khugepaged->pages_to_scan);
> +	thp_update_num("khugepaged/defrag", khugepaged->defrag);
> +	thp_update_num("khugepaged/alloc_sleep_millisecs",
> +		       khugepaged->alloc_sleep_millisecs);
> +	thp_update_num("khugepaged/scan_sleep_millisecs",
> +		       khugepaged->scan_sleep_millisecs);
> +	thp_update_num("khugepaged/max_ptes_none", khugepaged->max_ptes_none);
> +	thp_update_num("khugepaged/max_ptes_swap", khugepaged->max_ptes_swap);
> +	thp_update_num("khugepaged/max_ptes_shared", khugepaged->max_ptes_shared);
> +	thp_update_num("khugepaged/pages_to_scan", khugepaged->pages_to_scan);
>  
>  	if (dev_queue_read_ahead_path[0])
>  		write_num(dev_queue_read_ahead_path, settings->read_ahead_kb);
> @@ -230,6 +241,43 @@ void thp_write_settings(struct thp_settings *settings)
>  	}
>  }
>  
> +/*
> + * Completion barrier for khugepaged: wait until a full scan pass that
> + * started after this call has finished. full_scans must advance by two;
> + * a +1 step may complete a pass that examined this mm before the
> + * caller's setup was in place.
> + *
> + * Any store to scan_sleep_millisecs wakes the daemon, so the barrier
> + * works regardless of the configured scan cadence. It wakes exactly
> + * once per missing pass — over-waking would queue a straggler pass
> + * behind the barrier, perturbing whatever the caller sets up next.
> + * One wake completes one full pass only if the whole mm list fits in
> + * one scan batch, so callers must pair this with a large
> + * pages_to_scan.
> + */
> +bool khugepaged_full_pass(unsigned int timeout_s)
> +{
> +	unsigned long deadline_ms = timeout_s * 1000UL;
> +	unsigned long sleep_ms =
> +		thp_read_num("khugepaged/scan_sleep_millisecs");
> +	unsigned long elapsed_ms = 0;
> +	int pass;
> +
> +	for (pass = 0; pass < 2; pass++) {
> +		unsigned long target =
> +			thp_read_num("khugepaged/full_scans") + 1;
> +
> +		thp_write_num("khugepaged/scan_sleep_millisecs", sleep_ms);
> +		while (thp_read_num("khugepaged/full_scans") < target) {
> +			if (elapsed_ms >= deadline_ms)
> +				return false;
> +			usleep(10 * 1000);
> +			elapsed_ms += 10;
> +		}
> +	}
> +	return true;
> +}
> +
>  struct thp_settings *thp_current_settings(void)
>  {
>  	if (!settings_index) {
> diff --git a/tools/testing/selftests/mm/hugepage_settings.h b/tools/testing/selftests/mm/hugepage_settings.h
> index 726c73c43c05..8de446affeec 100644
> --- a/tools/testing/selftests/mm/hugepage_settings.h
> +++ b/tools/testing/selftests/mm/hugepage_settings.h
> @@ -83,6 +83,8 @@ static inline void thp_save_settings(void)
>  	hugepage_save_settings(/* thp = */ true, /* hugetlb = */ false);
>  }
>  
> +bool khugepaged_full_pass(unsigned int timeout_s);
> +
>  void thp_set_read_ahead_path(char *path);
>  unsigned long thp_supported_orders(void);
>  unsigned long thp_shmem_supported_orders(void);
> diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c
> index 971e97a7330a..65fafab06410 100644
> --- a/tools/testing/selftests/mm/khugepaged.c
> +++ b/tools/testing/selftests/mm/khugepaged.c
> @@ -1126,23 +1126,10 @@ static bool range_not_collapsed(void *p, size_t len)
>  	return true;
>  }
>  
> -/*
> - * Completion barrier: one full khugepaged pass that started after this
> - * call. Waiting for full_scans to advance by two guarantees it; a +1
> - * step might complete a pass that scanned our mm before the setup.
> - */
>  static bool khugepaged_wait_full_pass(void)
>  {
> -	int full_scans = thp_read_num("khugepaged/full_scans") + 2;
> -	int timeout = 60; /* 30 seconds */
> -
> -	while (timeout--) {
> -		if (thp_read_num("khugepaged/full_scans") >= full_scans)
> -			return true;
> -		printf(".");
> -		usleep(TICK);
> -	}
> -	return false;
> +	/* Wait up to 30 seconds for the pass to complete. */
> +	return khugepaged_full_pass(30);

This implies that _full_pass() helper should be in a patch before its
users :)

-- 
Sincerely yours,
Mike.