diff mbox series

[v13,010/137] mm: Add folio flag manipulation functions

Message ID 20210712030701.4000097-11-willy@infradead.org (mailing list archive)
State New, archived
Headers show
Series Memory folios | expand

Commit Message

Matthew Wilcox (Oracle) July 12, 2021, 3:04 a.m. UTC
These new functions are the folio analogues of the various PageFlags
functions.  If CONFIG_DEBUG_VM_PGFLAGS is enabled, we check the folio
is not a tail page at every invocation.  This will also catch the
PagePoisoned case as a poisoned page has every bit set, which would
include PageTail.

This saves 1684 bytes of text with the distro-derived config that
I'm testing due to removing a double call to compound_head() in
PageSwapCache().

Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Acked-by: Jeff Layton <jlayton@kernel.org>
Acked-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Vlastimil Babka <vbabka@suse.cz>
Reviewed-by: William Kucharski <william.kucharski@oracle.com>
Reviewed-by: David Howells <dhowells@redhat.com>
---
 include/linux/page-flags.h | 219 ++++++++++++++++++++++++++-----------
 1 file changed, 156 insertions(+), 63 deletions(-)

Comments

Johannes Weiner July 13, 2021, 12:24 a.m. UTC | #1
On Mon, Jul 12, 2021 at 04:04:54AM +0100, Matthew Wilcox (Oracle) wrote:
> +/* Whether there are one or multiple pages in a folio */
> +static inline bool folio_single(struct folio *folio)
> +{
> +	return !folio_head(folio);
> +}

Reading more converted code in the series, I keep tripping over the
new non-camelcased flag testers.

It's not an issue when it's adjectives: folio_uptodate(),
folio_referenced(), folio_locked() etc. - those are obvious. But nouns
and words that overlap with struct member names can easily be confused
with non-bool accessors and lookups. Pop quiz: flag test or accessor?

folio_private()
folio_lru()
folio_nid()
folio_head()
folio_mapping()
folio_slab()
folio_waiters()

This requires a lot of double-taking on what is actually being
queried. Bool types, ! etc. don't help, since we test pointers for
NULL/non-NULL all the time.

I see in a later patch you changed the existing page_lru() (which
returns an enum) to folio_lru_list() to avoid the obvious collision
with the PG_lru flag test. page_private() has the same problem but it
changed into folio_get_private() (no refcounting involved). There
doesn't seem to be a consistent, future-proof scheme to avoid this new
class of collisions between flag testing and member accessors.

There is also an inconsistency between flag test and set that makes me
pause to think if they're actually testing and setting the same thing:

	if (folio_idle(folio))
		folio_clear_idle_flag(folio);

Compare this to check_move_unevictable_pages(), where we do

	if (page_evictable(page))
		ClearPageUnevictable(page);

where one queries a more complex, contextual userpage state and the
other updates the corresponding pageframe bit flag.

The camelcase stuff we use for page flag testing is unusual for kernel
code. But the page API is also unusually rich and sprawling. What
would actually come close? task? inode? Having those multiple
namespaces to structure and organize the API has been quite helpful.

On top of losing the flagops namespacing, this series also disappears
many <verb>_page() operations (which currently optically distinguish
themselves from page_<noun>() accessors) into the shared folio_
namespace. This further increases the opportunities for collisions,
which force undesirable naming compromises and/or ambiguity.

More double-taking when the verb can be read as a noun: lock_folio()
vs folio_lock().

Now, is anybody going to mistake folio_lock() for an accessor? Not
once they think about it. Can you figure out and remember what
folio_head() returns? Probably. What about all the examples above at
the same time? Personally, I'm starting to struggle. It certainly
eliminates syntactic help and pattern matching, and puts much more
weight on semantic analysis and remembering API definitions.

What about functions like shrink_page_list() which are long sequences
of page queries and manipulations? Many lines would be folio_<foo>
with no further cue whether you're looking at tests, accessors, or a
high-level state change that is being tested for success. There are
fewer visual anchors to orient yourself when you page up and down. It
quite literally turns some code into blah_(), blah_(), blah_():

       if (!folio_active(folio) && !folio_unevictable(folio)) {
	       folio_del_from_lru_list(folio, lruvec);
	       folio_set_active_flag(folio);
	       folio_add_to_lru_list(folio, lruvec);
	       trace_mm_lru_activate(&folio->page);
	}

Think about the mental strain of reading and writing complicated
memory management code with such a degree of syntactic parsimony, let
alone the repetetive monotony.

In those few lines of example code alone, readers will pause on things
that should be obvious, and miss grave errors that should stand out.

Add compatible return types to similarly named functions and we'll
provoke subtle bugs that the compiler won't catch either.

There are warts and inconsistencies in our naming patterns that could
use cleanups. But I think this compresses a vast API into one template
that isn't nearly expressive enough to adequately communicate and
manage the complexity of the underlying structure and its operations.
Matthew Wilcox (Oracle) July 13, 2021, 2:15 a.m. UTC | #2
On Mon, Jul 12, 2021 at 08:24:09PM -0400, Johannes Weiner wrote:
> On Mon, Jul 12, 2021 at 04:04:54AM +0100, Matthew Wilcox (Oracle) wrote:
> > +/* Whether there are one or multiple pages in a folio */
> > +static inline bool folio_single(struct folio *folio)
> > +{
> > +	return !folio_head(folio);
> > +}
> 
> Reading more converted code in the series, I keep tripping over the
> new non-camelcased flag testers.

Added PeterZ as he asked for it.

https://lore.kernel.org/linux-mm/20210419135528.GC2531743@casper.infradead.org/

> It's not an issue when it's adjectives: folio_uptodate(),
> folio_referenced(), folio_locked() etc. - those are obvious. But nouns
> and words that overlap with struct member names can easily be confused
> with non-bool accessors and lookups. Pop quiz: flag test or accessor?
> 
> folio_private()
> folio_lru()
> folio_nid()
> folio_head()
> folio_mapping()
> folio_slab()
> folio_waiters()

I know the answers to each of those, but your point is valid.  So what's
your preferred alternative?  folio_is_lru(), folio_is_uptodate(),
folio_is_slab(), etc?  I've seen suggestions for folio_test_lru(),
folio_test_uptodate(), and I don't much care for that alternative.

