FAILED: patch "[PATCH] mm/huge_memory: use folio's memcg inside __folio_split()" failed to apply to 6.6-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 6.6-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.6.y
git checkout FETCH_HEAD
git cherry-pick -x c299a2285d9d8bda4da024455de65e3d00de6f17
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090357-daybed-breach-27b2@gregkh' --subject-prefix 'PATCH 6.6.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From c299a2285d9d8bda4da024455de65e3d00de6f17 Mon Sep 17 00:00:00 2001
From: Zi Yan <[email protected]>
Date: Tue, 4 Aug 2026 17:04:27 -0400
Subject: [PATCH] mm/huge_memory: use folio's memcg inside __folio_split()
Patch series "Honor XA_FLAGS_ACCOUNT in xas_split_alloc() and charge to folio's memcg", v3.
__GFP_ACCOUNT is needed for xarray node allocation accounting when XA_FLAGS_ACCOUNT is set. Commit 7b785645e8f13 ("mm: fix page cache convergence regression") fixed a workingset regression with it. xas_split_alloc() does not have it and needs to be fixed.
In addition, based on Sashiko's review[1] and Johannes' confirmation[2], to charge the right memcg, folio's memcg needs to be active during folio split. Add that before adding __GFP_ACCOUNT.
There is no workingset convergence regression related to missing __GFP_ACCOUNT in xas_split_alloc() and the impact to userspace should be minor.
This patch (of 2):
During a pagecache folio split, an xarray node allocation can happen and needs to charge at folio's memcg instead of folio split invoker's memcg, because for example folio split can happen during reclaim and reclaim's active memcg might not be folio's memcg. Switch to folio's memcg at the beginning and switch back afterwards.
mm/huge_memory.c +16 -4
@@ -4105,34 +4105,42 @@ static int __folio_split(struct folio *folio, unsigned int new_order,XA_STATE (xas, &folio->mapping->i_pages, folio->index);struct folio *end_folio =folio_next (folio);bool is_anon =folio_test_anon (folio); +struct mem_cgroup *memcg, *old_memcg;struct address_space *mapping =NULL ;struct anon_vma *anon_vma =NULL ;int old_order =folio_order (folio);struct folio *new_folio, *next;int nr_shmem_dropped =0 ;enum ttu_flags ttu_flags =0 ; -int ret;pgoff_t end =0 ; +int ret;VM_WARN_ON_ONCE_FOLIO (!folio_test_locked (folio), folio);VM_WARN_ON_ONCE_FOLIO (!folio_test_large (folio), folio);if (folio !=page_folio (split_at) || folio !=page_folio (lock_at)) { ret = -EINVAL ; - goto out; + goto out_no_memcg; }if (new_order >= old_order) { ret = -EINVAL ; - goto out; + goto out_no_memcg; } ret =folio_check_splittable (folio, new_order, split_type);if (ret) {VM_WARN_ONCE (ret == -EINVAL ,"Tried to split an unsplittable folio" ); - goto out; + goto out_no_memcg; } +/* +* switch to folio's memcg as xarray node allocation can happen and +* needs to charge to it. +*/ + memcg =get_mem_cgroup_from_folio (folio); + old_memcg =set_active_memcg (memcg); +if (is_anon) {/* * The caller does not necessarily hold an mmap_lock that would
@@ -4275,6 +4283,10 @@ static int __folio_split(struct folio *folio, unsigned int new_order,if (mapping)i_mmap_unlock_read (mapping); out: +/* restore to caller's old_memcg */ +set_active_memcg (old_memcg); +mem_cgroup_put (memcg); +out_no_memcg:xas_destroy (&xas);if (is_pmd_order (old_order))count_vm_event (!ret ?THP_SPLIT_PAGE :THP_SPLIT_PAGE_FAILED );
Zi Yan
reviewerPATCH 1/1 mm/huge_memory: use folio's memcg inside __folio_split()
Patch series "Honor XA_FLAGS_ACCOUNT in xas_split_alloc() and charge to folio's memcg", v3.
__GFP_ACCOUNT is needed for xarray node allocation accounting when XA_FLAGS_ACCOUNT is set. Commit 7b785645e8f13 ("mm: fix page cache convergence regression") fixed a workingset regression with it. xas_split_alloc() does not have it and needs to be fixed.
In addition, based on Sashiko's review[1] and Johannes' confirmation[2], to charge the right memcg, folio's memcg needs to be active during folio split. Add that before adding __GFP_ACCOUNT.
There is no workingset convergence regression related to missing __GFP_ACCOUNT in xas_split_alloc() and the impact to userspace should be minor.
This patch (of 2):
During a pagecache folio split, an xarray node allocation can happen and needs to charge at folio's memcg instead of folio split invoker's memcg, because for example folio split can happen during reclaim and reclaim's active memcg might not be folio's memcg. Switch to folio's memcg at the beginning and switch back afterwards.
--- mm/huge_memory.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
mm/huge_memory.c +10 -0
@@ -2646,6 +2646,7 @@ int split_huge_page_to_list(struct page *page, struct list_head *list)struct folio *folio =page_folio (page);struct deferred_split *ds_queue =get_deferred_split_queue (folio);XA_STATE (xas, &folio->mapping->i_pages, folio->index); +struct mem_cgroup *memcg, *old_memcg;struct anon_vma *anon_vma =NULL ;struct address_space *mapping =NULL ;int extra_pins, ret;
@@ -2664,6 +2665,13 @@ int split_huge_page_to_list(struct page *page, struct list_head *list)if (folio_test_writeback (folio))return -EBUSY ; +/* +* switch to folio's memcg as xarray node allocation can happen and +* needs to charge to it. +*/ + memcg =folio_memcg (folio); + old_memcg =set_active_memcg (memcg); +if (folio_test_anon (folio)) {/* * The caller does not necessarily hold an mmap_lock that would
@@ -2792,6 +2800,8 @@ int split_huge_page_to_list(struct page *page, struct list_head *list)if (mapping)i_mmap_unlock_read (mapping); out: +/* restore to caller's old_memcg */ +set_active_memcg (old_memcg);xas_destroy (&xas);count_vm_event (!ret ?THP_SPLIT_PAGE :THP_SPLIT_PAGE_FAILED );return ret;
Signature · 1 line
2.53.0
Sasha Levin
maintainerRe: [PATCH 6.6.y] mm/huge_memory: use folio's memcg inside __folio_split()
Quoted 4 lines
__GFP_ACCOUNTis needed for xarray node allocation accounting whenXA_FLAGS_ACCOUNTis set. Commit7b785645e8f13("mm: fix page cache convergence regression") fixed a workingset regression with it.xas_split_alloc()does not have it and needs to be fixed.
Mainline's c299a2285d9d takes a reference on the folio's memcg with get_mem_cgroup_from_folio() and holds it in current->active_memcg for the duration of __folio_split() - rcu_read_lock()+css_tryget(), released with mem_cgroup_put() afterwards. This 6.6.y version instead holds a bare folio_memcg(folio) pointer across the same window, which includes a TLB shootdown and several lock acquisitions - exactly the kind of window the upstream refcount exists to close.
6.6 already has rcu_read_lock(), css_tryget() and mem_cgroup_put() available, so nothing's missing to do this the same way upstream does. Could you resend a v2 that takes and drops the reference (under CONFIG_MEMCG, as upstream does)?
Signature · 2 lines
Thanks, Sasha
Zi Yan
reviewerRe: [PATCH 6.6.y] mm/huge_memory: use folio's memcg inside __folio_split()
Quoted 13 lines · Sasha Levin
Quoted 4 lines
__GFP_ACCOUNTis needed for xarray node allocation accounting whenXA_FLAGS_ACCOUNTis set. Commit7b785645e8f13("mm: fix page cache convergence regression") fixed a workingset regression with it.xas_split_alloc()does not have it and needs to be fixed.Mainline's
c299a2285d9dtakes a reference on the folio's memcg withget_mem_cgroup_from_folio()and holds it incurrent->active_memcgfor the duration of__folio_split()- rcu_read_lock()+css_tryget(), released withmem_cgroup_put()afterwards. This 6.6.y version instead holds a barefolio_memcg(folio)pointer across the same window, which includes a TLB shootdown and several lock acquisitions - exactly the kind of window the upstream refcount exists to close.
Quoted 3 lines
6.6 already has
rcu_read_lock(),css_tryget()andmem_cgroup_put()available, so nothing's missing to do this the same way upstream does. Could you resend a v2 that takes and drops the reference (underCONFIG_MEMCG, as upstream does)?
No, this is not needed. Before commit f1cf8d2f36dc3 ("mm: memcontrol:
eliminate the problem of dying memory cgroup for LRU folios"), like v6.6, a charged folio take a ref on its memcg. During split, the folio is locked, so will not be uncharged. As a result, its memcg will not go away.
According to Claude, adding a ref there might cause a deadlock when a to-be-split folio has a dying memcg, where css_tryget() keeps failing and folio_memcg() keeps returning the same memcg.
This should also apply to v6.1. I will send my version for 6.1 backport.
Signature · 2 lines
Best Regards, Yan, Zi
Sasha Levin
maintainerRe: [PATCH 6.6.y] mm/huge_memory: use folio's memcg inside __folio_split()
Quoted 5 lines
No, this is not needed. Before commit
f1cf8d2f36dc3("mm: memcontrol:
eliminate the problem of dying memory cgroup for LRU folios"), like v6.6, a charged folio take a ref on its memcg. During split, the folio is locked, so will not be uncharged. As a result, its memcg will not go away.
A review from one of the mm/ folks will be appreciated here :)
Signature · 2 lines
Thanks, Sasha
Johannes Weiner
Reviewed-byRe: [PATCH 6.6.y] mm/huge_memory: use folio's memcg inside __folio_split()
In reply to Zi YanQuoted 18 lines · Zi Yan
Quoted 13 lines · Sasha Levin
Quoted 4 lines
__GFP_ACCOUNTis needed for xarray node allocation accounting whenXA_FLAGS_ACCOUNTis set. Commit7b785645e8f13("mm: fix page cache convergence regression") fixed a workingset regression with it.xas_split_alloc()does not have it and needs to be fixed.Mainline's
c299a2285d9dtakes a reference on the folio's memcg withget_mem_cgroup_from_folio()and holds it incurrent->active_memcgfor the duration of__folio_split()- rcu_read_lock()+css_tryget(), released withmem_cgroup_put()afterwards. This 6.6.y version instead holds a barefolio_memcg(folio)pointer across the same window, which includes a TLB shootdown and several lock acquisitions - exactly the kind of window the upstream refcount exists to close.Quoted 3 lines
6.6 already has
rcu_read_lock(),css_tryget()andmem_cgroup_put()available, so nothing's missing to do this the same way upstream does. Could you resend a v2 that takes and drops the reference (underCONFIG_MEMCG, as upstream does)?
Quoted 5 lines
No, this is not needed. Before commit
f1cf8d2f36dc3("mm: memcontrol:
eliminate the problem of dying memory cgroup for LRU folios"), like v6.6, a charged folio take a ref on its memcg. During split, the folio is locked, so will not be uncharged. As a result, its memcg will not go away.
Yes. It's safe until that last unlock & free in __split_huge_page(). We don't have allocations or indirect references to current->active_memcg afterwards that I can see.
Quoted 3 lines
According to Claude, adding a ref there might cause a deadlock when a to-be-split folio has a dying memcg, where
css_tryget()keeps failing andfolio_memcg()keeps returning the same memcg.
If the cgroup was rmdir'd, only css_tryget_online() would fail. The page itself holds a reference, keeping the dying cgroup from being freed. And css_tryget() would still work.
So it would be doable if you wanted to keep the backport closer to what we have upstream. But yes, it's not strictly necessary.
Sasha Levin
maintainerRe: [PATCH 6.6.y] mm/huge_memory: use folio's memcg inside __folio_split()
Thanks for the review, Johannes.
I'm holding the 6.6.y patch for now, because 6.12.y needs this first. c299a2285d9d ("mm/huge_memory: use folio's memcg inside __folio_split()") is absent from 6.12.y, and we don't queue a fix on an older tree while a newer one still lacks it.
6.12.y also needs its own version rather than this one. I tried your 6.6.y patch against it and it fails at mm/huge_memory.c:2646. The two trees have different shapes here: 6.6.y has split_huge_page_to_list(page, list) using XA_STATE, while 6.12.y has split_huge_page_to_list_to_order(page, list, new_order) using XA_STATE_ORDER.
Zi Yan, could you send a 6.12.y backport? Once that one is in I'll take both together.
Signature · 2 lines
Thanks, Sasha
Zi Yan
reviewerRe: [PATCH 6.6.y] mm/huge_memory: use folio's memcg inside __folio_split()
Quoted 7 lines · Sasha Levin
Thanks for the review, Johannes.
I'm holding the 6.6.y patch for now, because 6.12.y needs this first.
c299a2285d9d("mm/huge_memory: use folio's memcg inside__folio_split()") is absent from 6.12.y, and we don't queue a fix on an older tree while a newer one still lacks it.
Quoted 8 lines
6.12.y also needs its own version rather than this one. I tried your 6.6.y patch against it and it fails at
mm/huge_memory.c:2646. The two trees have different shapes here: 6.6.y hassplit_huge_page_to_list(page, list)usingXA_STATE, while 6.12.y hassplit_huge_page_to_list_to_order(page, list, new_order)usingXA_STATE_ORDER.Zi Yan, could you send a 6.12.y backport? Once that one is in I'll take both together.
Done. See https://lore.kernel.org/all/[email protected]/
BTW, I see you also sent one[1], but it seems that it is using a non-existing get_mem_cgroup_from_folio(). What is that for? Thanks.
[1] https://lore.kernel.org/all/[email protected]/
Signature · 2 lines
Best Regards, Yan, Zi
Re: [PATCH 6.6.y] mm/huge_memory: use folio's memcg inside __folio_split()
Quoted 1 line
Queued for 6.12 and 6.6, thanks.
Quoted 2 lines
BTW, I see you also sent one[1], but it seems that it is using a non-existing
get_mem_cgroup_from_folio(). What is that for? Thanks.
That one was generated against a tree that has the helper and should not have gone out for 6.6.y. Please ignore it, yours is what is queued.
Signature · 2 lines
Thanks, Sasha
Sasha Levin
maintainerPATCH 1/1 mm/huge_memory: use folio's memcg inside __folio_split()
In reply to [email protected]From: Zi Yan <[email protected]>
[ Upstream commit c299a2285d9d8bda4da024455de65e3d00de6f17 ]
Patch series "Honor XA_FLAGS_ACCOUNT in xas_split_alloc() and charge to folio's memcg", v3.
__GFP_ACCOUNT is needed for xarray node allocation accounting when XA_FLAGS_ACCOUNT is set. Commit 7b785645e8f13 ("mm: fix page cache convergence regression") fixed a workingset regression with it. xas_split_alloc() does not have it and needs to be fixed.
In addition, based on Sashiko's review[1] and Johannes' confirmation[2], to charge the right memcg, folio's memcg needs to be active during folio split. Add that before adding __GFP_ACCOUNT.
There is no workingset convergence regression related to missing __GFP_ACCOUNT in xas_split_alloc() and the impact to userspace should be minor.
This patch (of 2):
During a pagecache folio split, an xarray node allocation can happen and needs to charge at folio's memcg instead of folio split invoker's memcg, because for example folio split can happen during reclaim and reclaim's active memcg might not be folio's memcg. Switch to folio's memcg at the beginning and switch back afterwards.
--- mm/huge_memory.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
mm/huge_memory.c +17 -0
@@ -2646,6 +2646,7 @@ int split_huge_page_to_list(struct page *page, struct list_head *list)struct folio *folio =page_folio (page);struct deferred_split *ds_queue =get_deferred_split_queue (folio);XA_STATE (xas, &folio->mapping->i_pages, folio->index); +struct mem_cgroup *memcg, *old_memcg;struct anon_vma *anon_vma =NULL ;struct address_space *mapping =NULL ;int extra_pins, ret;
@@ -2664,6 +2665,19 @@ int split_huge_page_to_list(struct page *page, struct list_head *list)if (folio_test_writeback (folio))return -EBUSY ; +/* +* switch to folio's memcg as xarray node allocation can happen and +* needs to charge to it. +*/ +#ifdef CONFIG_MEMCG + memcg =folio_memcg (folio); +if (!memcg ||WARN_ON_ONCE (!css_tryget (&memcg->css))) + memcg = root_mem_cgroup; +#else + memcg =NULL ; +#endif + old_memcg =set_active_memcg (memcg); +if (folio_test_anon (folio)) {/* * The caller does not necessarily hold an mmap_lock that would
@@ -2792,6 +2806,9 @@ int split_huge_page_to_list(struct page *page, struct list_head *list)if (mapping)i_mmap_unlock_read (mapping); out: +/* restore to caller's old_memcg */ +set_active_memcg (old_memcg); +mem_cgroup_put (memcg);xas_destroy (&xas);count_vm_event (!ret ?THP_SPLIT_PAGE :THP_SPLIT_PAGE_FAILED );return ret;
Signature · 1 line
2.53.0