Add a per-node page counter that tracks the number of dirty pages with the dropbehind flag set (i.e., pages dirtied via RWF_DONTCACHE writes). Increment the counter alongside NR_FILE_DIRTY in folio_account_dirtied() when the folio has the dropbehind flag set, and decrement it in folio_clear_dirty_for_io(), folio_account_cleaned(), and when a non-DONTCACHE access clears the dropbehind flag on a dirty folio. The counter is visible via /proc/vmstat as "nr_dontcache_dirty" and will be used by the writeback flusher to determine how many pages to write back when expediting writeback for IOCB_DONTCACHE writes, without flushing the entire BDI's dirty pages. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Jeff Layton --- include/linux/mmzone.h | 1 + mm/filemap.c | 6 +++++- mm/page-writeback.c | 7 +++++++ mm/vmstat.c | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 9adb2ad21da5..ed9cc61c7627 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -259,6 +259,7 @@ enum node_stat_item { only modified from process context */ NR_FILE_PAGES, NR_FILE_DIRTY, + NR_DONTCACHE_DIRTY, NR_WRITEBACK, NR_SHMEM, /* shmem pages (included tmpfs/GEM pages) */ NR_SHMEM_THPS, diff --git a/mm/filemap.c b/mm/filemap.c index 4e636647100c..45089fde5150 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2052,8 +2052,12 @@ struct folio *__filemap_get_folio_mpol(struct address_space *mapping, if (!folio) return ERR_PTR(-ENOENT); /* not an uncached lookup, clear uncached if set */ - if (folio_test_dropbehind(folio) && !(fgp_flags & FGP_DONTCACHE)) + if (folio_test_dropbehind(folio) && !(fgp_flags & FGP_DONTCACHE)) { + if (folio_test_dirty(folio)) + lruvec_stat_mod_folio(folio, NR_DONTCACHE_DIRTY, + -folio_nr_pages(folio)); folio_clear_dropbehind(folio); + } return folio; } EXPORT_SYMBOL(__filemap_get_folio_mpol); diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 88cd53d4ba09..e1df93fb3e3b 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -2630,6 +2630,8 @@ static void folio_account_dirtied(struct folio *folio, wb = inode_to_wb(inode); lruvec_stat_mod_folio(folio, NR_FILE_DIRTY, nr); + if (folio_test_dropbehind(folio)) + lruvec_stat_mod_folio(folio, NR_DONTCACHE_DIRTY, nr); __zone_stat_mod_folio(folio, NR_ZONE_WRITE_PENDING, nr); __node_stat_mod_folio(folio, NR_DIRTIED, nr); wb_stat_mod(wb, WB_RECLAIMABLE, nr); @@ -2651,6 +2653,8 @@ void folio_account_cleaned(struct folio *folio, struct bdi_writeback *wb) long nr = folio_nr_pages(folio); lruvec_stat_mod_folio(folio, NR_FILE_DIRTY, -nr); + if (folio_test_dropbehind(folio)) + lruvec_stat_mod_folio(folio, NR_DONTCACHE_DIRTY, -nr); zone_stat_mod_folio(folio, NR_ZONE_WRITE_PENDING, -nr); wb_stat_mod(wb, WB_RECLAIMABLE, -nr); task_io_account_cancelled_write(nr * PAGE_SIZE); @@ -2920,6 +2924,9 @@ bool folio_clear_dirty_for_io(struct folio *folio) if (folio_test_clear_dirty(folio)) { long nr = folio_nr_pages(folio); lruvec_stat_mod_folio(folio, NR_FILE_DIRTY, -nr); + if (folio_test_dropbehind(folio)) + lruvec_stat_mod_folio(folio, + NR_DONTCACHE_DIRTY, -nr); zone_stat_mod_folio(folio, NR_ZONE_WRITE_PENDING, -nr); wb_stat_mod(wb, WB_RECLAIMABLE, -nr); ret = true; diff --git a/mm/vmstat.c b/mm/vmstat.c index f534972f517d..c3e5dfadb9a5 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -1240,6 +1240,7 @@ const char * const vmstat_text[] = { [I(NR_FILE_MAPPED)] = "nr_mapped", [I(NR_FILE_PAGES)] = "nr_file_pages", [I(NR_FILE_DIRTY)] = "nr_dirty", + [I(NR_DONTCACHE_DIRTY)] = "nr_dontcache_dirty", [I(NR_WRITEBACK)] = "nr_writeback", [I(NR_SHMEM)] = "nr_shmem", [I(NR_SHMEM_THPS)] = "nr_shmem_hugepages", -- 2.53.0