introduce uncharged file mapped folios
Subsystems (6)
v2 cover introduce uncharged file mapped folios
I would like to revisit Qu's proposal to not charge btrfs extent_buffer allocations to the user's cgroup.
I believe it is detrimental to randomly account these global pages to the cgroup using them, basically at random. A bit more justification and explanation in the patches themselves.
---
Changelog:
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_UNCHARGED
mm: add vmstat for cgroup uncharged pages
btrfs: set AS_UNCHARGED on the btree_inode
fs/btrfs/disk-io.c | 1 + include/linux/mmzone.h | 3 +++ include/linux/pagemap.h | 1 + mm/filemap.c | 29 +++++++++++++++++++++++++---- mm/vmstat.c | 3 +++ 5 files changed, 33 insertions(+), 4 deletions(-)
Signature · 1 line
2.50.1
PATCH v2 1/3 mm/filemap: add AS_UNCHARGED
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.
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 uncharged 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 the uncharged behavior.
--- include/linux/pagemap.h | 1 + mm/filemap.c | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-)
include/linux/pagemap.h +1 -0
@@ -211,6 +211,7 @@ 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_UNCHARGED =10 ,/* Do not charge usage to a cgroup */ /* Bits 16-25 are used for FOLIO_ORDER */ AS_FOLIO_ORDER_BITS =5 , AS_FOLIO_ORDER_MIN = 16,
mm/filemap.c +8 -4
@@ -960,15 +960,19 @@ int filemap_add_folio(struct address_space *mapping, struct folio *folio, {void *shadow =NULL ;int ret; +bool charge_mem_cgroup = !test_bit (AS_UNCHARGED , &mapping->flags); - ret =mem_cgroup_charge (folio,NULL , gfp); -if (ret) -return ret; +if (charge_mem_cgroup) { + ret =mem_cgroup_charge (folio,NULL , gfp); +if (ret) +return ret; + }__folio_set_locked (folio); ret =__filemap_add_folio (mapping, folio, index, gfp, &shadow);if (unlikely (ret)) { -mem_cgroup_uncharge (folio); +if (charge_mem_cgroup) +mem_cgroup_uncharge (folio);__folio_clear_locked (folio); }else {/*
Signature · 1 line
2.50.1
Re: [PATCH v2 1/3] mm/filemap: add AS_UNCHARGED
Quoted 25 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.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 uncharged behavior should apply to all use of theaddress_space, not select allocations. I.e., if someone adds anotherfilemap_add_folio()call using btrfs'sbtree_inode, we would almost certainly want the uncharged behavior.
Quoted 3 lines
PATCH v2 2/3 mm: add vmstat for cgroup uncharged pages
In reply to Boris BurkovUncharged pages are tricky to track by their essential "uncharged" nature. To maintain good accounting, introduce a vmstat counter tracking all uncharged pages. Since this is only meaningful when cgroups are configured, only expose the counter when CONFIG_MEMCG is set.
Confirmed that these work as expected at a high level by mounting a btrfs using AS_UNCHARGED 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 | 3 +++ mm/filemap.c | 17 +++++++++++++++++ mm/vmstat.c | 3 +++ 3 files changed, 23 insertions(+)
include/linux/mmzone.h +3 -0
@@ -245,6 +245,9 @@ enum node_stat_item { NR_HUGETLB, #endif NR_BALLOON_PAGES, +#ifdef CONFIG_MEMCG + NR_UNCHARGED_FILE_PAGES, +#endif NR_VM_NODE_STAT_ITEMS };
mm/filemap.c +17 -0
@@ -146,6 +146,19 @@ static void page_cache_delete(struct address_space *mapping, mapping->nrpages -= nr; } +#ifdef CONFIG_MEMCG +static void filemap_mod_uncharged_vmstat (struct folio *folio,int sign) +{ +long nr =folio_nr_pages (folio) * sign; + +lruvec_stat_mod_folio (folio,NR_UNCHARGED_FILE_PAGES , nr); +} +#else +static void filemap_mod_uncharged_vmstat (struct folio *folio,int sign) +{ +} +#endif +static void filemap_unaccount_folio (struct address_space *mapping,struct folio *folio) {
@@ -190,6 +203,8 @@ 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_UNCHARGED , &folio->mapping->flags)) +filemap_mod_uncharged_vmstat (folio,-1 );/* * At this point folio must be either written or cleaned by
@@ -987,6 +1002,8 @@ 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 (!charge_mem_cgroup) +filemap_mod_uncharged_vmstat (folio,1 ); }return ret; }
mm/vmstat.c +3 -0
@@ -1289,6 +1289,9 @@ const char * const vmstat_text[] = { [I(NR_HUGETLB)] = "nr_hugetlb", #endif [I (NR_BALLOON_PAGES )] ="nr_balloon_pages" , +#ifdef CONFIG_MEMCG + [I (NR_UNCHARGED_FILE_PAGES )] ="nr_uncharged_file_pages" , +#endif #undef I/* system-wide enum vm_stat_item counters */
Signature · 1 line
2.50.1
Re: [PATCH v2 2/3] mm: add vmstat for cgroup uncharged pages
Quoted 37 lines · Boris Burkov
Uncharged pages are tricky to track by their essential "uncharged" nature. To maintain good accounting, introduce a vmstat counter tracking all uncharged pages. Since this is only meaningful when cgroups are configured, only expose the counter when
CONFIG_MEMCGis set.Confirmed that these work as expected at a high level by mounting a btrfs using
AS_UNCHARGEDfor metadata pages, and seeing the counter rise with fs usage then go back to a minimal level afterdrop_cachesand finally down to 0 after unmounting the fs.Suggested-by Shakeel Butt <[email protected]>Signed-off-by Boris Burkov <[email protected]>--- include/linux/mmzone.h | 3 +++ mm/filemap.c | 17 +++++++++++++++++ mm/vmstat.c | 3 +++ 3 files changed, 23 insertions(+)include/linux/mmzone.h +3
-0@@ -245,6 +245,9 @@ enum node_stat_item { NR_HUGETLB, #endif NR_BALLOON_PAGES, +#ifdef CONFIG_MEMCG + NR_UNCHARGED_FILE_PAGES, +#endif NR_VM_NODE_STAT_ITEMS };mm/filemap.c +0
-0
mm/filemap.c static void page_cache_delete(struct address_space *mapping, · line 154
Quoted 9 lines
mm/filemap.c +6
-0@@ -146,6 +146,19 @@ static void page_cache_delete(struct address_space *mapping, mapping->nrpages -= nr; } +#ifdef CONFIG_MEMCG +static void filemap_mod_uncharged_vmstat (struct folio *folio,int sign) +{ +long nr =folio_nr_pages (folio) * sign; + +lruvec_stat_mod_folio (folio,NR_UNCHARGED_FILE_PAGES , nr);
Since we expect to add this metric to memory.stat, I think we should use mod_node_page_state() instead here.
With that you can add:
Shakeel Butt
reviewerRe: [PATCH v2 2/3] mm: add vmstat for cgroup uncharged pages
Quoted 41 lines · Shakeel Butt
Quoted 40 lines · Boris Burkov
Uncharged pages are tricky to track by their essential "uncharged" nature. To maintain good accounting, introduce a vmstat counter tracking all uncharged pages. Since this is only meaningful when cgroups are configured, only expose the counter when
CONFIG_MEMCGis set.Confirmed that these work as expected at a high level by mounting a btrfs using
AS_UNCHARGEDfor metadata pages, and seeing the counter rise with fs usage then go back to a minimal level afterdrop_cachesand finally down to 0 after unmounting the fs.Suggested-by Shakeel Butt <[email protected]>Signed-off-by Boris Burkov <[email protected]>--- include/linux/mmzone.h | 3 +++ mm/filemap.c | 17 +++++++++++++++++ mm/vmstat.c | 3 +++ 3 files changed, 23 insertions(+)include/linux/mmzone.h +3
-0@@ -245,6 +245,9 @@ enum node_stat_item { NR_HUGETLB, #endif NR_BALLOON_PAGES, +#ifdef CONFIG_MEMCG + NR_UNCHARGED_FILE_PAGES, +#endif NR_VM_NODE_STAT_ITEMS };mm/filemap.c +0
-0@@ -146,6 +146,19 @@ static void page_cache_delete(struct address_space *mapping, mapping->nrpages -= nr; }
Quoted 8 lines
mm/filemap.c static void page_cache_delete(struct address_space *mapping, · line 154
Quoted 6 lines
mm/filemap.c +6
-0+#ifdef CONFIG_MEMCG +static void filemap_mod_uncharged_vmstat (struct folio *folio,int sign) +{ +long nr =folio_nr_pages (folio) * sign; + +lruvec_stat_mod_folio (folio,NR_UNCHARGED_FILE_PAGES , nr);Since we
*never*
Quoted 7 lines
expect to add this metric to memory.stat, I think we should use
mod_node_page_state()instead here.With that you can add:
Acked-by Shakeel Butt <[email protected]>
PATCH v2 3/3 btrfs: set AS_UNCHARGED on the btree_inode
In reply to Boris Burkovextent_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
@@ -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_UNCHARGED , &inode->i_mapping->flags); fs_info->btree_inode = inode;return 0 ;
Signature · 1 line
2.50.1
Re: [PATCH v2 3/3] btrfs: set AS_UNCHARGED on the btree_inode
Quoted 8 lines · Boris Burkov
extent_buffersare global and shared so their pages should not belong to any particular cgroup (currently whichever cgroups happens to allocate theextent_buffer).Btrfs tree operations should not arbitrarily block on cgroup reclaim or have the shared
extent_bufferpages on a cgroup's reclaim lists.Signed-off-by Boris Burkov <[email protected]>
I think mm-tree is better for this series.
David Sterba
Acked-byRe: [PATCH v2 3/3] btrfs: set AS_UNCHARGED on the btree_inode
Quoted 5 lines · Shakeel Butt
Quoted 4 lines · Boris Burkov
extent_buffersare global and shared so their pages should not belong to any particular cgroup (currently whichever cgroups happens to allocate theextent_buffer).
Quoted 8 lines
Quoted 4 lines
Btrfs tree operations should not arbitrarily block on cgroup reclaim or have the shared
extent_bufferpages on a cgroup's reclaim lists.Signed-off-by Boris Burkov <[email protected]>Acked-by Shakeel Butt <[email protected]>I think mm-tree is better for this series.
Agreed, majority of the changes are in MM code and the btrfs side is not something that would affect other testing.
syzbot ci has tested the following series
syzbot ci has tested the following series
[v2] introduce uncharged file mapped folios
https://lore.kernel.org/all/[email protected]
* [PATCH v2 1/3] mm/filemap: add AS_UNCHARGED
* [PATCH v2 2/3] mm: add vmstat for cgroup uncharged pages
* [PATCH v2 3/3] btrfs: set AS_UNCHARGED on the btree_inode
and found the following issue:
WARNING in folio_lruvec_lock_irqsave
Full report is available here:
https://ci.syzbot.org/series/15fd2538-1138-43c0-b4d6-6d7f53b0be69
***
WARNING in folio_lruvec_lock_irqsave
tree: torvalds URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux base: dfc0f6373094dd88e1eaf76c44f2ff01b65db851 arch: amd64 compiler: Debian clang version 20.1.7 (++20250616065708+6146a88f6049-1~exp1~20250616065826.132), Debian LLD 20.1.7 config: https://ci.syzbot.org/builds/5b3f7bc8-d0c2-4985-8ae6-d51ea4e2baed/config C repro: https://ci.syzbot.org/findings/f02552bd-e7e6-4c3f-8cc3-04a5a946771d/c_repro syz repro: https://ci.syzbot.org/findings/f02552bd-e7e6-4c3f-8cc3-04a5a946771d/syz_repro
do_new_mount+0x2a2/0x9e0 fs/namespace.c:3805 do_mount fs/namespace.c:4133 [inline] __do_sys_mount fs/namespace.c:4344 [inline] __se_sys_mount+0x317/0x410 fs/namespace.c:4321 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xfa/0x3b0 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f ------------[ cut here ]------------ WARNING: CPU: 0 PID: 5951 at ./include/linux/memcontrol.h:734 folio_lruvec include/linux/memcontrol.h:734 [inline] WARNING: CPU: 0 PID: 5951 at ./include/linux/memcontrol.h:734 folio_lruvec_lock_irqsave+0x184/0x1d0 mm/memcontrol.c:1252 Modules linked in: CPU: 0 UID: 0 PID: 5951 Comm: syz-executor Not tainted 6.17.0-rc1-syzkaller-00036-gdfc0f6373094-dirty #0 PREEMPT(full) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014 RIP: 0010:folio_lruvec include/linux/memcontrol.h:734 [inline] RIP: 0010:folio_lruvec_lock_irqsave+0x184/0x1d0 mm/memcontrol.c:1252 Code: 74 0c 4c 89 f7 e8 cc 53 f8 ff 48 8b 04 24 49 89 06 eb a0 48 89 df 48 c7 c6 60 4e 98 8b e8 a4 36 fd fe c6 05 52 a1 62 0d 01 90 <0f> 0b 90 e9 a5 fe ff ff 44 89 e1 80 e1 07 80 c1 03 38 c1 0f 8c 22 RSP: 0018:ffffc9000359f540 EFLAGS: 00010246 RAX: f0f9782cf8520600 RBX: ffffea0000807140 RCX: f0f9782cf8520600 RDX: 0000000000000002 RSI: ffffffff8dba6067 RDI: ffff888020a73980 RBP: ffffc9000359f5e0 R08: ffff88804b024253 R09: 1ffff1100960484a R10: dffffc0000000000 R11: ffffed100960484b R12: ffff88804b032fe8 R13: ffff88801ba80918 R14: ffff888106950000 R15: 0000000000000000 FS: 0000555587fa3500(0000) GS:ffff8880b861c000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 000000c00003e720 CR3: 0000000029a9c000 CR4: 00000000000006f0 Call Trace: <TASK> folio_lruvec_relock_irqsave include/linux/memcontrol.h:1544 [inline] folio_batch_move_lru+0x20a/0x3a0 mm/swap.c:167 lru_add_drain_cpu+0x119/0x880 mm/swap.c:647 lru_add_drain+0x122/0x3e0 mm/swap.c:735 __folio_batch_release+0x48/0x90 mm/swap.c:1054 folio_batch_release include/linux/pagevec.h:101 [inline] invalidate_inode_pages2_range+0x889/0xa80 mm/truncate.c:707 close_ctree+0x6ff/0x1380 fs/btrfs/disk-io.c:4408 generic_shutdown_super+0x135/0x2c0 fs/super.c:643 kill_anon_super+0x3b/0x70 fs/super.c:1282 btrfs_kill_super+0x41/0x50 fs/btrfs/super.c:2114 deactivate_locked_super+0xbc/0x130 fs/super.c:474 cleanup_mnt+0x425/0x4c0 fs/namespace.c:1378 task_work_run+0x1d4/0x260 kernel/task_work.c:227 resume_user_mode_work include/linux/resume_user_mode.h:50 [inline] exit_to_user_mode_loop+0xec/0x110 kernel/entry/common.c:43 exit_to_user_mode_prepare include/linux/irq-entry-common.h:225 [inline] syscall_exit_to_user_mode_work include/linux/entry-common.h:175 [inline] syscall_exit_to_user_mode include/linux/entry-common.h:210 [inline] do_syscall_64+0x2bd/0x3b0 arch/x86/entry/syscall_64.c:100 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f4a78b8ff17 Code: a8 ff ff ff f7 d8 64 89 01 48 83 c8 ff c3 0f 1f 44 00 00 31 f6 e9 09 00 00 00 66 0f 1f 84 00 00 00 00 00 b8 a6 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 01 c3 48 c7 c2 a8 ff ff ff f7 d8 64 89 02 b8 RSP: 002b:00007ffe33fe80a8 EFLAGS: 00000246 ORIG_RAX: 00000000000000a6 RAX: 0000000000000000 RBX: 00007f4a78c11c05 RCX: 00007f4a78b8ff17 RDX: 0000000000000000 RSI: 0000000000000009 RDI: 00007ffe33fe8160 RBP: 00007ffe33fe8160 R08: 0000000000000000 R09: 0000000000000000 R10: 00000000ffffffff R11: 0000000000000246 R12: 00007ffe33fe91f0 R13: 00007f4a78c11c05 R14: 000000000000f471 R15: 00007ffe33fe9230 </TASK>
***
If these findings have caused you to resend the series or submit a separate fix, please add the following tag to your commit message:
---
This report is generated by a bot. It may contain errors. syzbot ci engineers can be reached at [email protected].
Shakeel Butt
reviewerQuoted 10 lines · syzbot ci
syzbot ci has tested the following series
[v2] introduce uncharged file mapped folios
https://lore.kernel.org/all/[email protected]
* [PATCH v2 1/3] mm/filemap: addAS_UNCHARGED
* [PATCH v2 2/3] mm: add vmstat for cgroup uncharged pages
* [PATCH v2 3/3] btrfs: setAS_UNCHARGEDon thebtree_inodeand found the following issue:
WARNING infolio_lruvec_lock_irqsave
Ok that is expected and we need to fix the warning instead. We can either remove this warning or make is only applicable to non-file folios.