diff mbox series

[v2] mm/page_alloc: Return nr_populated when the array is full

Message ID 162490397938.1485.7782934829743772831.stgit@klimt.1015granger.net (mailing list archive)
State New, archived
Headers show
Series [v2] mm/page_alloc: Return nr_populated when the array is full | expand

Commit Message

Chuck Lever III June 28, 2021, 6:12 p.m. UTC
The SUNRPC consumer of __alloc_bulk_pages() legitimately calls it
with a full array sometimes. In that case, the correct return code,
according to the API contract, is to return the number of pages
already in the array/list.

Let's clean up the return logic to make it clear that the returned
value is always the total number of pages in the array/list, not the
number of pages that were allocated during this call.

Fixes: b3b64ebd3822 ("mm/page_alloc: do bulk array bounds check after checking populated elements")
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 mm/page_alloc.c |    9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

Comments

Matthew Wilcox June 28, 2021, 6:17 p.m. UTC | #1
On Mon, Jun 28, 2021 at 02:12:59PM -0400, Chuck Lever wrote:
> The SUNRPC consumer of __alloc_bulk_pages() legitimately calls it
> with a full array sometimes. In that case, the correct return code,
> according to the API contract, is to return the number of pages
> already in the array/list.
> 
> Let's clean up the return logic to make it clear that the returned
> value is always the total number of pages in the array/list, not the
> number of pages that were allocated during this call.

This is more complicated than either v1 or the version that Mel sent
earlier today.  Is it worth it?
Chuck Lever III June 28, 2021, 8:06 p.m. UTC | #2
Hi-

> On Jun 28, 2021, at 2:17 PM, Matthew Wilcox <willy@infradead.org> wrote:
> 
> On Mon, Jun 28, 2021 at 02:12:59PM -0400, Chuck Lever wrote:
>> The SUNRPC consumer of __alloc_bulk_pages() legitimately calls it
>> with a full array sometimes. In that case, the correct return code,
>> according to the API contract, is to return the number of pages
>> already in the array/list.
>> 
>> Let's clean up the return logic to make it clear that the returned
>> value is always the total number of pages in the array/list, not the
>> number of pages that were allocated during this call.
> 
> This is more complicated than either v1 or the version that Mel sent
> earlier today.  Is it worth it?

Yes.

My v2 addresses the reason the bug was introduced in the first place: The
code currently does not reflect the API contract described in the documenting
comment.

A human reading the function as it is currently written might easily expect
that a zero return code is proper if something failed. However, the API
contract does not list zero as a unique return value with a special meaning.
The contract merely states: "Returns the number of pages on the list or
array." Therefore zero is a plausible return value only if @nr_pages is
zero or less.

Note that the value returned if prepare_alloc_pages() fails is also incorrect,
by my reading, and my v2 addresses that.

The only other call site is __page_pool_alloc_pages_slow(), and that looks
incorrect to me -- it does not agree with either the API contract or the
SUNRPC call site. I did not fix that, but I think it should be looked into
by someone familiar with that code.

I haven't seen the patch Mel sent earlier today. I was not cc'd on that one
or on b3b64ebd3822.

--
Chuck Lever
Mel Gorman June 29, 2021, 8:14 a.m. UTC | #3
On Mon, Jun 28, 2021 at 02:12:59PM -0400, Chuck Lever wrote:
> The SUNRPC consumer of __alloc_bulk_pages() legitimately calls it
> with a full array sometimes. In that case, the correct return code,
> according to the API contract, is to return the number of pages
> already in the array/list.
> 
> Let's clean up the return logic to make it clear that the returned
> value is always the total number of pages in the array/list, not the
> number of pages that were allocated during this call.
> 
> Fixes: b3b64ebd3822 ("mm/page_alloc: do bulk array bounds check after checking populated elements")
> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>

Commit 66d9282523b3 ("mm/page_alloc: Correct return value of populated
elements if bulk array is populated") has since been merged as it was
the minimal obvious for the problem introduced but I have no objection
to your patch being rebased on top and sent as a cleanup.

Thanks.
diff mbox series

Patch

diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index ef2265f86b91..270719898b47 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -5047,7 +5047,7 @@  unsigned long __alloc_pages_bulk(gfp_t gfp, int preferred_nid,
 	int nr_populated = 0;
 
 	if (unlikely(nr_pages <= 0))
-		return 0;
+		goto out;
 
 	/*
 	 * Skip populated array elements to determine if any pages need
@@ -5058,7 +5058,7 @@  unsigned long __alloc_pages_bulk(gfp_t gfp, int preferred_nid,
 
 	/* Already populated array? */
 	if (unlikely(page_array && nr_pages - nr_populated == 0))
-		return 0;
+		goto out;
 
 	/* Use the single page allocator for one page. */
 	if (nr_pages - nr_populated == 1)
@@ -5068,7 +5068,7 @@  unsigned long __alloc_pages_bulk(gfp_t gfp, int preferred_nid,
 	gfp &= gfp_allowed_mask;
 	alloc_gfp = gfp;
 	if (!prepare_alloc_pages(gfp, 0, preferred_nid, nodemask, &ac, &alloc_gfp, &alloc_flags))
-		return 0;
+		goto out;
 	gfp = alloc_gfp;
 
 	/* Find an allowed local zone that meets the low watermark. */
@@ -5141,6 +5141,7 @@  unsigned long __alloc_pages_bulk(gfp_t gfp, int preferred_nid,
 
 	local_irq_restore(flags);
 
+out:
 	return nr_populated;
 
 failed_irq:
@@ -5156,7 +5157,7 @@  unsigned long __alloc_pages_bulk(gfp_t gfp, int preferred_nid,
 		nr_populated++;
 	}
 
-	return nr_populated;
+	goto out;
 }
 EXPORT_SYMBOL_GPL(__alloc_pages_bulk);