Commit 59eb73b9 authored by John Groves's avatar John Groves Committed by Ira Weiny
Browse files

dax: Factor out dax_folio_reset_order() helper



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: default avatarJonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: default avatarIra Weiny <ira.weiny@intel.com>
Reviewed-by: default avatarDave Jiang <dave.jiang@intel.com>
Reviewed-by: default avatarJonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: default avatarJohn Groves <john@groves.net>
Link: https://patch.msgid.link/0100019d311cc6b9-5be7428a-7f16-4774-8f90-a44b88ac5660-000000@email.amazonses.com


Signed-off-by: default avatarIra Weiny <ira.weiny@intel.com>
parent a73cc506
Loading
Loading
Loading
Loading
+55 −18
Original line number Diff line number Diff line
@@ -378,6 +378,58 @@ 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 +443,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;
+1 −0
Original line number Diff line number Diff line
@@ -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);