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]>
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.
+        */
+       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.
+                */
+               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;
+}
+
 static inline unsigned long dax_folio_put(struct folio *folio)
 {
        unsigned long ref;
@@ -391,28 +444,13 @@ static inline unsigned long dax_folio_put(struct folio 
*folio)
        if (ref)
                return ref;
 
-       folio->mapping = NULL;
-       order = folio_order(folio);
-       if (!order)
-               return 0;
-       folio_reset_order(folio);
+       order = dax_folio_reset_order(folio);
 
+       /* Debug check: verify refcounts are zero for all sub-folios */
        for (i = 0; i < (1UL << order); i++) {
-               struct dev_pagemap *pgmap = page_pgmap(&folio->page);
                struct page *page = folio_page(folio, i);
-               struct folio *new_folio = (struct folio *)page;
-
-               ClearPageHead(page);
-               clear_compound_head(page);
 
-               new_folio->mapping = NULL;
-               /*
-                * Reset pgmap which was over-written by
-                * prep_compound_page().
-                */
-               new_folio->pgmap = pgmap;
-               new_folio->share = 0;
-               WARN_ON_ONCE(folio_ref_count(new_folio));
+               WARN_ON_ONCE(folio_ref_count((struct folio *)page));
        }
 
        return ref;
diff --git a/include/linux/dax.h b/include/linux/dax.h
index bf103f317cac..73cfc1a7c8f1 100644
--- a/include/linux/dax.h
+++ b/include/linux/dax.h
@@ -153,6 +153,7 @@ static inline void fs_put_dax(struct dax_device *dax_dev, 
void *holder)
 #if IS_ENABLED(CONFIG_FS_DAX)
 int dax_writeback_mapping_range(struct address_space *mapping,
                struct dax_device *dax_dev, struct writeback_control *wbc);
+int dax_folio_reset_order(struct folio *folio);
 
 struct page *dax_layout_busy_page(struct address_space *mapping);
 struct page *dax_layout_busy_page_range(struct address_space *mapping, loff_t 
start, loff_t end);
-- 
2.53.0



Reply via email to