[RFC PATCH 3/3] mm/memory-failure: handle min_order_for_split() error code properly

Zi Yan posted 3 patches 1 week, 4 days ago
[RFC PATCH 3/3] mm/memory-failure: handle min_order_for_split() error code properly
Posted by Zi Yan 1 week, 4 days ago
min_order_for_split() returns -EBUSY when the folio is truncated and cannot
be split. In commit 77008e1b2ef7 ("mm/huge_memory: do not change
split_huge_page*() target order silently"), memory_failure() does not
handle it and pass -EBUSY to try_to_split_thp_page() directly.
try_to_split_thp_page() returns -EINVAL since -EBUSY becomes 0xfffffff0 as
new_order is unsigned int in __folio_split() and this large new_order is
rejected as an invalid input. The code does not cause a bug.
soft_offline_in_use_page() also uses min_order_for_split() but it always
passes 0 as new_order for split.

Handle it properly by checking min_order_for_split() return value and not
calling try_to_split_thp_page() if the value is negative. Add a comment
in soft_offline_in_use_page() to clarify the possible negative new_order
value.

Signed-off-by: Zi Yan <ziy@nvidia.com>
---
 mm/memory-failure.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 7f908ad795ad..86582f030159 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -2437,8 +2437,11 @@ int memory_failure(unsigned long pfn, int flags)
 		 * or unhandlable page.  The refcount is bumped iff the
 		 * page is a valid handlable page.
 		 */
-		folio_set_has_hwpoisoned(folio);
-		err = try_to_split_thp_page(p, new_order, /* release= */ false);
+		if (new_order >= 0) {
+			folio_set_has_hwpoisoned(folio);
+			err = try_to_split_thp_page(p, new_order, /* release= */ false);
+		} else
+			err = new_order;
 		/*
 		 * If splitting a folio to order-0 fails, kill the process.
 		 * Split the folio regardless to minimize unusable pages.
@@ -2779,6 +2782,7 @@ static int soft_offline_in_use_page(struct page *page)
 		/*
 		 * If new_order (target split order) is not 0, do not split the
 		 * folio at all to retain the still accessible large folio.
+		 * new_order can be -EBUSY, meaning the folio cannot be split.
 		 * NOTE: if minimizing the number of soft offline pages is
 		 * preferred, split it to non-zero new_order like it is done in
 		 * memory_failure().
-- 
2.51.0
Re: [RFC PATCH 3/3] mm/memory-failure: handle min_order_for_split() error code properly
Posted by David Hildenbrand (Red Hat) 1 week, 4 days ago
On 11/20/25 04:59, Zi Yan wrote:
> min_order_for_split() returns -EBUSY when the folio is truncated and cannot
> be split. In commit 77008e1b2ef7 ("mm/huge_memory: do not change
> split_huge_page*() target order silently"), memory_failure() does not
> handle it and pass -EBUSY to try_to_split_thp_page() directly.
> try_to_split_thp_page() returns -EINVAL since -EBUSY becomes 0xfffffff0 as

I'm wondering whether we should change min_order_for_split() to something like:


diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 7c69572b6c3f5..34eb6fec9a059 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -4210,16 +4210,19 @@ int folio_split(struct folio *folio, unsigned int new_order,
                              SPLIT_TYPE_NON_UNIFORM);
  }
  
-int min_order_for_split(struct folio *folio)
+unsigned int min_order_for_split(struct folio *folio)
  {
         if (folio_test_anon(folio))
                 return 0;
  
+       /*
+        * If the folio got truncated, we don't know the previous mapping and
+        * consequently the old min order. But it doesn't matter, as any split
+        * attempt will immediately fail with -EBUSY as the folio cannot get
+        * split until freed.
+        */
         if (!folio->mapping) {
-               if (folio_test_pmd_mappable(folio))
-                       count_vm_event(THP_SPLIT_PAGE_FAILED);
-               return -EBUSY;
-       }
+               return 0;
  
         return mapping_min_folio_order(folio->mapping);
  }

-- 
Cheers

David
Re: [RFC PATCH 3/3] mm/memory-failure: handle min_order_for_split() error code properly
Posted by Zi Yan 1 week, 4 days ago
On 20 Nov 2025, at 4:37, David Hildenbrand (Red Hat) wrote:

