From: David Hildenbrand <david@redhat.com>
To: Aristeu Rozanski <aris@ruivo.org>, linux-mm@kvack.org
Cc: Andrew Morton <akpm@linux-foundation.org>,
Maarten Lankhorst <maarten.lankhorst@linux.intel.com>,
Maxime Ripard <mripard@kernel.org>,
Thomas Zimmermann <tzimmermann@suse.de>,
David Airlie <airlied@gmail.com>, Simona Vetter <simona@ffwll.ch>
Subject: Re: [PATCH] mm: make folio page count functions return unsigned
Date: Wed, 27 Aug 2025 11:13:03 +0200 [thread overview]
Message-ID: <d4f8365a-a840-4024-8d25-d7930035947f@redhat.com> (raw)
In-Reply-To: <20250826153721.GA23292@cathedrallabs.org>
On 26.08.25 17:37, Aristeu Rozanski wrote:
> As raised by Andrew [1], a folio/compound page never spans a negative number
> of pages. Consequently, let's use "unsigned long" instead of "long"
> consistently for folio_nr_pages(), folio_large_nr_pages() and compound_nr().
>
> Using "unsigned long" as return value is fine, because even
> "(long)-folio_nr_pages()" will keep on working as expected. Using "unsigned
> int" instead would actually break these use cases.
>
> This patch takes the first step changing these to return unsigned long (and
> making drm_gem_get_pages() use the new types instead of replacing min()).
>
> In the future, we might want to make more callers of these functions to
> consistently use "unsigned long".
>
> [1] https://lore.kernel.org/linux-mm/20250503182858.5a02729fcffd6d4723afcfc2@linux-foundation.org/
>
> Link: https://lore.kernel.org/linux-mm/20250503182858.5a02729fcffd6d4723afcfc2@linux-foundation.org/
> Signed-off-by: Aristeu Rozanski <aris@ruivo.org>
> Suggested-by: Andrew Morton <akpm@linux-foundation.org>
> Suggested-by: David Hildenbrand <david@redhat.com>
> Cc: David Hildenbrand <david@redhat.com>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> Cc: Maxime Ripard <mripard@kernel.org>
> Cc: Thomas Zimmermann <tzimmermann@suse.de>
> Cc: David Airlie <airlied@gmail.com>
> Cc: Simona Vetter <simona@ffwll.ch>
>
TL;DR
Acked-by: David Hildenbrand <david@redhat.com>
I agree with Andrew that using an unsigned type is clearer, and it confused me in
the past.
When I last looked at that, I realized that "unsigned int" is problematic
(the hard way ...) and concluded that "unsigned long" should work as well.
commit 1ea5212aed0682ae1249cba9df7ad7ef539d0a7d
Author: David Hildenbrand <david@redhat.com>
Date: Mon Mar 3 17:29:55 2025 +0100
mm: factor out large folio handling from folio_nr_pages() into folio_large_nr_pages()
Let's factor it out into a simple helper function. This helper will also
come in handy when working with code where we know that our folio is
large.
While at it, let's consistently return a "long" value from all these
similar functions. Note that we cannot use "unsigned int" (even though
_folio_nr_pages is of that type), because it would break some callers that
do stuff like "-folio_nr_pages()". Both "int" or "unsigned long" would
work as well.
compound_nr() used "unsigned long" before.
I did a quick test below to make sure I am not missing something.
"unsigned long" works as expected except. The only exception would be 32bit
when we would have a function that expects a signed or unsigned "long long".
Should we be using "unsigned long long"? I don't think so.
Using long long / unsigned long long for something nr_pages (or PFN) related
is highly questionable and should be cleaned up.
Because I am curious, si I looked around (I hope I caught most cases):
$ git grep -e "\- *folio_nr_pages"
include/linux/mm_inline.h: -folio_nr_pages(folio));
-> consumes "long nr_pages"
include/linux/vmstat.h: __mod_zone_page_state(folio_zone(folio), item, -folio_nr_pages(folio));
include/linux/vmstat.h: mod_zone_page_state(folio_zone(folio), item, -folio_nr_pages(folio));
include/linux/vmstat.h: __mod_node_page_state(folio_pgdat(folio), item, -folio_nr_pages(folio));
include/linux/vmstat.h: mod_node_page_state(folio_pgdat(folio), item, -folio_nr_pages(folio));
-> consume "long delta"
include/linux/vmstat.h: __lruvec_stat_mod_folio(folio, idx, -folio_nr_pages(folio));
include/linux/vmstat.h: lruvec_stat_mod_folio(folio, idx, -folio_nr_pages(folio));
-> Consume "int val"
mm/khugepaged.c: -folio_nr_pages(folio));
-> Consumes "long nr"
mm/memcontrol-v1.c: memcg1_charge_statistics(memcg, -folio_nr_pages(folio));
-> Consumes "int nr_pages"
mm/migrate.c: folio_is_file_lru(folio), -folio_nr_pages(folio));
-> consume "long delta"
mm/migrate.c: folio_ref_unfreeze(src, expected_count - folio_nr_pages(src));
-> expected_count is an "int"
mm/migrate.c: folio_is_file_lru(src), -folio_nr_pages(src));
-> Consumes "long nr"
$ git grep -e "\-= *folio_nr_pages"
fs/bcachefs/fs-io-buffered.c: ractl->_index -= folio_nr_pages(folio);
-> "pgoff_t _index" -> "unsigned long"
fs/btrfs/extent_io.c: wbc->nr_to_write -= folio_nr_pages(folio);
fs/ext4/inode.c: mpd->wbc->nr_to_write -= folio_nr_pages(folio);
fs/ext4/inode.c: mpd->wbc->nr_to_write -= folio_nr_pages(folio);
-> "long nr_to_write"
mm/memory-failure.c: count -= folio_nr_pages(page_folio(p));
-> "int count"
mm/page-writeback.c: wbc->nr_to_write -= folio_nr_pages(folio);
-> "long nr_to_write"
--
$ gcc tst.c -o tst; ./tst
Testing: receive_unsigned_long_long(-return_unsigned_long_long())
PASS
Testing: receive_unsigned_long_long(-return_long_long())
PASS
Testing: receive_unsigned_long_long(-return_unsigned_long())
PASS
Testing: receive_unsigned_long_long(-return_long())
PASS
Testing: receive_unsigned_long_long(-return_unsigned_int())
FAIL
Testing: receive_unsigned_long_long(-return_int())
PASS
Testing: receive_long_long(-return_unsigned_long_long())
PASS
Testing: receive_long_long(-return_long_long())
PASS
Testing: receive_long_long(-return_unsigned_long())
PASS
Testing: receive_long_long(-return_long())
PASS
Testing: receive_long_long(-return_unsigned_int())
FAIL
Testing: receive_long_long(-return_int())
PASS
Testing: receive_unsigned_long(-return_unsigned_long_long())
PASS
Testing: receive_unsigned_long(-return_long_long())
PASS
Testing: receive_unsigned_long(-return_unsigned_long())
PASS
Testing: receive_unsigned_long(-return_long())
PASS
Testing: receive_unsigned_long(-return_unsigned_int())
FAIL
Testing: receive_unsigned_long(-return_int())
PASS
Testing: receive_long(-return_unsigned_long_long())
PASS
Testing: receive_long(-return_long_long())
PASS
Testing: receive_long(-return_unsigned_long())
PASS
Testing: receive_long(-return_long())
PASS
Testing: receive_long(-return_unsigned_int())
FAIL
Testing: receive_long(-return_int())
PASS
Testing: receive_unsigned_int(-return_unsigned_long_long())
PASS
Testing: receive_unsigned_int(-return_long_long())
PASS
Testing: receive_unsigned_int(-return_unsigned_long())
PASS
Testing: receive_unsigned_int(-return_long())
PASS
Testing: receive_unsigned_int(-return_unsigned_int())
PASS
Testing: receive_unsigned_int(-return_int())
PASS
Testing: receive_int(-return_unsigned_long_long())
PASS
Testing: receive_int(-return_long_long())
PASS
Testing: receive_int(-return_unsigned_long())
PASS
Testing: receive_int(-return_long())
PASS
Testing: receive_int(-return_unsigned_int())
PASS
Testing: receive_int(-return_int())
PASS
$ gcc -m32 tst.c -o tst; ./tst
Testing: receive_unsigned_long_long(-return_unsigned_long_long())
PASS
Testing: receive_unsigned_long_long(-return_long_long())
PASS
Testing: receive_unsigned_long_long(-return_unsigned_long())
FAIL
Testing: receive_unsigned_long_long(-return_long())
PASS
Testing: receive_unsigned_long_long(-return_unsigned_int())
FAIL
Testing: receive_unsigned_long_long(-return_int())
PASS
Testing: receive_long_long(-return_unsigned_long_long())
PASS
Testing: receive_long_long(-return_long_long())
PASS
Testing: receive_long_long(-return_unsigned_long())
FAIL
Testing: receive_long_long(-return_long())
PASS
Testing: receive_long_long(-return_unsigned_int())
FAIL
Testing: receive_long_long(-return_int())
PASS
Testing: receive_unsigned_long(-return_unsigned_long_long())
PASS
Testing: receive_unsigned_long(-return_long_long())
PASS
Testing: receive_unsigned_long(-return_unsigned_long())
PASS
Testing: receive_unsigned_long(-return_long())
PASS
Testing: receive_unsigned_long(-return_unsigned_int())
PASS
Testing: receive_unsigned_long(-return_int())
PASS
Testing: receive_long(-return_unsigned_long_long())
PASS
Testing: receive_long(-return_long_long())
PASS
Testing: receive_long(-return_unsigned_long())
PASS
Testing: receive_long(-return_long())
PASS
Testing: receive_long(-return_unsigned_int())
PASS
Testing: receive_long(-return_int())
PASS
Testing: receive_unsigned_int(-return_unsigned_long_long())
PASS
Testing: receive_unsigned_int(-return_long_long())
PASS
Testing: receive_unsigned_int(-return_unsigned_long())
PASS
Testing: receive_unsigned_int(-return_long())
PASS
Testing: receive_unsigned_int(-return_unsigned_int())
PASS
Testing: receive_unsigned_int(-return_int())
PASS
Testing: receive_int(-return_unsigned_long_long())
PASS
Testing: receive_int(-return_long_long())
PASS
Testing: receive_int(-return_unsigned_long())
PASS
Testing: receive_int(-return_long())
PASS
Testing: receive_int(-return_unsigned_int())
PASS
Testing: receive_int(-return_int())
PASS
--
#include <stdio.h>
#include <stdbool.h>
volatile int orig_val = 1234;
volatile int receive_val = -1234;
static unsigned long long return_unsigned_long_long(void)
{
return orig_val;
}
static long long return_long_long(void)
{
return orig_val;
}
static unsigned long return_unsigned_long(void)
{
return orig_val;
}
static long return_long(void)
{
return orig_val;
}
static unsigned int return_unsigned_int(void)
{
return orig_val;
}
static int return_int(void)
{
return orig_val;
}
static bool receive_unsigned_long_long(unsigned long long val)
{
return val == receive_val;
}
static bool receive_long_long(long long val)
{
return val == receive_val;
}
static bool receive_unsigned_long(unsigned long val)
{
return val == receive_val;
}
static bool receive_long(long val)
{
return val == receive_val;
}
static bool receive_unsigned_int(unsigned int val)
{
return val == receive_val;
}
static bool receive_int(int val)
{
return val == receive_val;
}
#define TEST(cmd) \
do { \
printf("Testing: " #cmd "\n"); \
if (cmd) \
printf("\tPASS\n"); \
else \
printf("\tFAIL\n"); \
} while(0)
int main(int ac, char** av) {
TEST(receive_unsigned_long_long(-return_unsigned_long_long()));
TEST(receive_unsigned_long_long(-return_long_long()));
TEST(receive_unsigned_long_long(-return_unsigned_long()));
TEST(receive_unsigned_long_long(-return_long()));
TEST(receive_unsigned_long_long(-return_unsigned_int()));
TEST(receive_unsigned_long_long(-return_int()));
TEST(receive_long_long(-return_unsigned_long_long()));
TEST(receive_long_long(-return_long_long()));
TEST(receive_long_long(-return_unsigned_long()));
TEST(receive_long_long(-return_long()));
TEST(receive_long_long(-return_unsigned_int()));
TEST(receive_long_long(-return_int()));
TEST(receive_unsigned_long(-return_unsigned_long_long()));
TEST(receive_unsigned_long(-return_long_long()));
TEST(receive_unsigned_long(-return_unsigned_long()));
TEST(receive_unsigned_long(-return_long()));
TEST(receive_unsigned_long(-return_unsigned_int()));
TEST(receive_unsigned_long(-return_int()));
TEST(receive_long(-return_unsigned_long_long()));
TEST(receive_long(-return_long_long()));
TEST(receive_long(-return_unsigned_long()));
TEST(receive_long(-return_long()));
TEST(receive_long(-return_unsigned_int()));
TEST(receive_long(-return_int()));
TEST(receive_unsigned_int(-return_unsigned_long_long()));
TEST(receive_unsigned_int(-return_long_long()));
TEST(receive_unsigned_int(-return_unsigned_long()));
TEST(receive_unsigned_int(-return_long()));
TEST(receive_unsigned_int(-return_unsigned_int()));
TEST(receive_unsigned_int(-return_int()));
TEST(receive_int(-return_unsigned_long_long()));
TEST(receive_int(-return_long_long()));
TEST(receive_int(-return_unsigned_long()));
TEST(receive_int(-return_long()));
TEST(receive_int(-return_unsigned_int()));
TEST(receive_int(-return_int()));
return 0;
}
--
Cheers
David / dhildenb
prev parent reply other threads:[~2025-08-27 9:13 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-26 15:37 Aristeu Rozanski
2025-08-26 19:00 ` Matthew Wilcox
2025-08-26 21:45 ` David Hildenbrand
2025-08-26 22:00 ` Aristeu Rozanski
2025-08-27 1:20 ` Matthew Wilcox
2025-08-27 9:14 ` David Hildenbrand
2025-08-27 9:13 ` David Hildenbrand [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=d4f8365a-a840-4024-8d25-d7930035947f@redhat.com \
--to=david@redhat.com \
--cc=airlied@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=aris@ruivo.org \
--cc=linux-mm@kvack.org \
--cc=maarten.lankhorst@linux.intel.com \
--cc=mripard@kernel.org \
--cc=simona@ffwll.ch \
--cc=tzimmermann@suse.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox