Re: [PATCH v3 05/18] selftests/mm: keep khugepaged out of the swapout the swap cases set up

Muhammad Usama Anjum <[email protected]>
Newsgroups gmane.linux.kernel,gmane.linux.kernel.mm
Message-ID <[email protected]>
On 12/08/2026 2:22 pm, Kiryl Shutsemau wrote:
> From: "Kiryl Shutsemau (Meta)" <[email protected]>
> 
> collapse_swapin_single_pte() and collapse_max_ptes_swap() page a range out
> and then require smaps to report exactly the count they asked for.  Two
> things keep it from arriving.  MADV_PAGEOUT is best effort, so the count
> often turns up a moment late.  And wait_for_scan() leaves MADV_HUGEPAGE
> behind, so khugepaged is still working on the range: collapsing one with up
> to max_ptes_swap pages swapped out means reading them back in, and the
> daemon empties the swap as fast as the case fills it.  On arm64 with 64K
> pages, where max_ptes_swap is 1024 pages, that is 64M a step and the case
> loses:
> 
>   # Swapout 1024 of 8192 pages... Fail
>   not ok 10 collapse_max_ptes_swap
> 
> Ask again for up to two seconds, with the range held out of the daemon's
> reach while asking.  The collapse each case runs next puts MADV_HUGEPAGE
> back, so only the setup is affected.
> 
> If the pages still will not go, skip.  is_swap_enabled() covers a machine
> with no swap; what is left -- swap too small, full, capped by a memcg, busy
> with writeback -- is not the kernel under test refusing.  An error from
> madvise() itself still ends the run.
> 
> Assisted-by: Claude-Code:claude-opus-5
> Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]>
> ---
>  tools/testing/selftests/mm/khugepaged.c | 53 +++++++++++++++++++------
>  1 file changed, 41 insertions(+), 12 deletions(-)
> 
> diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c
> index c049ac997def..8458cd2ff0df 100644
> --- a/tools/testing/selftests/mm/khugepaged.c
> +++ b/tools/testing/selftests/mm/khugepaged.c
> @@ -241,6 +241,41 @@ static bool check_swap(void *addr, unsigned long size)
>  	return swap;
>  }
>  
> +/*
> + * Page the range out and wait for the swap count to say so.
> + *
> + * Two things get in the way.  MADV_PAGEOUT is best effort:
> + * shrink_folio_list() leaves a folio alone when it cannot reclaim it right
> + * away, and one still under writeback from an earlier pageout is the common
> + * case, so the count the caller asks for arrives a moment later.  And a range
> + * an earlier collapse left MADV_HUGEPAGE is one khugepaged is still working
> + * on: collapsing a range with up to max_ptes_swap pages swapped out means
> + * reading those pages back in, so the daemon undoes the pageout as fast as it
> + * is asked for.  Keep the range out of its reach; the collapse the caller runs
> + * next puts MADV_HUGEPAGE back.
> + *
> + * Failing to get the pages out is the machine's answer, not the kernel's --
> + * swap too small, swap full, a memcg cap, a folio still under writeback -- so
> + * callers skip rather than fail.  An error from madvise() is different, and
> + * ends the run here.
> + */
> +static bool swapout_range(void *p, unsigned long size)
> +{
> +	int i;
> +
> +	if (madvise(p, size, MADV_NOHUGEPAGE))
> +		ksft_exit_fail_perror("madvise(MADV_NOHUGEPAGE)");
> +
> +	for (i = 0; i < 40; i++) {
> +		if (madvise(p, size, MADV_PAGEOUT))
> +			ksft_exit_fail_perror("madvise(MADV_PAGEOUT)");
> +		if (check_swap(p, size))
> +			return true;
> +		usleep(50 * 1000);
> +	}
> +	return false;
> +}
> +
>  static void *alloc_mapping(int nr)
>  {
>  	void *p;
> @@ -855,12 +890,10 @@ static void collapse_swapin_single_pte(struct collapse_context *c, struct mem_op
>  	p = ops->setup_area(1);
>  	ops->fault(p, 0, hpage_pmd_size);
>  
> -	if (madvise(p, page_size, MADV_PAGEOUT))
> -		ksft_exit_fail_perror("madvise(MADV_PAGEOUT)");
> -	if (check_swap(p, page_size)) {
> +	if (swapout_range(p, page_size)) {
>  		success("OK");
>  	} else {
> -		fail("Fail");
> +		skip("Could not swap out");
>  		goto out;
>  	}
>  
> @@ -887,12 +920,10 @@ static void collapse_max_ptes_swap(struct collapse_context *c, struct mem_ops *o
>  	p = ops->setup_area(1);
>  	ops->fault(p, 0, hpage_pmd_size);
>  
> -	if (madvise(p, (max_ptes_swap + 1) * page_size, MADV_PAGEOUT))
> -		ksft_exit_fail_perror("madvise(MADV_PAGEOUT)");
> -	if (check_swap(p, (max_ptes_swap + 1) * page_size)) {
> +	if (swapout_range(p, (max_ptes_swap + 1) * page_size)) {
>  		success("OK");
>  	} else {
> -		fail("Fail");
> +		skip("Could not swap out");
>  		goto out;
>  	}
>  
> @@ -904,12 +935,10 @@ static void collapse_max_ptes_swap(struct collapse_context *c, struct mem_ops *o
>  		ops->fault(p, 0, hpage_pmd_size);
>  		ksft_print_msg("Swapout %d of %d pages...", max_ptes_swap,
>  		       hpage_pmd_nr);
> -		if (madvise(p, max_ptes_swap * page_size, MADV_PAGEOUT))
> -			ksft_exit_fail_perror("madvise(MADV_PAGEOUT)");
> -		if (check_swap(p, max_ptes_swap * page_size)) {
> +		if (swapout_range(p, max_ptes_swap * page_size)) {
>  			success("OK");
>  		} else {
> -			fail("Fail");
> +			skip("Could not swap out");
>  			goto out;
>  		}
>  

Reviewed-by: Muhammad Usama Anjum <[email protected]>
Tested-by: Muhammad Usama Anjum <[email protected]>

-- 
Thanks,
Usama
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.