From: Mike Snitzer <snitzer@kernel.org>
To: Matthew Wilcox <willy@infradead.org>, Jan Kara <jack@suse.cz>,
linux-mm@kvack.org, Christian Brauner <brauner@kernel.org>,
linux-fsdevel@vger.kernel.org
Cc: Mike Snitzer <snitzer@kernel.org>, Chuck Lever <cel@kernel.org>,
Jeff Layton <jlayton@kernel.org>,
linux-nfs@vger.kernel.org
Subject: [PATCH v12 4/7] mm: add folio_mark_dropbehind() to mark a folio dropbehind and account it
Date: Thu, 12 Nov 2026 14:09:07 -0500 [thread overview]
Message-ID: <20261112190911.84702-5-snitzer@kernel.org> (raw)
In-Reply-To: <20261112190911.84702-1-snitzer@kernel.org>
WB_DONTCACHE_DIRTY counts the dirty folios that are marked dropbehind.
When a dropbehind folio is dirtied, folio_account_dirtied() adds it to
the counter. When the folio is cleaned, folio_clear_dirty_for_io() or
folio_account_cleaned() subtracts it again. This balances only if the
folio is marked before it is dirtied, as it is for the folios that an
RWF_DONTCACHE write creates.
To prepare for NFSD to mark a folio dropbehind once both writes that
share it have written their parts, handle a folio that is already dirty
when it is marked. folio_set_dropbehind() only sets the flag. With it,
the clean would subtract the folio from WB_DONTCACHE_DIRTY although it
was never added, and the counter would go negative.
Add folio_mark_dropbehind(), the inverse of filemap_clear_dropbehind().
With the folio locked, it sets PG_dropbehind. Under the i_pages lock,
it adds the folio to WB_DONTCACHE_DIRTY if both PG_dirty and the dirty
tag are set. A dirtier that has set PG_dirty but not yet counted the
folio finds the flag set and counts the folio itself.
Leave a mapped folio unmarked. Through its page table, a mapped folio
can be dirtied without the folio lock: zap_present_folio_ptes() sets
PG_dirty before the folio is counted. __folio_cancel_dirty() can leave
a stale dirty tag on a folio. Then PG_dirty and the tag are both set in
that window, and the folio would be counted twice. The folio lock keeps
an unmapped folio unmapped. The dirtiers of an unmapped folio hold the
folio lock, except mark_buffer_dirty() on a file system with buffer
heads. filemap_end_dropbehind() does not drop a mapped folio either, so
nothing is lost.
The name follows folio_set_dirty() and folio_mark_dirty(): the set
variant only sets the flag, the mark variant also accounts for it.
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
---
include/linux/pagemap.h | 1 +
mm/filemap.c | 42 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 43 insertions(+)
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 73af18a373670..3f5295d6fd73b 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -1306,6 +1306,7 @@ void end_page_writeback(struct page *page);
void folio_end_writeback(struct folio *folio);
void folio_end_writeback_no_dropbehind(struct folio *folio);
void folio_end_dropbehind(struct folio *folio);
+void folio_mark_dropbehind(struct folio *folio);
void folio_wait_stable(struct folio *folio);
void __folio_mark_dirty(struct folio *folio, struct address_space *, int warn);
void folio_account_cleaned(struct folio *folio, struct bdi_writeback *wb);
diff --git a/mm/filemap.c b/mm/filemap.c
index 98ce955404127..196c385f29801 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -1654,6 +1654,48 @@ void folio_end_dropbehind(struct folio *folio)
}
EXPORT_SYMBOL_GPL(folio_end_dropbehind);
+/**
+ * folio_mark_dropbehind - mark an existing folio dropbehind
+ * @folio: the folio, locked by the caller
+ *
+ * Mark @folio so that the writeback that cleans it drops it. If the folio
+ * is already counted as dirty, which with the i_pages lock held means both
+ * PG_dirty and the dirty tag are set, add it to WB_DONTCACHE_DIRTY so that
+ * the clean that subtracts it leaves the counter balanced. A mapped folio
+ * is left unmarked.
+ */
+void folio_mark_dropbehind(struct folio *folio)
+{
+ struct address_space *mapping = folio->mapping;
+ unsigned long flags;
+
+ VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio);
+
+ if (folio_test_dropbehind(folio))
+ return;
+ /*
+ * Through its page table, a mapped folio can be dirtied without the
+ * folio lock, and PG_dirty set before the folio is counted: with a
+ * stale dirty tag it would be counted twice. The folio lock keeps
+ * an unmapped folio unmapped. filemap_end_dropbehind() does not drop
+ * a mapped folio either.
+ */
+ if (folio_mapped(folio))
+ return;
+ if (!mapping || !mapping_can_writeback(mapping)) {
+ folio_set_dropbehind(folio);
+ return;
+ }
+ xa_lock_irqsave(&mapping->i_pages, flags);
+ folio_set_dropbehind(folio);
+ if (folio_test_dirty(folio) &&
+ xa_get_mark(&mapping->i_pages, folio->index, PAGECACHE_TAG_DIRTY))
+ wb_stat_mod(inode_to_wb(mapping->host), WB_DONTCACHE_DIRTY,
+ folio_nr_pages(folio));
+ xa_unlock_irqrestore(&mapping->i_pages, flags);
+}
+EXPORT_SYMBOL_GPL(folio_mark_dropbehind);
+
/**
* folio_end_writeback_no_dropbehind - End writeback against a folio.
* @folio: The folio.
--
2.52.0
next prev parent reply other threads:[~2026-11-12 19:09 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-12 19:09 [PATCH v12 0/7] mm, iomap, NFSD: stop DONTCACHE writes reading back shared pages Mike Snitzer
2026-11-12 19:09 ` [PATCH v12 1/7] mm: move clearing the dropbehind flag of a found folio into a helper Mike Snitzer
2026-11-12 19:09 ` [PATCH v12 2/7] mm: take a folio off WB_DONTCACHE_DIRTY only if it was counted there Mike Snitzer
2026-11-12 19:09 ` [PATCH v12 3/7] mm: expose WB_DONTCACHE_DIRTY in bdi debugfs stats Mike Snitzer
2026-11-12 19:09 ` Mike Snitzer [this message]
2026-11-12 19:09 ` [PATCH v12 5/7] mm, iomap: keep a partially covered page until a DONTCACHE write completes it Mike Snitzer
2026-11-12 19:09 ` [PATCH v12 6/7] mm, iomap: let a DONTCACHE writer complete its shared pages itself Mike Snitzer
2026-11-12 19:09 ` [PATCH v12 7/7] NFSD: drop a shared boundary page only once both WRITEs have written it Mike Snitzer
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=20261112190911.84702-5-snitzer@kernel.org \
--to=snitzer@kernel.org \
--cc=brauner@kernel.org \
--cc=cel@kernel.org \
--cc=jack@suse.cz \
--cc=jlayton@kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=linux-nfs@vger.kernel.org \
--cc=willy@infradead.org \
/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