> This requires a lot of double-taking on what is actually being
> queried. Bool types, ! etc. don't help, since we test pointers for
> NULL/non-NULL all the time.
> 
> I see in a later patch you changed the existing page_lru() (which
> returns an enum) to folio_lru_list() to avoid the obvious collision
> with the PG_lru flag test. page_private() has the same problem but it
> changed into folio_get_private() (no refcounting involved). There
> doesn't seem to be a consistent, future-proof scheme to avoid this new
> class of collisions between flag testing and member accessors.
> 
> There is also an inconsistency between flag test and set that makes me
> pause to think if they're actually testing and setting the same thing:
> 
> 	if (folio_idle(folio))
> 		folio_clear_idle_flag(folio);
> 
> Compare this to check_move_unevictable_pages(), where we do
> 
> 	if (page_evictable(page))
> 		ClearPageUnevictable(page);
> 
> where one queries a more complex, contextual userpage state and the
> other updates the corresponding pageframe bit flag.
> 
> The camelcase stuff we use for page flag testing is unusual for kernel
> code. But the page API is also unusually rich and sprawling. What
> would actually come close? task? inode? Having those multiple
> namespaces to structure and organize the API has been quite helpful.
> 
> On top of losing the flagops namespacing, this series also disappears
> many <verb>_page() operations (which currently optically distinguish
> themselves from page_<noun>() accessors) into the shared folio_
> namespace. This further increases the opportunities for collisions,
> which force undesirable naming compromises and/or ambiguity.
> 
> More double-taking when the verb can be read as a noun: lock_folio()
> vs folio_lock().
> 
> Now, is anybody going to mistake folio_lock() for an accessor? Not
> once they think about it. Can you figure out and remember what
> folio_head() returns? Probably. What about all the examples above at
> the same time? Personally, I'm starting to struggle. It certainly
> eliminates syntactic help and pattern matching, and puts much more
> weight on semantic analysis and remembering API definitions.

Other people have given the opposite advice.  For example,
https://lore.kernel.org/linux-mm/YMmfQNjExNs3cuyq@kroah.com/

> What about functions like shrink_page_list() which are long sequences
> of page queries and manipulations? Many lines would be folio_<foo>
> with no further cue whether you're looking at tests, accessors, or a
> high-level state change that is being tested for success. There are
> fewer visual anchors to orient yourself when you page up and down. It
> quite literally turns some code into blah_(), blah_(), blah_():
> 
>        if (!folio_active(folio) && !folio_unevictable(folio)) {
> 	       folio_del_from_lru_list(folio, lruvec);
> 	       folio_set_active_flag(folio);
> 	       folio_add_to_lru_list(folio, lruvec);
> 	       trace_mm_lru_activate(&folio->page);
> 	}