> On 11/20/25 04:59, Zi Yan wrote:
>> min_order_for_split() returns -EBUSY when the folio is truncated and cannot
>> be split. In commit 77008e1b2ef7 ("mm/huge_memory: do not change
>> split_huge_page*() target order silently"), memory_failure() does not
>> handle it and pass -EBUSY to try_to_split_thp_page() directly.
>> try_to_split_thp_page() returns -EINVAL since -EBUSY becomes 0xfffffff0 as
>
> I'm wondering whether we should change min_order_for_split() to something like:
>
>
> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> index 7c69572b6c3f5..34eb6fec9a059 100644
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -4210,16 +4210,19 @@ int folio_split(struct folio *folio, unsigned int new_order,
>                              SPLIT_TYPE_NON_UNIFORM);
>  }
>  -int min_order_for_split(struct folio *folio)
> +unsigned int min_order_for_split(struct folio *folio)
>  {
>         if (folio_test_anon(folio))
>                 return 0;
>  +       /*
> +        * If the folio got truncated, we don't know the previous mapping and
> +        * consequently the old min order. But it doesn't matter, as any split
> +        * attempt will immediately fail with -EBUSY as the folio cannot get
> +        * split until freed.
> +        */
>         if (!folio->mapping) {
> -               if (folio_test_pmd_mappable(folio))
> -                       count_vm_event(THP_SPLIT_PAGE_FAILED);
> -               return -EBUSY;
> -       }
> +               return 0;
>          return mapping_min_folio_order(folio->mapping);
>  }

I thought about it. My concern was that what if the returned order is not
immediately used for split, maybe for some calculation. I might think too much.
Your approach is much simpler.

I am also going to add a kernel-doc and change the return type to unsigned int:

diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
index 0d55354e3a34..e0731e01df27 100644
--- a/include/linux/huge_mm.h
+++ b/include/linux/huge_mm.h
@@ -373,7 +373,7 @@ bool can_split_folio(struct folio *folio, int caller_pins, int *pextra_pins);
 int __split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
                unsigned int new_order);
 int folio_split_unmapped(struct folio *folio, unsigned int new_order);
-int min_order_for_split(struct folio *folio);
+unsigned int min_order_for_split(struct folio *folio);
 int split_folio_to_list(struct folio *folio, struct list_head *list);
 bool folio_split_supported(struct folio *folio, unsigned int new_order,
                enum split_type split_type, bool warns);
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 23239c19b36e..f45560b53210 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -4238,7 +4238,18 @@ int folio_split(struct folio *folio, unsigned int new_order,
                             SPLIT_TYPE_NON_UNIFORM);
 }

