diff mbox series

mm/compaction: fix the total_isolated in strict mode

Message ID 20241102201621.95291-1-liuq131@chinatelecom.cn (mailing list archive)
State New
Headers show
Series mm/compaction: fix the total_isolated in strict mode | expand

Commit Message

Qiang Liu Nov. 2, 2024, 8:16 p.m. UTC
If the last cycle reads bogus compound_order() and blockpfn > end_pfn occurs,
it is possible that total_isolated will be less than nr_scanned. In this case,
strict mode should return 0, but the “if (strict && blockpfn < end_pfn)”
statement cannot recognize this situation

Signed-off-by: Qiang Liu <liuq131@chinatelecom.cn>
---
 mm/compaction.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

Andrew Morton Nov. 12, 2024, 12:22 a.m. UTC | #1
On Sat,  2 Nov 2024 20:16:21 +0000 Qiang Liu <liuq131@chinatelecom.cn> wrote:

> If the last cycle reads bogus compound_order() and blockpfn > end_pfn occurs,
> it is possible that total_isolated will be less than nr_scanned. In this case,
> strict mode should return 0, but the “if (strict && blockpfn < end_pfn)”
> statement cannot recognize this situation
> 
> ...
>
> --- a/mm/compaction.c
> +++ b/mm/compaction.c
> @@ -699,7 +699,7 @@ static unsigned long isolate_freepages_block(struct compact_control *cc,
>  	 * pages requested were isolated. If there were any failures, 0 is
>  	 * returned and CMA will fail.
>  	 */
> -	if (strict && blockpfn < end_pfn)
> +	if (strict && (blockpfn < end_pfn || total_isolated != nr_scanned))
>  		total_isolated = 0;
>  
>  	cc->total_free_scanned += nr_scanned;

That's really old code.  What userspace-visible effects might this
have?  Is this from code inspection, or was some misbehaviour observed?

Thanks.
Baolin Wang Nov. 12, 2024, 1:24 a.m. UTC | #2
On 2024/11/3 04:16, Qiang Liu wrote:
> If the last cycle reads bogus compound_order() and blockpfn > end_pfn occurs,

if blockpfn > end_pfn occurs, we will reset the blockpfn, right?

	/*
	 * Be careful to not go outside of the pageblock.
	 */
	if (unlikely(blockpfn > end_pfn))
		blockpfn = end_pfn;

So how this can happen?

> it is possible that total_isolated will be less than nr_scanned. In this case,
> strict mode should return 0, but the “if (strict && blockpfn < end_pfn)”
> statement cannot recognize this situation
> 
> Signed-off-by: Qiang Liu <liuq131@chinatelecom.cn>
> ---
>   mm/compaction.c | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/mm/compaction.c b/mm/compaction.c
> index a2b16b08cbbf..6009f5d1021a 100644
> --- a/mm/compaction.c
> +++ b/mm/compaction.c
> @@ -699,7 +699,7 @@ static unsigned long isolate_freepages_block(struct compact_control *cc,
>   	 * pages requested were isolated. If there were any failures, 0 is
>   	 * returned and CMA will fail.
>   	 */
> -	if (strict && blockpfn < end_pfn)
> +	if (strict && (blockpfn < end_pfn || total_isolated != nr_scanned))
>   		total_isolated = 0;
>   
>   	cc->total_free_scanned += nr_scanned;
Baolin Wang Nov. 12, 2024, 9:47 a.m. UTC | #3
On 2024/11/12 10:16, liuq131@chinatelecom.cn wrote:
> "We assume that the block we are currently processing is distributed as follows:
> 0   1   2                                                            511
> --------------------------------------------------
> |    |    |                                                              |
> ---------------------------------------------------
> Index 0 and 1 are both pages with an order of 0.
> Index 2 has a bogus order (let's assume the order is 9).
> When the for loop reaches index 2, it will enter the following code:
> /*
>   * For compound pages such as THP and hugetlbfs, we can save
>   * potentially a lot of iterations if we skip them at once.
>   * The check is racy, but we can consider only valid values
>   * and the only danger is skipping too much.
>   */
> if (PageCompound(page)) {
>      const unsigned int order = compound_order(page);
>      if (blockpfn + (1UL << order) <= end_pfn) {
>          blockpfn += (1UL << order) - 1;
>          page += (1UL << order) - 1;
>          nr_scanned += (1UL << order) - 1;
>      }
>      goto isolate_fail;
> }
> 
> After exiting the for loop:
> blockpfn =basepfn+ 2+2^9 = basepfn+514
> endpfn  = basepfn +512
> total_isolated = 2
> nr_scanned = 514

In your case, the 'blockpfn' will not be updated to 'basepfn+514', 
because 'blockpfn + (1UL << order) > end_pfn', right? And remember the 
'end_pfn' is the end of the pageblock.

So I'm still confused about your case. Is this from code inspection?

> /*
> * Be careful to not go outside of the pageblock.
> */
> if (unlikely(blockpfn > end_pfn))
> blockpfn = end_pfn;
>   
> So this can happen
> 
> /*
>   * If strict isolation is requested by CMA then check that all the
>   * pages requested were isolated. If there were any failures, 0 is
>   * returned and CMA will fail.
>   */
> if (strict && blockpfn < end_pfn)
> total_isolated = 0;
> 
> If processed according to the old code, it will not enter the if statement to reset total_isolated, but the correct handling is to reset total_isolated to 0.

Please do not top-posting:

"
- Use interleaved ("inline") replies, which makes your response easier 
to read. (i.e. avoid top-posting -- the practice of putting your answer 
above the quoted text you are responding to.) For more details, see
   :ref:`Documentation/process/submitting-patches.rst 
<interleaved_replies>`.
"
diff mbox series

Patch

diff --git a/mm/compaction.c b/mm/compaction.c
index a2b16b08cbbf..6009f5d1021a 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -699,7 +699,7 @@  static unsigned long isolate_freepages_block(struct compact_control *cc,
 	 * pages requested were isolated. If there were any failures, 0 is
 	 * returned and CMA will fail.
 	 */
-	if (strict && blockpfn < end_pfn)
+	if (strict && (blockpfn < end_pfn || total_isolated != nr_scanned))
 		total_isolated = 0;
 
 	cc->total_free_scanned += nr_scanned;