I actually like the way that looks (other than the trace_mm_lru_activate()
which is pending a conversion from page to folio).  But I have my head
completely down in it, and I can't tell what works for someone who's
fresh to it.  I do know that it's hard to change from an API you're
used to (and that's part of the cost of changing an API), and I don't
know how to balance that against making a more discoverable API.

> Think about the mental strain of reading and writing complicated
> memory management code with such a degree of syntactic parsimony, let
> alone the repetetive monotony.
> 
> In those few lines of example code alone, readers will pause on things
> that should be obvious, and miss grave errors that should stand out.
> 
> Add compatible return types to similarly named functions and we'll
> provoke subtle bugs that the compiler won't catch either.
> 
> There are warts and inconsistencies in our naming patterns that could
> use cleanups. But I think this compresses a vast API into one template
> that isn't nearly expressive enough to adequately communicate and
> manage the complexity of the underlying structure and its operations.

I don't want to dismiss your concerns.  I just don't agree with them.
If there's a consensus on folio_verb() vs verb_folio(), I'm happy to
go back through all these patches and do the rename.
Peter Zijlstra July 13, 2021, 9:15 a.m. UTC | #3
On Tue, Jul 13, 2021 at 03:15:10AM +0100, Matthew Wilcox wrote:
> On Mon, Jul 12, 2021 at 08:24:09PM -0400, Johannes Weiner wrote:
> > On Mon, Jul 12, 2021 at 04:04:54AM +0100, Matthew Wilcox (Oracle) wrote:
> > > +/* Whether there are one or multiple pages in a folio */
> > > +static inline bool folio_single(struct folio *folio)
> > > +{
> > > +	return !folio_head(folio);
> > > +}
> > 
> > Reading more converted code in the series, I keep tripping over the
> > new non-camelcased flag testers.
> 
> Added PeterZ as he asked for it.
> 
> https://lore.kernel.org/linux-mm/20210419135528.GC2531743@casper.infradead.org/

Aye; I hate me some Camels with a passion. And Linux Coding style
explicitly not having Camels these things were always a sore spot. I'm
very glad to see them go.

> > It's not an issue when it's adjectives: folio_uptodate(),
> > folio_referenced(), folio_locked() etc. - those are obvious. But nouns
> > and words that overlap with struct member names can easily be confused
> > with non-bool accessors and lookups. Pop quiz: flag test or accessor?
> > 
> > folio_private()
> > folio_lru()
> > folio_nid()
> > folio_head()
> > folio_mapping()
> > folio_slab()
> > folio_waiters()
> 
> I know the answers to each of those, but your point is valid.  So what's
> your preferred alternative?  folio_is_lru(), folio_is_uptodate(),
> folio_is_slab(), etc?  I've seen suggestions for folio_test_lru(),
> folio_test_uptodate(), and I don't much care for that alternative.

Either _is_ or _test_ works for me, with a slight preference to _is_ on
account it of being shorter.

> > Now, is anybody going to mistake folio_lock() for an accessor? Not
> > once they think about it. Can you figure out and remember what
> > folio_head() returns? Probably. What about all the examples above at
> > the same time? Personally, I'm starting to struggle. It certainly
> > eliminates syntactic help and pattern matching, and puts much more
> > weight on semantic analysis and remembering API definitions.
> 
> Other people have given the opposite advice.  For example,
> https://lore.kernel.org/linux-mm/YMmfQNjExNs3cuyq@kroah.com/

Yes, we -tip folk tend to also prefer consistent prefix_ naming, and
every time something big gets refactorered we make sure to make it so.

Look at it like a namespace; you can read it like
folio::del_from_lru_list() if you want. Obviously there's nothing like
'using folio' for this being C and not C++.

> > What about functions like shrink_page_list() which are long sequences
> > of page queries and manipulations? Many lines would be folio_<foo>
> > with no further cue whether you're looking at tests, accessors, or a
> > high-level state change that is being tested for success. There are
> > fewer visual anchors to orient yourself when you page up and down. It
> > quite literally turns some code into blah_(), blah_(), blah_():
> > 
> >        if (!folio_active(folio) && !folio_unevictable(folio)) {
> > 	       folio_del_from_lru_list(folio, lruvec);
> > 	       folio_set_active_flag(folio);
> > 	       folio_add_to_lru_list(folio, lruvec);
> > 	       trace_mm_lru_activate(&folio->page);
> > 	}
> 
> I actually like the way that looks (other than the trace_mm_lru_activate()
> which is pending a conversion from page to folio).  But I have my head
> completely down in it, and I can't tell what works for someone who's
> fresh to it.  I do know that it's hard to change from an API you're
> used to (and that's part of the cost of changing an API), and I don't
> know how to balance that against making a more discoverable API.

Yeah, I don't particularly have a problem with the repeated folio_ thing
either, it's something you'll get used to.

I agree that significantly changing the naming of things is a majoy
PITA, but given the level of refactoring at that, I think folio_ beats
pageymcpageface_. Give it some time to get used to it...
Johannes Weiner July 13, 2021, 3:55 p.m. UTC | #4
On Tue, Jul 13, 2021 at 11:15:33AM +0200, Peter Zijlstra wrote:
> On Tue, Jul 13, 2021 at 03:15:10AM +0100, Matthew Wilcox wrote:
> > On Mon, Jul 12, 2021 at 08:24:09PM -0400, Johannes Weiner wrote:
> > > On Mon, Jul 12, 2021 at 04:04:54AM +0100, Matthew Wilcox (Oracle) wrote:
> > > > +/* Whether there are one or multiple pages in a folio */
> > > > +static inline bool folio_single(struct folio *folio)
> > > > +{
> > > > +	return !folio_head(folio);
> > > > +}
> > > 
> > > Reading more converted code in the series, I keep tripping over the
> > > new non-camelcased flag testers.
> > 
> > Added PeterZ as he asked for it.
> > 
> > https://lore.kernel.org/linux-mm/20210419135528.GC2531743@casper.infradead.org/
> 
> Aye; I hate me some Camels with a passion. And Linux Coding style
> explicitly not having Camels these things were always a sore spot. I'm
> very glad to see them go.
> 
> > > It's not an issue when it's adjectives: folio_uptodate(),
> > > folio_referenced(), folio_locked() etc. - those are obvious. But nouns
> > > and words that overlap with struct member names can easily be confused
> > > with non-bool accessors and lookups. Pop quiz: flag test or accessor?
> > > 
> > > folio_private()
> > > folio_lru()
> > > folio_nid()
> > > folio_head()
> > > folio_mapping()
> > > folio_slab()
> > > folio_waiters()
> > 
> > I know the answers to each of those, but your point is valid.  So what's
> > your preferred alternative?  folio_is_lru(), folio_is_uptodate(),
> > folio_is_slab(), etc?  I've seen suggestions for folio_test_lru(),
> > folio_test_uptodate(), and I don't much care for that alternative.
> 
> Either _is_ or _test_ works for me, with a slight preference to _is_ on
> account it of being shorter.

I agree that _is_ reads nicer by itself, but paired with other ops
such as testset, _test_ might be better.

For example, in __set_page_dirty_no_writeback()

	if (folio_is_dirty())
		return !folio_testset_dirty()

is less clear about what's going on than would be:

	if (folio_test_dirty())
		return !folio_testset_dirty()

My other example wasn't quoted, but IMO set and clear naming should
also match testing to not cause confusion. I.e. the current:

	if (folio_idle())
		folio_clear_idle_flag()

can make you think two different things are being tested and modified
(as in if (page_evictable()) ClearPageUnevictable()). IMO easier:

	if (folio_test_idle())
		folio_clear_idle()

Non-atomics would have the __ modifier in front of folio rather than
read __clear or __set, which works I suppose?

	__folio_clear_dirty()

With all that, we'd have something like:

	folio_test_foo()
	folio_set_foo()
	folio_clear_foo()
	folio_testset_foo()
	folio_testclear_foo()

	__folio_test_foo()
	__folio_set_foo()
	__folio_clear_foo()

Would that be a workable compromise for everybody?

> > > Now, is anybody going to mistake folio_lock() for an accessor? Not
> > > once they think about it. Can you figure out and remember what
> > > folio_head() returns? Probably. What about all the examples above at
> > > the same time? Personally, I'm starting to struggle. It certainly
> > > eliminates syntactic help and pattern matching, and puts much more
> > > weight on semantic analysis and remembering API definitions.
> > 
> > Other people have given the opposite advice.  For example,
> > https://lore.kernel.org/linux-mm/YMmfQNjExNs3cuyq@kroah.com/
> 
> Yes, we -tip folk tend to also prefer consistent prefix_ naming, and
> every time something big gets refactorered we make sure to make it so.
> 
> Look at it like a namespace; you can read it like
> folio::del_from_lru_list() if you want. Obviously there's nothing like
> 'using folio' for this being C and not C++.

Yeah the lack of `using` is my concern.

Namespacing is nice for more contained APIs. Classic class + method
type deals, with non-namespaced private helpers implementing public
methods, and public methods not layered past trivial stuff like
foo_insert() calling __foo_insert() with a lock held.

memcg, vmalloc, kobject, you name it.

But the page api is pretty sprawling with sizable overlaps between
interface and implementation, and heavy layering in both. `using`
would be great to avoid excessive repetition where file or function
context already does plenty of namespacing. Alas, it's not an option.

So IMO we're taking a concept of more stringent object-oriented
encapsulation to a large, heavily layered public API without having
the tools e.g. C++ provides to manage exactly such situations.

If everybody agrees we'll be fine, I won't stand in the way. But I do
think the page API is a bit unusual in that regard. And while it is
nice for the outward-facing filesystem interface - and I can see why
fs people love it - the cost of it seems to be carried by the MM
implementation code.

> > > What about functions like shrink_page_list() which are long sequences
> > > of page queries and manipulations? Many lines would be folio_<foo>
> > > with no further cue whether you're looking at tests, accessors, or a
> > > high-level state change that is being tested for success. There are
> > > fewer visual anchors to orient yourself when you page up and down. It
> > > quite literally turns some code into blah_(), blah_(), blah_():
> > > 
> > >        if (!folio_active(folio) && !folio_unevictable(folio)) {
> > > 	       folio_del_from_lru_list(folio, lruvec);
> > > 	       folio_set_active_flag(folio);
> > > 	       folio_add_to_lru_list(folio, lruvec);
> > > 	       trace_mm_lru_activate(&folio->page);
> > > 	}
> > 
> > I actually like the way that looks (other than the trace_mm_lru_activate()
> > which is pending a conversion from page to folio).  But I have my head
> > completely down in it, and I can't tell what works for someone who's
> > fresh to it.  I do know that it's hard to change from an API you're
> > used to (and that's part of the cost of changing an API), and I don't
> > know how to balance that against making a more discoverable API.
> 
> Yeah, I don't particularly have a problem with the repeated folio_ thing
> either, it's something you'll get used to.

Yeah I won't stand in the way if everybody agrees this is fine.

Although I will say, folio_del_from_lru_list() reads a bit like
'a'.append_to(string) to me. lruvec_add_folio() would match more
conventional object hierarchy for container/collection/list/array
interactions, like with list_add, xa_store, rb_insert, etc.

Taking all of the above, we'd have:

	if (!folio_test_active(folio) && !folio_test_unevictable(folio)) {
		lruvec_del_folio(folio, lruvec);
		folio_set_active(folio);
		lruvec_add_folio(folio, lruvec);
		trace_mm_lru_activate(&folio->page);
	}

which reads a little better overall, IMO.

Is that a direction we could agree on?


It still loses the visual anchoring of page state changes. These are
often the "commit" part of multi-step transactions, and having those
cut through the procedural grind a bit is nice - to see more easily
what the code is fundamentally about, what is prerequisite for the
transaction, and what is post-transactional housekeeping noise:

	if (!PageActive(page) && !PageUnevictable(page)) {
		del_page_from_lru_list(page, lruvec);
		SetPageActive(page);
		add_page_to_lru_list(page, lruvec);
		trace_mm_lru_activate(page);
	}

Similar for isolation clearing PG_lru (empties, comments, locals
removed):

		if (page_zonenum(page) > sc->reclaim_idx) {
			list_move(&page->lru, &pages_skipped);
			nr_skipped[page_zonenum(page)] += nr_pages;
			continue;
		}
		scan += nr_pages;
		if (!__isolate_lru_page_prepare(page, mode)) {
			list_move(&page->lru, src);
			continue;
		}
		if (unlikely(!get_page_unless_zero(page))) {
			list_move(&page->lru, src);
			continue;
		}
		if (!TestClearPageLRU(page)) {
			put_page(page);
			list_move(&page->lru, src);
			continue;
		}
		nr_taken += nr_pages;
		nr_zone_taken[page_zonenum(page)] += nr_pages;
		list_move(&page->lru, dst);

Or writeback clearing PG_writeback:

	lock_page_memcg(page);
	if (mapping && mapping_use_writeback_tags(mapping)) {
		xa_lock_irqsave(&mapping->i_pages, flags);
		ret = TestClearPageWriteback(page);
		if (ret) {
			__xa_clear_mark(&mapping->i_pages, page_index(page),
						PAGECACHE_TAG_WRITEBACK);
			if (bdi->capabilities & BDI_CAP_WRITEBACK_ACCT) {
				dec_wb_stat(wb, WB_WRITEBACK);
				__wb_writeout_inc(wb);
			}
		}
		if (mapping->host && !mapping_tagged(mapping,
						     PAGECACHE_TAG_WRITEBACK))
			sb_clear_inode_writeback(mapping->host);
		xa_unlock_irqrestore(&mapping->i_pages, flags);
	} else {
		ret = TestClearPageWriteback(page);
	}
	if (ret) {
		dec_lruvec_page_state(page, NR_WRITEBACK);
		dec_zone_page_state(page, NR_ZONE_WRITE_PENDING);
		inc_node_page_state(page, NR_WRITTEN);
	}
	unlock_page_memcg(page);

It's somewhat unfortunate to lose that bit of extra help when
navigating the code, but I suppose we can live without it.

> I agree that significantly changing the naming of things is a majoy
> PITA, but given the level of refactoring at that, I think folio_ beats
> pageymcpageface_. Give it some time to get used to it...

I'll try ;-)
Matthew Wilcox (Oracle) July 14, 2021, 1:55 a.m. UTC | #5
On Tue, Jul 13, 2021 at 11:55:04AM -0400, Johannes Weiner wrote:
> On Tue, Jul 13, 2021 at 11:15:33AM +0200, Peter Zijlstra wrote:
> > On Tue, Jul 13, 2021 at 03:15:10AM +0100, Matthew Wilcox wrote:
> > > On Mon, Jul 12, 2021 at 08:24:09PM -0400, Johannes Weiner wrote:
> > > > On Mon, Jul 12, 2021 at 04:04:54AM +0100, Matthew Wilcox (Oracle) wrote:
> > > > > +/* Whether there are one or multiple pages in a folio */
> > > > > +static inline bool folio_single(struct folio *folio)
> > > > > +{
> > > > > +	return !folio_head(folio);
> > > > > +}
> > > > 
> > > > Reading more converted code in the series, I keep tripping over the
> > > > new non-camelcased flag testers.
> > > 
> > > Added PeterZ as he asked for it.
> > > 
> > > https://lore.kernel.org/linux-mm/20210419135528.GC2531743@casper.infradead.org/
> > 
> > Aye; I hate me some Camels with a passion. And Linux Coding style
> > explicitly not having Camels these things were always a sore spot. I'm
> > very glad to see them go.
> > 
> > > > It's not an issue when it's adjectives: folio_uptodate(),
> > > > folio_referenced(), folio_locked() etc. - those are obvious. But nouns
> > > > and words that overlap with struct member names can easily be confused
> > > > with non-bool accessors and lookups. Pop quiz: flag test or accessor?
> > > > 
> > > > folio_private()
> > > > folio_lru()
> > > > folio_nid()
> > > > folio_head()
> > > > folio_mapping()
> > > > folio_slab()
> > > > folio_waiters()
> > > 
> > > I know the answers to each of those, but your point is valid.  So what's
> > > your preferred alternative?  folio_is_lru(), folio_is_uptodate(),
> > > folio_is_slab(), etc?  I've seen suggestions for folio_test_lru(),
> > > folio_test_uptodate(), and I don't much care for that alternative.
> > 
> > Either _is_ or _test_ works for me, with a slight preference to _is_ on
> > account it of being shorter.
> 
> I agree that _is_ reads nicer by itself, but paired with other ops
> such as testset, _test_ might be better.
> 
> For example, in __set_page_dirty_no_writeback()
> 
> 	if (folio_is_dirty())
> 		return !folio_testset_dirty()
> 
> is less clear about what's going on than would be:
> 
> 	if (folio_test_dirty())
> 		return !folio_testset_dirty()
> 
> My other example wasn't quoted, but IMO set and clear naming should
> also match testing to not cause confusion. I.e. the current:
> 
> 	if (folio_idle())
> 		folio_clear_idle_flag()
> 
> can make you think two different things are being tested and modified
> (as in if (page_evictable()) ClearPageUnevictable()). IMO easier:
> 
> 	if (folio_test_idle())
> 		folio_clear_idle()
> 
> Non-atomics would have the __ modifier in front of folio rather than
> read __clear or __set, which works I suppose?
> 
> 	__folio_clear_dirty()
> 
> With all that, we'd have something like:
> 
> 	folio_test_foo()
> 	folio_set_foo()
> 	folio_clear_foo()
> 	folio_testset_foo()
> 	folio_testclear_foo()
> 
> 	__folio_test_foo()

BTW, this one doesn't exist.

> 	__folio_set_foo()
> 	__folio_clear_foo()
> 
> Would that be a workable compromise for everybody?

I think it has to be, because not all these work (marked with *):

  folio_is_locked()
  folio_is_referenced()
  folio_is_uptodate()
  folio_is_dirty()
* folio_is_lru()
  folio_is_active()
  folio_is_workingset()
* folio_is_waiters()
  folio_is_error()
  folio_is_slab()
* folio_is_owner_priv_1()
* folio_is_arch_1()
  folio_is_reserved()
* folio_is_private()
* folio_is_private_2()
  folio_is_writeback()
+ folio_is_head()
  folio_is_mappedtodisk()
* folio_is_reclaim()
  folio_is_swapbacked()
  folio_is_unevictable()
  folio_is_mlocked()
  folio_is_uncached()
* folio_is_hwpoison()
  folio_is_young()
  folio_is_idle()
  folio_is_arch_2()
* folio_is_skip_kasan_poison()
  folio_is_readahead()
  folio_is_checked()
  folio_is_swapcache()
  folio_is_fscache()
  folio_is_pinned()
  folio_is_savepinned()
  folio_is_foreign()
  folio_is_xen_remapped()
  folio_is_slob_free()
  folio_is_double_map()
  folio_is_isolated()
* folio_is_reported()

> > Yes, we -tip folk tend to also prefer consistent prefix_ naming, and
> > every time something big gets refactorered we make sure to make it so.
> > 
> > Look at it like a namespace; you can read it like
> > folio::del_from_lru_list() if you want. Obviously there's nothing like
> > 'using folio' for this being C and not C++.
> 
> Yeah the lack of `using` is my concern.
> 
> Namespacing is nice for more contained APIs. Classic class + method
> type deals, with non-namespaced private helpers implementing public
> methods, and public methods not layered past trivial stuff like
> foo_insert() calling __foo_insert() with a lock held.
> 
> memcg, vmalloc, kobject, you name it.
> 
> But the page api is pretty sprawling with sizable overlaps between
> interface and implementation, and heavy layering in both. `using`
> would be great to avoid excessive repetition where file or function
> context already does plenty of namespacing. Alas, it's not an option.

I mean, we could do ...

#include <linux/using_folio.h>

which makes
	bool test_writeback(struct folio *)
an alias of folio_test_writeback.  But I don't know that's a great
thing to do.  It makes it hard for people to get started in mm,
hard to move code between mm and other parts of the kernel, or
between mm/ and include/

Maybe I'm missing something important about 'using'.  It's been over
twenty years since I wrote Java in earnest and twenty-five since
I wrote a single line of Ada, so I'm a little rusty with the concept
of namespacing.

> If everybody agrees we'll be fine, I won't stand in the way. But I do
> think the page API is a bit unusual in that regard. And while it is
> nice for the outward-facing filesystem interface - and I can see why
> fs people love it - the cost of it seems to be carried by the MM
> implementation code.

I'm actually OK with that tradeoff.  There are more filesystem people than
MM people, and their concern is with how to implement their filesystem,
not with how the page cache works.  So if the MM side of the house needs
to be a little more complicated to make filesystems simpler, then that's
fine with me.

> Although I will say, folio_del_from_lru_list() reads a bit like
> 'a'.append_to(string) to me. lruvec_add_folio() would match more
> conventional object hierarchy for container/collection/list/array
> interactions, like with list_add, xa_store, rb_insert, etc.
> 
> Taking all of the above, we'd have:
> 
> 	if (!folio_test_active(folio) && !folio_test_unevictable(folio)) {
> 		lruvec_del_folio(folio, lruvec);
> 		folio_set_active(folio);
> 		lruvec_add_folio(folio, lruvec);
> 		trace_mm_lru_activate(&folio->page);
> 	}
> 
> which reads a little better overall, IMO.
> 
> Is that a direction we could agree on?

Yes!  I have that ordering already with filemap_add_folio().  I don't
mind doing that for lruvec too.  But, it should then be:

		lruvec_del_folio(lruvec, folio);
		folio_set_active(folio);
		lruvec_add_folio(lruvec, folio);
		trace_mm_lru_activate(folio);
Andrew Morton July 14, 2021, 1:56 a.m. UTC | #6
On Tue, 13 Jul 2021 11:55:04 -0400 Johannes Weiner <hannes@cmpxchg.org> wrote:

> On Tue, Jul 13, 2021 at 11:15:33AM +0200, Peter Zijlstra wrote:
> > On Tue, Jul 13, 2021 at 03:15:10AM +0100, Matthew Wilcox wrote:
> > > On Mon, Jul 12, 2021 at 08:24:09PM -0400, Johannes Weiner wrote:
> > > > On Mon, Jul 12, 2021 at 04:04:54AM +0100, Matthew Wilcox (Oracle) wrote:
> > > > > +/* Whether there are one or multiple pages in a folio */
> > > > > +static inline bool folio_single(struct folio *folio)
> > > > > +{
> > > > > +	return !folio_head(folio);
> > > > > +}
> > > > 
> > > > Reading more converted code in the series, I keep tripping over the
> > > > new non-camelcased flag testers.
> > > 
> > > Added PeterZ as he asked for it.
> > > 
> > > https://lore.kernel.org/linux-mm/20210419135528.GC2531743@casper.infradead.org/
> > 
> > Aye; I hate me some Camels with a passion. And Linux Coding style
> > explicitly not having Camels these things were always a sore spot. I'm
> > very glad to see them go.
> > 
> > > > It's not an issue when it's adjectives: folio_uptodate(),
> > > > folio_referenced(), folio_locked() etc. - those are obvious. But nouns
> > > > and words that overlap with struct member names can easily be confused
> > > > with non-bool accessors and lookups. Pop quiz: flag test or accessor?
> > > > 
> > > > folio_private()
> > > > folio_lru()
> > > > folio_nid()
> > > > folio_head()
> > > > folio_mapping()
> > > > folio_slab()
> > > > folio_waiters()
> > > 
> > > I know the answers to each of those, but your point is valid.  So what's
> > > your preferred alternative?  folio_is_lru(), folio_is_uptodate(),
> > > folio_is_slab(), etc?  I've seen suggestions for folio_test_lru(),
> > > folio_test_uptodate(), and I don't much care for that alternative.
> > 
> > Either _is_ or _test_ works for me, with a slight preference to _is_ on
> > account it of being shorter.

Useful discussion, and quite important.  Thanks for bringing it up.

> I agree that _is_ reads nicer by itself, but paired with other ops
> such as testset, _test_ might be better.
> 
> For example, in __set_page_dirty_no_writeback()
> 
> 	if (folio_is_dirty())
> 		return !folio_testset_dirty()
> 
> is less clear about what's going on than would be:
> 
> 	if (folio_test_dirty())
> 		return !folio_testset_dirty()

I like folio_is_foo().  As long as it is used consistently, we'll get
used to it quickly.

Some GNU tools are careful about appending "_p" to
functions-which-test-something (stands for "predicate").  Having spent
a lot of time a long time ago with my nose in this stuff, I found the
convention to be very useful.  I think foo_is_bar() is as good as
foo_bar_p() in this regard.

> 
> 	folio_test_foo()
> 	folio_set_foo()
> 	folio_clear_foo()
> 	folio_testset_foo()
> 	folio_testclear_foo()

Agree with everyone else about prefixing every symbol with "folio_". 
Although at times there will be heartache over which subsystem the
function actually belongs to.  For example, a hypothetical function
which writes back a folio to disk could be writeback_folio() or
folio_writeback().  Really it's a part of writeback so should be
writeback_folio().  Plus folio isn't really a subsystem.  But then,
neither is spin_lock much, and that naming works OK.


And sure, the CaMeLcAsE is fugly, but it sure is useful. 
set_page_dirty() is very different from SetPageDirty() and boy that
visual differentiation is a relief.
David Howells July 14, 2021, 9:18 a.m. UTC | #7
Johannes Weiner <hannes@cmpxchg.org> wrote:

> For example, in __set_page_dirty_no_writeback()
> 
> 	if (folio_is_dirty())
> 		return !folio_testset_dirty()
> 
> is less clear about what's going on than would be:
> 
> 	if (folio_test_dirty())
> 		return !folio_testset_dirty()

"if (folio_is_dirty())" reads better to me as that's more or less how you'd
structure a sentence beginning with "if" in English.

On the other hand, folio_test_xxx() fits in with a folio_testset_xxx() naming
style.  English doesn't really have test-and-set operator words.

David
Matthew Wilcox (Oracle) July 14, 2021, 2:03 p.m. UTC | #8
On Tue, Jul 13, 2021 at 06:56:28PM -0700, Andrew Morton wrote:
> On Tue, 13 Jul 2021 11:55:04 -0400 Johannes Weiner <hannes@cmpxchg.org> wrote:
> > I agree that _is_ reads nicer by itself, but paired with other ops
> > such as testset, _test_ might be better.
> > 
> > For example, in __set_page_dirty_no_writeback()
> > 
> > 	if (folio_is_dirty())
> > 		return !folio_testset_dirty()
> > 
> > is less clear about what's going on than would be:
> > 
> > 	if (folio_test_dirty())
> > 		return !folio_testset_dirty()
> 
> I like folio_is_foo().  As long as it is used consistently, we'll get
> used to it quickly.

I'm not sure that folio_is_private(), folio_is_lru(),
folio_is_waiters(), or folio_is_reclaim() really work.

> Some GNU tools are careful about appending "_p" to
> functions-which-test-something (stands for "predicate").  Having spent
> a lot of time a long time ago with my nose in this stuff, I found the
> convention to be very useful.  I think foo_is_bar() is as good as
> foo_bar_p() in this regard.

I just wish C let us put '?' on the end of a function name, but I
recognise the ambiguity with foo?bar:baz;

> And sure, the CaMeLcAsE is fugly, but it sure is useful. 
> set_page_dirty() is very different from SetPageDirty() and boy that
> visual differentiation is a relief.

Oh, I'm glad you brought that up </sarcasm>

In folios, here's how that ends up looking:

SetPageDirty() -> folio_set_dirty_flag()
		 (johannes proposes folio_set_dirty instead)
set_page_dirty() -> folio_mark_dirty()
aops->set_page_dirty() -> aops->dirty_folio()
__set_page_dirty() -> __folio_mark_dirty()
__set_page_dirty_buffers() -> block_dirty_folio()
__set_page_dirty_nobuffers() -> filemap_dirty_folio()
__set_page_dirty_no_writeback() -> dirty_folio_no_writeback()

I kind of feel that last one should be nowb_dirty_folio(), but I'm also
hoping to eliminate it; if the filesystem sets AS_NO_WRITEBACK_TAGS
in mapping->flags, then we just inline the no-writeback case into
folio_mark_dirty() (which already has it for the !mapping case).
diff mbox series

Patch

diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 70ede8345538..fb914468b302 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -143,6 +143,8 @@  enum pageflags {
 #endif
 	__NR_PAGEFLAGS,
 
+	PG_readahead = PG_reclaim,
+
 	/* Filesystems */
 	PG_checked = PG_owner_priv_1,
 
@@ -243,6 +245,15 @@  static inline void page_init_poison(struct page *page, size_t size)
 }
 #endif
 
+static unsigned long *folio_flags(struct folio *folio, unsigned n)
+{
+	struct page *page = &folio->page;
+
+	VM_BUG_ON_PGFLAGS(PageTail(page), page);
+	VM_BUG_ON_PGFLAGS(n > 0 && !test_bit(PG_head, &page->flags), page);
+	return &page[n].flags;
+}
+
 /*
  * Page flags policies wrt compound pages
  *
@@ -287,36 +298,64 @@  static inline void page_init_poison(struct page *page, size_t size)
 		VM_BUG_ON_PGFLAGS(!PageHead(page), page);		\
 		PF_POISONED_CHECK(&page[1]); })
 
+/* Which page is the flag stored in */
+#define FOLIO_PF_ANY		0
+#define FOLIO_PF_HEAD		0
+#define FOLIO_PF_ONLY_HEAD	0
+#define FOLIO_PF_NO_TAIL	0
+#define FOLIO_PF_NO_COMPOUND	0
+#define FOLIO_PF_SECOND		1
+
 /*
  * Macros to create function definitions for page flags
  */
 #define TESTPAGEFLAG(uname, lname, policy)				\
+static __always_inline bool folio_##lname(struct folio *folio)		\
+{ return test_bit(PG_##lname, folio_flags(folio, FOLIO_##policy)); }	\
 static __always_inline int Page##uname(struct page *page)		\
-	{ return test_bit(PG_##lname, &policy(page, 0)->flags); }
+{ return test_bit(PG_##lname, &policy(page, 0)->flags); }
 
 #define SETPAGEFLAG(uname, lname, policy)				\
+static __always_inline							\
+void folio_set_##lname##_flag(struct folio *folio)			\
+{ set_bit(PG_##lname, folio_flags(folio, FOLIO_##policy)); }		\
 static __always_inline void SetPage##uname(struct page *page)		\
-	{ set_bit(PG_##lname, &policy(page, 1)->flags); }
+{ set_bit(PG_##lname, &policy(page, 1)->flags); }
 
 #define CLEARPAGEFLAG(uname, lname, policy)				\
+static __always_inline							\
+void folio_clear_##lname##_flag(struct folio *folio)			\
+{ clear_bit(PG_##lname, folio_flags(folio, FOLIO_##policy)); }		\
 static __always_inline void ClearPage##uname(struct page *page)		\
-	{ clear_bit(PG_##lname, &policy(page, 1)->flags); }
+{ clear_bit(PG_##lname, &policy(page, 1)->flags); }
 
 #define __SETPAGEFLAG(uname, lname, policy)				\
+static __always_inline							\
+void __folio_set_##lname##_flag(struct folio *folio)			\
+{ __set_bit(PG_##lname, folio_flags(folio, FOLIO_##policy)); }		\
 static __always_inline void __SetPage##uname(struct page *page)		\
-	{ __set_bit(PG_##lname, &policy(page, 1)->flags); }
+{ __set_bit(PG_##lname, &policy(page, 1)->flags); }
 
 #define __CLEARPAGEFLAG(uname, lname, policy)				\
+static __always_inline							\
+void __folio_clear_##lname##_flag(struct folio *folio)			\
+{ __clear_bit(PG_##lname, folio_flags(folio, FOLIO_##policy)); }	\
 static __always_inline void __ClearPage##uname(struct page *page)	\
-	{ __clear_bit(PG_##lname, &policy(page, 1)->flags); }
+{ __clear_bit(PG_##lname, &policy(page, 1)->flags); }
 
 #define TESTSETFLAG(uname, lname, policy)				\
+static __always_inline							\
+bool folio_test_set_##lname##_flag(struct folio *folio)			\
+{ return test_and_set_bit(PG_##lname, folio_flags(folio, FOLIO_##policy)); } \
 static __always_inline int TestSetPage##uname(struct page *page)	\
-	{ return test_and_set_bit(PG_##lname, &policy(page, 1)->flags); }
+{ return test_and_set_bit(PG_##lname, &policy(page, 1)->flags); }
 
 #define TESTCLEARFLAG(uname, lname, policy)				\
+static __always_inline							\
+bool folio_test_clear_##lname##_flag(struct folio *folio)		\
+{ return test_and_clear_bit(PG_##lname, folio_flags(folio, FOLIO_##policy)); } \
 static __always_inline int TestClearPage##uname(struct page *page)	\
-	{ return test_and_clear_bit(PG_##lname, &policy(page, 1)->flags); }
+{ return test_and_clear_bit(PG_##lname, &policy(page, 1)->flags); }
 
 #define PAGEFLAG(uname, lname, policy)					\
 	TESTPAGEFLAG(uname, lname, policy)				\
@@ -332,29 +371,37 @@  static __always_inline int TestClearPage##uname(struct page *page)	\
 	TESTSETFLAG(uname, lname, policy)				\
 	TESTCLEARFLAG(uname, lname, policy)
 
-#define TESTPAGEFLAG_FALSE(uname)					\
+#define TESTPAGEFLAG_FALSE(uname, lname)				\
+static inline bool folio_##lname(const struct folio *folio) { return 0; } \
 static inline int Page##uname(const struct page *page) { return 0; }
 
-#define SETPAGEFLAG_NOOP(uname)						\
+#define SETPAGEFLAG_NOOP(uname, lname)					\
+static inline void folio_set_##lname##_flag(struct folio *folio) { }	\
 static inline void SetPage##uname(struct page *page) {  }
 
-#define CLEARPAGEFLAG_NOOP(uname)					\
+#define CLEARPAGEFLAG_NOOP(uname, lname)				\
+static inline void folio_clear_##lname##_flag(struct folio *folio) { }	\
 static inline void ClearPage##uname(struct page *page) {  }
 
-#define __CLEARPAGEFLAG_NOOP(uname)					\
+#define __CLEARPAGEFLAG_NOOP(uname, lname)				\
+static inline void __folio_clear_##lname_flags(struct folio *folio) { }	\
 static inline void __ClearPage##uname(struct page *page) {  }
 
-#define TESTSETFLAG_FALSE(uname)					\
+#define TESTSETFLAG_FALSE(uname, lname)					\
+static inline bool folio_test_set_##lname##_flag(struct folio *folio)	\
+{ return 0; }								\
 static inline int TestSetPage##uname(struct page *page) { return 0; }
 
-#define TESTCLEARFLAG_FALSE(uname)					\
+#define TESTCLEARFLAG_FALSE(uname, lname)				\
+static inline bool folio_test_clear_##lname##_flag(struct folio *folio) \
+{ return 0; }								\
 static inline int TestClearPage##uname(struct page *page) { return 0; }
 
-#define PAGEFLAG_FALSE(uname) TESTPAGEFLAG_FALSE(uname)			\
-	SETPAGEFLAG_NOOP(uname) CLEARPAGEFLAG_NOOP(uname)
+#define PAGEFLAG_FALSE(uname, lname) TESTPAGEFLAG_FALSE(uname, lname)	\
+	SETPAGEFLAG_NOOP(uname, lname) CLEARPAGEFLAG_NOOP(uname, lname)
 
-#define TESTSCFLAG_FALSE(uname)						\
-	TESTSETFLAG_FALSE(uname) TESTCLEARFLAG_FALSE(uname)
+#define TESTSCFLAG_FALSE(uname, lname)					\
+	TESTSETFLAG_FALSE(uname, lname) TESTCLEARFLAG_FALSE(uname, lname)
 
 __PAGEFLAG(Locked, locked, PF_NO_TAIL)
 PAGEFLAG(Waiters, waiters, PF_ONLY_HEAD) __CLEARPAGEFLAG(Waiters, waiters, PF_ONLY_HEAD)
@@ -410,8 +457,8 @@  PAGEFLAG(MappedToDisk, mappedtodisk, PF_NO_TAIL)
 /* PG_readahead is only used for reads; PG_reclaim is only for writes */
 PAGEFLAG(Reclaim, reclaim, PF_NO_TAIL)
 	TESTCLEARFLAG(Reclaim, reclaim, PF_NO_TAIL)
-PAGEFLAG(Readahead, reclaim, PF_NO_COMPOUND)
-	TESTCLEARFLAG(Readahead, reclaim, PF_NO_COMPOUND)
+PAGEFLAG(Readahead, readahead, PF_NO_COMPOUND)
+	TESTCLEARFLAG(Readahead, readahead, PF_NO_COMPOUND)
 
 #ifdef CONFIG_HIGHMEM
 /*
@@ -420,22 +467,25 @@  PAGEFLAG(Readahead, reclaim, PF_NO_COMPOUND)
  */
 #define PageHighMem(__p) is_highmem_idx(page_zonenum(__p))
 #else
-PAGEFLAG_FALSE(HighMem)
+PAGEFLAG_FALSE(HighMem, highmem)
 #endif
 
 #ifdef CONFIG_SWAP
-static __always_inline int PageSwapCache(struct page *page)
+static __always_inline bool folio_swapcache(struct folio *folio)
 {
-#ifdef CONFIG_THP_SWAP
-	page = compound_head(page);
-#endif
-	return PageSwapBacked(page) && test_bit(PG_swapcache, &page->flags);
+	return folio_swapbacked(folio) &&
+			test_bit(PG_swapcache, folio_flags(folio, 0));
+}
 
+static __always_inline bool PageSwapCache(struct page *page)
+{
+	return folio_swapcache(page_folio(page));
 }
+
 SETPAGEFLAG(SwapCache, swapcache, PF_NO_TAIL)
 CLEARPAGEFLAG(SwapCache, swapcache, PF_NO_TAIL)
 #else
-PAGEFLAG_FALSE(SwapCache)
+PAGEFLAG_FALSE(SwapCache, swapcache)
 #endif
 
 PAGEFLAG(Unevictable, unevictable, PF_HEAD)
@@ -447,14 +497,14 @@  PAGEFLAG(Mlocked, mlocked, PF_NO_TAIL)
 	__CLEARPAGEFLAG(Mlocked, mlocked, PF_NO_TAIL)
 	TESTSCFLAG(Mlocked, mlocked, PF_NO_TAIL)
 #else
-PAGEFLAG_FALSE(Mlocked) __CLEARPAGEFLAG_NOOP(Mlocked)
-	TESTSCFLAG_FALSE(Mlocked)
+PAGEFLAG_FALSE(Mlocked, mlocked) __CLEARPAGEFLAG_NOOP(Mlocked, mlocked)
+	TESTSCFLAG_FALSE(Mlocked, mlocked)
 #endif
 
 #ifdef CONFIG_ARCH_USES_PG_UNCACHED
 PAGEFLAG(Uncached, uncached, PF_NO_COMPOUND)
 #else
-PAGEFLAG_FALSE(Uncached)
+PAGEFLAG_FALSE(Uncached, uncached)
 #endif
 
 #ifdef CONFIG_MEMORY_FAILURE
@@ -463,7 +513,7 @@  TESTSCFLAG(HWPoison, hwpoison, PF_ANY)
 #define __PG_HWPOISON (1UL << PG_hwpoison)
 extern bool take_page_off_buddy(struct page *page);
 #else
-PAGEFLAG_FALSE(HWPoison)
+PAGEFLAG_FALSE(HWPoison, hwpoison)
 #define __PG_HWPOISON 0
 #endif
 
@@ -477,7 +527,7 @@  PAGEFLAG(Idle, idle, PF_ANY)
 #ifdef CONFIG_KASAN_HW_TAGS
 PAGEFLAG(SkipKASanPoison, skip_kasan_poison, PF_HEAD)
 #else
-PAGEFLAG_FALSE(SkipKASanPoison)
+PAGEFLAG_FALSE(SkipKASanPoison, skip_kasan_poison)
 #endif
 
 /*
@@ -515,10 +565,14 @@  static __always_inline int PageMappingFlags(struct page *page)
 	return ((unsigned long)page->mapping & PAGE_MAPPING_FLAGS) != 0;
 }
 
-static __always_inline int PageAnon(struct page *page)
+static __always_inline bool folio_anon(struct folio *folio)
+{
+	return ((unsigned long)folio->mapping & PAGE_MAPPING_ANON) != 0;
+}
+
+static __always_inline bool PageAnon(struct page *page)
 {
-	page = compound_head(page);
-	return ((unsigned long)page->mapping & PAGE_MAPPING_ANON) != 0;
+	return folio_anon(page_folio(page));
 }
 
 static __always_inline int __PageMovable(struct page *page)
@@ -534,30 +588,32 @@  static __always_inline int __PageMovable(struct page *page)
  * is found in VM_MERGEABLE vmas.  It's a PageAnon page, pointing not to any
  * anon_vma, but to that page's node of the stable tree.
  */
-static __always_inline int PageKsm(struct page *page)
+static __always_inline bool folio_ksm(struct folio *folio)
 {
-	page = compound_head(page);
-	return ((unsigned long)page->mapping & PAGE_MAPPING_FLAGS) ==
+	return ((unsigned long)folio->mapping & PAGE_MAPPING_FLAGS) ==
 				PAGE_MAPPING_KSM;
 }
+
+static __always_inline bool PageKsm(struct page *page)
+{
+	return folio_ksm(page_folio(page));
+}
 #else
-TESTPAGEFLAG_FALSE(Ksm)
+TESTPAGEFLAG_FALSE(Ksm, ksm)
 #endif
 
 u64 stable_page_flags(struct page *page);
 
-static inline int PageUptodate(struct page *page)
+static inline bool folio_uptodate(struct folio *folio)
 {
-	int ret;
-	page = compound_head(page);
-	ret = test_bit(PG_uptodate, &(page)->flags);
+	bool ret = test_bit(PG_uptodate, folio_flags(folio, 0));
 	/*
-	 * Must ensure that the data we read out of the page is loaded
-	 * _after_ we've loaded page->flags to check for PageUptodate.
-	 * We can skip the barrier if the page is not uptodate, because
+	 * Must ensure that the data we read out of the folio is loaded
+	 * _after_ we've loaded folio->flags to check the uptodate bit.
+	 * We can skip the barrier if the folio is not uptodate, because
 	 * we wouldn't be reading anything from it.
 	 *
-	 * See SetPageUptodate() for the other side of the story.
+	 * See folio_mark_uptodate() for the other side of the story.
 	 */
 	if (ret)
 		smp_rmb();
@@ -565,23 +621,36 @@  static inline int PageUptodate(struct page *page)
 	return ret;
 }
 
-static __always_inline void __SetPageUptodate(struct page *page)
+static inline int PageUptodate(struct page *page)
+{
+	return folio_uptodate(page_folio(page));
+}
+
+static __always_inline void __folio_mark_uptodate(struct folio *folio)
 {
-	VM_BUG_ON_PAGE(PageTail(page), page);
 	smp_wmb();
-	__set_bit(PG_uptodate, &page->flags);
+	__set_bit(PG_uptodate, folio_flags(folio, 0));
 }
 
-static __always_inline void SetPageUptodate(struct page *page)
+static __always_inline void folio_mark_uptodate(struct folio *folio)
 {
-	VM_BUG_ON_PAGE(PageTail(page), page);
 	/*
 	 * Memory barrier must be issued before setting the PG_uptodate bit,
-	 * so that all previous stores issued in order to bring the page
-	 * uptodate are actually visible before PageUptodate becomes true.
+	 * so that all previous stores issued in order to bring the folio
+	 * uptodate are actually visible before folio_uptodate becomes true.
 	 */
 	smp_wmb();
-	set_bit(PG_uptodate, &page->flags);
+	set_bit(PG_uptodate, folio_flags(folio, 0));
+}
+
+static __always_inline void __SetPageUptodate(struct page *page)
+{
+	__folio_mark_uptodate((struct folio *)page);
+}
+
+static __always_inline void SetPageUptodate(struct page *page)
+{
+	folio_mark_uptodate((struct folio *)page);
 }
 
 CLEARPAGEFLAG(Uptodate, uptodate, PF_NO_TAIL)
@@ -606,6 +675,17 @@  static inline void set_page_writeback_keepwrite(struct page *page)
 
 __PAGEFLAG(Head, head, PF_ANY) CLEARPAGEFLAG(Head, head, PF_ANY)
 
+/* Whether there are one or multiple pages in a folio */
+static inline bool folio_single(struct folio *folio)
+{
+	return !folio_head(folio);
+}
+
+static inline bool folio_multi(struct folio *folio)
+{
+	return folio_head(folio);
+}
+
 static __always_inline void set_compound_head(struct page *page, struct page *head)
 {
 	WRITE_ONCE(page->compound_head, (unsigned long)head + 1);
@@ -629,12 +709,15 @@  static inline void ClearPageCompound(struct page *page)
 #ifdef CONFIG_HUGETLB_PAGE
 int PageHuge(struct page *page);
 int PageHeadHuge(struct page *page);
+static inline bool folio_hugetlb(struct folio *folio)
+{
+	return PageHeadHuge(&folio->page);
+}
 #else
-TESTPAGEFLAG_FALSE(Huge)
-TESTPAGEFLAG_FALSE(HeadHuge)
+TESTPAGEFLAG_FALSE(Huge, hugetlb)
+TESTPAGEFLAG_FALSE(HeadHuge, headhuge)
 #endif
 
-
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
 /*
  * PageHuge() only returns true for hugetlbfs pages, but not for
@@ -650,6 +733,11 @@  static inline int PageTransHuge(struct page *page)
 	return PageHead(page);
 }
 
+static inline bool folio_transhuge(struct folio *folio)
+{
+	return folio_head(folio);
+}
+
 /*
  * PageTransCompound returns true for both transparent huge pages
  * and hugetlbfs pages, so it should only be called when it's known
@@ -723,12 +811,12 @@  static inline int PageTransTail(struct page *page)
 PAGEFLAG(DoubleMap, double_map, PF_SECOND)
 	TESTSCFLAG(DoubleMap, double_map, PF_SECOND)
 #else
-TESTPAGEFLAG_FALSE(TransHuge)
-TESTPAGEFLAG_FALSE(TransCompound)
-TESTPAGEFLAG_FALSE(TransCompoundMap)
-TESTPAGEFLAG_FALSE(TransTail)
-PAGEFLAG_FALSE(DoubleMap)
-	TESTSCFLAG_FALSE(DoubleMap)
+TESTPAGEFLAG_FALSE(TransHuge, transhuge)
+TESTPAGEFLAG_FALSE(TransCompound, transcompound)
+TESTPAGEFLAG_FALSE(TransCompoundMap, transcompoundmap)
+TESTPAGEFLAG_FALSE(TransTail, transtail)
+PAGEFLAG_FALSE(DoubleMap, double_map)
+	TESTSCFLAG_FALSE(DoubleMap, double_map)
 #endif
 
 /*
@@ -903,6 +991,11 @@  static inline int page_has_private(struct page *page)
 	return !!(page->flags & PAGE_FLAGS_PRIVATE);
 }
 
+static inline bool folio_has_private(struct folio *folio)
+{
+	return page_has_private(&folio->page);
+}
+
 #undef PF_ANY
 #undef PF_HEAD
 #undef PF_ONLY_HEAD