Skip to contentSkip to conversation

introduce kernel file mapped folios

PATCH In review Boris Burkov 4 participants 9 messages Aug 21, 2025 – Aug 29, 2025

Subsystems (7)
lore
Share & tools
lore
#0 lore raw

v4 cover introduce kernel file mapped folios

I would like to revisit Qu's proposal to not charge btrfs extent_buffer allocations to the user's cgroup.

https://lore.kernel.org/linux-mm/b5fef5372ae454a7b6da4f2f75c427aeab6a07d6.1727498749.git.wqu@suse.com/

I believe it is detrimental to account these global pages to the cgroup using them, basically at random. A bit more justification and explanation in the patches themselves.

---
Changelog:
v4:
- change the concept from "uncharged" to "kernel_file"
- no longer violates the invariant that each mapped folio has a memcg when CONFIG_MEMCG=y
- no longer really tied to memcg conceptually, so simplify build/helpers v3:
- use mod_node_page_state since we will never count cgroup stats
- include Shakeel's patch that removes a WARNING triggered by this series v2:
- switch from filemap_add_folio_nocharge() to AS_UNCHARGED on the address_space.
- fix an interrupt safety bug in the vmstat patch.
- fix some foolish build errors for CONFIG_MEMCG=n

Boris Burkov (3):
mm/filemap: add AS_KERNEL_FILE
mm: add vmstat for kernel_file pages
btrfs: set AS_KERNEL_FILE on the btree_inode

 fs/btrfs/disk-io.c      |  1 +
 include/linux/mmzone.h  |  1 +
 include/linux/pagemap.h |  2 ++
 mm/filemap.c            | 13 +++++++++++++
 mm/vmstat.c             |  1 +
 5 files changed, 18 insertions(+)
Signature · 1 line
2.50.1
#1 lore raw

PATCH v4 1/3 mm/filemap: add AS_KERNEL_FILE

Btrfs currently tracks its metadata pages in the page cache, using a fake inode (fs_info->btree_inode) with offsets corresponding to where the metadata is stored in the filesystem's full logical address space.

A consequence of this is that when btrfs uses filemap_add_folio(), this usage is charged to the cgroup of whichever task happens to be running at the time. These folios don't belong to any particular user cgroup, so I don't think it makes much sense for them to be charged in that way. Some negative consequences as a result:
- A task can be holding some important btrfs locks, then need to lookup some metadata and go into reclaim, extending the duration it holds that lock for, and unfairly pushing its own reclaim pain onto other cgroups.
- If that cgroup goes into reclaim, it might reclaim these folios a different non-reclaiming cgroup might need soon. This is naturally offset by LRU reclaim, but still.

We have two options for how to manage such file pages:
1. charge them to the root cgroup.
2. don't charge them to any cgroup at all.

2. breaks the invariant that every mapped page has a cgroup. This is workable, but unnecessarily risky. Therefore, go with 1.

A very similar proposal to use the root cgroup was previously made by Qu, where he eventually proposed the idea of setting it per address_space. This makes good sense for the btrfs use case, as the behavior should apply to all use of the address_space, not select allocations. I.e., if someone adds another filemap_add_folio() call using btrfs's btree_inode, we would almost certainly want to account that to the root cgroup as well.

---
 include/linux/pagemap.h | 2 ++
 mm/filemap.c            | 6 ++++++
 2 files changed, 8 insertions(+)