-int min_order_for_split(struct folio *folio)
+/**
+ * min_order_for_split() - get the minimum order @folio can be split to
+ * @folio: folio to split
+ *
+ * min_order_for_split() tells the minimum order @folio can be split to.
+ * Anonymous folios can be split to order 0, file-backed folios might have
+ * limitations at file system level. If the folio is truncated, 0 will be
+ * returned and any split attempt will get -EBUSY.
+ *
+ * Return: @folio's minimum order
+ */
+unsigned int min_order_for_split(struct folio *folio)
 {
        if (folio_test_anon(folio))
                return 0;

Best Regards,
Yan, Zi
Re: [RFC PATCH 3/3] mm/memory-failure: handle min_order_for_split() error code properly
Posted by Balbir Singh 1 week, 4 days ago
On 11/20/25 14:59, Zi Yan wrote:
> min_order_for_split() returns -EBUSY when the folio is truncated and cannot
> be split. In commit 77008e1b2ef7 ("mm/huge_memory: do not change
> split_huge_page*() target order silently"), memory_failure() does not
> handle it and pass -EBUSY to try_to_split_thp_page() directly.
> try_to_split_thp_page() returns -EINVAL since -EBUSY becomes 0xfffffff0 as
> new_order is unsigned int in __folio_split() and this large new_order is
> rejected as an invalid input. The code does not cause a bug.
> soft_offline_in_use_page() also uses min_order_for_split() but it always
> passes 0 as new_order for split.
> 
> Handle it properly by checking min_order_for_split() return value and not
> calling try_to_split_thp_page() if the value is negative. Add a comment
> in soft_offline_in_use_page() to clarify the possible negative new_order
> value.
> 
> Signed-off-by: Zi Yan <ziy@nvidia.com>
> ---
>  mm/memory-failure.c | 8 ++++++--
>  1 file changed, 6 insertions(+), 2 deletions(-)
> 
> diff --git a/mm/memory-failure.c b/mm/memory-failure.c
> index 7f908ad795ad..86582f030159 100644
> --- a/mm/memory-failure.c
> +++ b/mm/memory-failure.c
> @@ -2437,8 +2437,11 @@ int memory_failure(unsigned long pfn, int flags)
>  		 * or unhandlable page.  The refcount is bumped iff the
>  		 * page is a valid handlable page.
>  		 */
> -		folio_set_has_hwpoisoned(folio);
> -		err = try_to_split_thp_page(p, new_order, /* release= */ false);
> +		if (new_order >= 0) {
> +			folio_set_has_hwpoisoned(folio);

if new_order < 0, do we skip setting hwpoisioned bit on the folio?

> +			err = try_to_split_thp_page(p, new_order, /* release= */ false);
> +		} else
> +			err = new_order;
>  		/*
>  		 * If splitting a folio to order-0 fails, kill the process.
>  		 * Split the folio regardless to minimize unusable pages.
> @@ -2779,6 +2782,7 @@ static int soft_offline_in_use_page(struct page *page)
>  		/*
>  		 * If new_order (target split order) is not 0, do not split the
>  		 * folio at all to retain the still accessible large folio.
> +		 * new_order can be -EBUSY, meaning the folio cannot be split.
>  		 * NOTE: if minimizing the number of soft offline pages is
>  		 * preferred, split it to non-zero new_order like it is done in
>  		 * memory_failure().

Balbir
Re: [RFC PATCH 3/3] mm/memory-failure: handle min_order_for_split() error code properly
Posted by Zi Yan 1 week, 4 days ago
On 19 Nov 2025, at 23:45, Balbir Singh wrote:

> On 11/20/25 14:59, Zi Yan wrote:
>> min_order_for_split() returns -EBUSY when the folio is truncated and cannot
>> be split. In commit 77008e1b2ef7 ("mm/huge_memory: do not change
>> split_huge_page*() target order silently"), memory_failure() does not
>> handle it and pass -EBUSY to try_to_split_thp_page() directly.
>> try_to_split_thp_page() returns -EINVAL since -EBUSY becomes 0xfffffff0 as
>> new_order is unsigned int in __folio_split() and this large new_order is
>> rejected as an invalid input. The code does not cause a bug.
>> soft_offline_in_use_page() also uses min_order_for_split() but it always
>> passes 0 as new_order for split.
>>
>> Handle it properly by checking min_order_for_split() return value and not
>> calling try_to_split_thp_page() if the value is negative. Add a comment
>> in soft_offline_in_use_page() to clarify the possible negative new_order
>> value.
>>
>> Signed-off-by: Zi Yan <ziy@nvidia.com>
>> ---
>>  mm/memory-failure.c | 8 ++++++--
>>  1 file changed, 6 insertions(+), 2 deletions(-)
>>
>> diff --git a/mm/memory-failure.c b/mm/memory-failure.c
>> index 7f908ad795ad..86582f030159 100644
>> --- a/mm/memory-failure.c
>> +++ b/mm/memory-failure.c
>> @@ -2437,8 +2437,11 @@ int memory_failure(unsigned long pfn, int flags)
>>  		 * or unhandlable page.  The refcount is bumped iff the
>>  		 * page is a valid handlable page.
>>  		 */
>> -		folio_set_has_hwpoisoned(folio);
>> -		err = try_to_split_thp_page(p, new_order, /* release= */ false);
>> +		if (new_order >= 0) {
>> +			folio_set_has_hwpoisoned(folio);
>
> if new_order < 0, do we skip setting hwpoisioned bit on the folio?

The bit should be set. Anyway, I am going to take David’s approach to
change min_order_for_split().

Thanks.

>
>> +			err = try_to_split_thp_page(p, new_order, /* release= */ false);
>> +		} else
>> +			err = new_order;
>>  		/*
>>  		 * If splitting a folio to order-0 fails, kill the process.
>>  		 * Split the folio regardless to minimize unusable pages.
>> @@ -2779,6 +2782,7 @@ static int soft_offline_in_use_page(struct page *page)
>>  		/*
>>  		 * If new_order (target split order) is not 0, do not split the
>>  		 * folio at all to retain the still accessible large folio.
>> +		 * new_order can be -EBUSY, meaning the folio cannot be split.
>>  		 * NOTE: if minimizing the number of soft offline pages is
>>  		 * preferred, split it to non-zero new_order like it is done in
>>  		 * memory_failure().
>
> Balbir


Best Regards,
Yan, Zi