On Tue, 24 Mar 2026 00:38:15 +0000 John Groves <[email protected]> wrote:
> From: John Groves <[email protected]> > > Both fs/dax.c:dax_folio_put() and drivers/dax/fsdev.c: > fsdev_clear_folio_state() (the latter coming in the next commit after this > one) contain nearly identical code to reset a compound DAX folio back to > order-0 pages. Factor this out into a shared helper function. > > The new dax_folio_reset_order() function: > - Clears the folio's mapping and share count > - Resets compound folio state via folio_reset_order() > - Clears PageHead and compound_head for each sub-page > - Restores the pgmap pointer for each resulting order-0 folio > - Returns the original folio order (for callers that need to advance by > that many pages) > > Two intentional differences from the original dax_folio_put() logic: > > 1. folio->share is cleared unconditionally. This is correct because the DAX > subsystem maintains the invariant that share != 0 only when mapping == NULL > (enforced by dax_folio_make_shared()). dax_folio_put() ensures share has > reached zero before calling this helper, so the unconditional clear is > safe. > > 2. folio->pgmap is now explicitly restored for order-0 folios. For the > dax_folio_put() caller this is a no-op (reads and writes back the same > field). It is intentional for the upcoming fsdev_clear_folio_state() > caller, which converts previously-compound folios and needs pgmap > re-established for all pages regardless of order. > > This simplifies fsdev_clear_folio_state() from ~50 lines to ~15 lines. > > Suggested-by: Jonathan Cameron <[email protected]> A couple of trivial "if you are respinning" line length of comments comments inline. Subject to DAX folk sanity checking the new comments match their expectations. Reviewed-by: Jonathan Cameron <[email protected]> > Reviewed-by: Ira Weiny <[email protected]> > Reviewed-by: Dave Jiang <[email protected]> > Signed-off-by: John Groves <[email protected]> > --- > fs/dax.c | 74 ++++++++++++++++++++++++++++++++++----------- > include/linux/dax.h | 1 + > 2 files changed, 57 insertions(+), 18 deletions(-) > > diff --git a/fs/dax.c b/fs/dax.c > index 289e6254aa30..eba86802a7a7 100644 > --- a/fs/dax.c > +++ b/fs/dax.c > @@ -378,6 +378,59 @@ static void dax_folio_make_shared(struct folio *folio) > folio->share = 1; > } > > +/** > + * dax_folio_reset_order - Reset a compound DAX folio to order-0 pages > + * @folio: The folio to reset > + * > + * Splits a compound folio back into individual order-0 pages, > + * clearing compound state and restoring pgmap pointers. > + * > + * Returns: the original folio order (0 if already order-0) > + */ > +int dax_folio_reset_order(struct folio *folio) > +{ > + struct dev_pagemap *pgmap = page_pgmap(&folio->page); > + int order = folio_order(folio); > + > + /* > + * DAX maintains the invariant that folio->share != 0 only when > + * folio->mapping == NULL (enforced by dax_folio_make_shared()). > + * Equivalently: folio->mapping != NULL implies folio->share == 0. > + * Callers ensure share has been decremented to zero before > + * calling here, so unconditionally clearing both fields is > + * correct. If you happen to spin again, wrap is a bit short of standard 80 chars. * DAX maintains the invariant that folio->share != 0 only when * folio->mapping == NULL (enforced by dax_folio_make_shared()). * Equivalently: folio->mapping != NULL implies folio->share == 0. * Callers ensure share has been decremented to zero before calling here, * so unconditionally clearing both fields is correct. > + */ > + folio->mapping = NULL; > + folio->share = 0; > + > + if (!order) { > + /* > + * Restore pgmap explicitly even for order-0 folios. For > + * the dax_folio_put() caller this is a no-op (same value), > + * but fsdev_clear_folio_state() may call this on folios > + * that were previously compound and need pgmap > + * re-established. > + */ * Restore pgmap explicitly even for order-0 folios. For the * dax_folio_put() caller this is a no-op (same value), but * fsdev_clear_folio_state() may call this on folios that were * previously compound and need pgmap re-established. */ > + folio->pgmap = pgmap; > + return 0; > + } > + > + folio_reset_order(folio); > + > + for (int i = 0; i < (1UL << order); i++) { > + struct page *page = folio_page(folio, i); > + struct folio *f = (struct folio *)page; > + > + ClearPageHead(page); > + clear_compound_head(page); > + f->mapping = NULL; > + f->share = 0; > + f->pgmap = pgmap; > + } > + > + return order; > +}