include/linux/pagemap.h +2 -0
index c9ba69e02e3e..a3e16d74792f 100644
@@ -211,6 +211,8 @@ enum mapping_flags {
 				   folio contents */
 	AS_INACCESSIBLE = 8,	/* Do not attempt direct R/W access to the mapping */
 	AS_WRITEBACK_MAY_DEADLOCK_ON_RECLAIM = 9,
+	AS_KERNEL_FILE = 10,	/* mapping for a fake kernel file that shouldn't
+				   account usage to user cgroups */
 	/* Bits 16-25 are used for FOLIO_ORDER */
 	AS_FOLIO_ORDER_BITS = 5,
 	AS_FOLIO_ORDER_MIN = 16,
mm/filemap.c +6 -0
index e4a5a46db89b..05c1384bd611 100644
@@ -960,8 +960,14 @@ int filemap_add_folio(struct address_space *mapping, struct folio *folio,
 {
 	void *shadow = NULL;
 	int ret;
+	struct mem_cgroup *tmp;
+	bool kernel_file = test_bit(AS_KERNEL_FILE, &mapping->flags);
 
+	if (kernel_file)
+		tmp = set_active_memcg(root_mem_cgroup);
 	ret = mem_cgroup_charge(folio, NULL, gfp);
+	if (kernel_file)
+		set_active_memcg(tmp);
 	if (ret)
 		return ret;
 
Signature · 1 line
2.50.1
maintainer Acked-by on v4 1/3
#2 lore raw

Re: [PATCH v4 1/3] mm/filemap: add AS_KERNEL_FILE

Quoted 32 lines · Boris Burkov

Btrfs currently tracks its metadata pages in the page cache, using a fake inode (fs_info->btree_inode) with offsets corresponding to where the metadata is stored in the filesystem's full logical address space.

A consequence of this is that when btrfs uses filemap_add_folio(), this usage is charged to the cgroup of whichever task happens to be running at the time. These folios don't belong to any particular user cgroup, so I don't think it makes much sense for them to be charged in that way. Some negative consequences as a result:
- A task can be holding some important btrfs locks, then need to lookup some metadata and go into reclaim, extending the duration it holds that lock for, and unfairly pushing its own reclaim pain onto other cgroups.
- If that cgroup goes into reclaim, it might reclaim these folios a different non-reclaiming cgroup might need soon. This is naturally offset by LRU reclaim, but still.

We have two options for how to manage such file pages:
1. charge them to the root cgroup.
2. don't charge them to any cgroup at all.

2. breaks the invariant that every mapped page has a cgroup. This is workable, but unnecessarily risky. Therefore, go with 1.

A very similar proposal to use the root cgroup was previously made by Qu, where he eventually proposed the idea of setting it per address_space. This makes good sense for the btrfs use case, as the behavior should apply to all use of the address_space, not select allocations. I.e., if someone adds another filemap_add_folio() call using btrfs's btree_inode, we would almost certainly want to account that to the root cgroup as well.

Quoted 5 lines
#3 lore raw

PATCH 1/1 mm: fix CONFIG_MEMCG build for AS_KERNEL_FILE

In reply to Boris Burkov

this needs to be folded into

mm/filemap: add AS_KERNEL_FILE

for it to build with CONFIG_MEMCG unset. Apologies for the churn.

---
 include/linux/memcontrol.h | 2 ++
 1 file changed, 2 insertions(+)
include/linux/memcontrol.h +2 -0
index 9fa3afc90dd5..e693978b2022 100644
@@ -1059,6 +1059,8 @@ extern int mem_cgroup_init(void);
 
 #define MEM_CGROUP_ID_SHIFT	0
 
+#define root_mem_cgroup		(NULL)
+
 static inline struct mem_cgroup *folio_memcg(struct folio *folio)
 {
 	return NULL;
Signature · 1 line
2.50.1
#4 lore raw

Re: [PATCH v4 1/3] mm/filemap: add AS_KERNEL_FILE

In reply to Boris Burkov
kernel test robot noticed the following build errors:

Hi Boris,

kernel test robot noticed the following build errors:

[auto build test ERROR on akpm-mm/mm-everything]
[also build test ERROR on kdave/for-next linus/master v6.17-rc2 next-20250822] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Boris-Burkov/mm-filemap-add-AS_KERNEL_FILE/20250822-055741
base:   https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-everything
patch link:    https://lore.kernel.org/r/f09c4e2c90351d4cb30a1969f7a863b9238bd291.1755812945.git.boris%40bur.io
patch subject: [PATCH v4 1/3] mm/filemap: add AS_KERNEL_FILE
config: x86_64-buildonly-randconfig-006-20250822 (https://download.01.org/0day-ci/archive/20250822/[email protected]/config)
compiler: gcc-12 (Debian 12.2.0-14+deb12u1) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250822/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All errors (new ones prefixed by >>):

mm/filemap.c: In function 'filemap_add_folio':

>> mm/filemap.c:967:40: error: 'root_mem_cgroup' undeclared (first use in this function); did you mean 'parent_mem_cgroup'?
     967 |                 tmp = set_active_memcg(root_mem_cgroup);
         |                                        ^~~~~~~~~~~~~~~
         |                                        parent_mem_cgroup

mm/filemap.c:967:40: note: each undeclared identifier is reported only once for each function it appears in

vim +967 mm/filemap.c

957
958 int filemap_add_folio(struct address_space *mapping, struct folio *folio,
959 pgoff_t index, gfp_t gfp)
960 {
961 void *shadow = NULL;
962 int ret;
963 struct mem_cgroup *tmp;
964 bool kernel_file = test_bit(AS_KERNEL_FILE, &mapping->flags);
965
966 if (kernel_file)

 > 967			tmp = set_active_memcg(root_mem_cgroup);

968 ret = mem_cgroup_charge(folio, NULL, gfp);
969 if (kernel_file)
970 set_active_memcg(tmp);
971 if (ret)
972 return ret;
973
974 __folio_set_locked(folio);
975 ret = __filemap_add_folio(mapping, folio, index, gfp, &shadow);
976 if (unlikely(ret)) {
977 mem_cgroup_uncharge(folio);
978 __folio_clear_locked(folio);
979 } else {
980 /*
981 * The folio might have been evicted from cache only
982 * recently, in which case it should be activated like
983 * any other repeatedly accessed folio.
984 * The exception is folios getting rewritten; evicting other
985 * data from the working set, only to cache data that will
986 * get overwritten with something else, is a waste of memory.
987 */
988 WARN_ON_ONCE(folio_test_active(folio));
989 if (!(gfp & __GFP_WRITE) && shadow)
990 workingset_refault(folio, shadow);
991 folio_add_lru(folio);
992 }
993 return ret;
994 }
995 EXPORT_SYMBOL_GPL(filemap_add_folio);
996

Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
#5 lore raw

PATCH v4 2/3 mm: add vmstat for kernel_file pages

In reply to Boris Burkov

Kernel file pages are tricky to track because they are indistinguishable from files whose usage is accounted to the root cgroup.

To maintain good accounting, introduce a vmstat counter tracking kernel file pages.

Confirmed that these work as expected at a high level by mounting a btrfs using AS_KERNEL_FILE for metadata pages, and seeing the counter rise with fs usage then go back to a minimal level after drop_caches and finally down to 0 after unmounting the fs.

---
 include/linux/mmzone.h | 1 +
 mm/filemap.c           | 7 +++++++
 mm/vmstat.c            | 1 +
 3 files changed, 9 insertions(+)
include/linux/mmzone.h +1 -0
index fe13ad175fed..f3272ef5131b 100644
@@ -259,6 +259,7 @@ enum node_stat_item {
 	NR_HUGETLB,
 #endif
 	NR_BALLOON_PAGES,
+	NR_KERNEL_FILE_PAGES,
 	NR_VM_NODE_STAT_ITEMS
 };
 
mm/filemap.c +7 -0
index 05c1384bd611..344ab106c21c 100644
@@ -190,6 +190,9 @@ static void filemap_unaccount_folio(struct address_space *mapping,
 		__lruvec_stat_mod_folio(folio, NR_FILE_THPS, -nr);
 		filemap_nr_thps_dec(mapping);
 	}
+	if (test_bit(AS_KERNEL_FILE, &folio->mapping->flags))
+		mod_node_page_state(folio_pgdat(folio),
+				    NR_KERNEL_FILE_PAGES, -nr);
 
 	/*
 	 * At this point folio must be either written or cleaned by
@@ -989,6 +992,10 @@ int filemap_add_folio(struct address_space *mapping, struct folio *folio,
 		if (!(gfp & __GFP_WRITE) && shadow)
 			workingset_refault(folio, shadow);
 		folio_add_lru(folio);
+		if (kernel_file)
+			mod_node_page_state(folio_pgdat(folio),
+					    NR_KERNEL_FILE_PAGES,
+					    folio_nr_pages(folio));
 	}
 	return ret;
 }
mm/vmstat.c +1 -0
index e74f0b2a1021..e522decf6a72 100644
@@ -1290,6 +1290,7 @@ const char * const vmstat_text[] = {
 	[I(NR_HUGETLB)]				= "nr_hugetlb",
 #endif
 	[I(NR_BALLOON_PAGES)]			= "nr_balloon_pages",
+	[I(NR_KERNEL_FILE_PAGES)]		= "nr_kernel_file_pages",
 #undef I
 
 	/* system-wide enum vm_stat_item counters */
Signature · 1 line
2.50.1
#6 lore raw

PATCH v4 3/3 btrfs: set AS_KERNEL_FILE on the btree_inode

In reply to Boris Burkov

extent_buffers are global and shared so their pages should not belong to any particular cgroup (currently whichever cgroups happens to allocate the extent_buffer).

Btrfs tree operations should not arbitrarily block on cgroup reclaim or have the shared extent_buffer pages on a cgroup's reclaim lists.

---
 fs/btrfs/disk-io.c | 1 +
 1 file changed, 1 insertion(+)
fs/btrfs/disk-io.c +1 -0
index 70fc4e7cc5a0..7fab5057cf8e 100644
@@ -1930,6 +1930,7 @@ static int btrfs_init_btree_inode(struct super_block *sb)
 	BTRFS_I(inode)->root = btrfs_grab_root(fs_info->tree_root);
 	set_bit(BTRFS_INODE_DUMMY, &BTRFS_I(inode)->runtime_flags);
 	__insert_inode_hash(inode, hash);
+	set_bit(AS_KERNEL_FILE, &inode->i_mapping->flags);
 	fs_info->btree_inode = inode;
 
 	return 0;
Signature · 1 line
2.50.1
maintainer
#7 lore raw
In reply to Boris Burkov

Hi Andrew, can you please pick this series up (replacing the "introduce uncharged file mapped folios" series)? I think it is ready for wider testing.

thanks,
Shakeel

Quoted 42 lines · Boris Burkov

I would like to revisit Qu's proposal to not charge btrfs extent_buffer allocations to the user's cgroup.

https://lore.kernel.org/linux-mm/b5fef5372ae454a7b6da4f2f75c427aeab6a07d6.1727498749.git.wqu@suse.com/

I believe it is detrimental to account these global pages to the cgroup using them, basically at random. A bit more justification and explanation in the patches themselves.

---
Changelog:
v4:
- change the concept from "uncharged" to "kernel_file"
- no longer violates the invariant that each mapped folio has a memcg when CONFIG_MEMCG=y
- no longer really tied to memcg conceptually, so simplify build/helpers v3:
- use mod_node_page_state since we will never count cgroup stats
- include Shakeel's patch that removes a WARNING triggered by this series v2:
- switch from filemap_add_folio_nocharge() to AS_UNCHARGED on the address_space.
- fix an interrupt safety bug in the vmstat patch.
- fix some foolish build errors for CONFIG_MEMCG=n

Boris Burkov (3):
mm/filemap: add AS_KERNEL_FILE
mm: add vmstat for kernel_file pages
btrfs: set AS_KERNEL_FILE on the btree_inode

 fs/btrfs/disk-io.c      |  1 +
 include/linux/mmzone.h  |  1 +
 include/linux/pagemap.h |  2 ++
 mm/filemap.c            | 13 +++++++++++++
 mm/vmstat.c             |  1 +
 5 files changed, 18 insertions(+)
Signature · 1 line
2.50.1
Acked-by on the v4 series
#8 lore raw
In reply to Boris Burkov
Quoted 32 lines · Boris Burkov

I would like to revisit Qu's proposal to not charge btrfs extent_buffer allocations to the user's cgroup.

https://lore.kernel.org/linux-mm/b5fef5372ae454a7b6da4f2f75c427aeab6a07d6.1727498749.git.wqu@suse.com/

I believe it is detrimental to account these global pages to the cgroup using them, basically at random. A bit more justification and explanation in the patches themselves.

---
Changelog:
v4:
- change the concept from "uncharged" to "kernel_file"
- no longer violates the invariant that each mapped folio has a memcg when CONFIG_MEMCG=y
- no longer really tied to memcg conceptually, so simplify build/helpers v3:
- use mod_node_page_state since we will never count cgroup stats
- include Shakeel's patch that removes a WARNING triggered by this series v2:
- switch from filemap_add_folio_nocharge() to AS_UNCHARGED on the address_space.
- fix an interrupt safety bug in the vmstat patch.
- fix some foolish build errors for CONFIG_MEMCG=n

Boris Burkov (3):
mm/filemap: add AS_KERNEL_FILE
mm: add vmstat for kernel_file pages
btrfs: set AS_KERNEL_FILE on the btree_inode

Quoted 5 lines
 fs/btrfs/disk-io.c      |  1 +
 include/linux/mmzone.h  |  1 +
 include/linux/pagemap.h |  2 ++
 mm/filemap.c            | 13 +++++++++++++
 mm/vmstat.c             |  1 +

For the btrfs parts,