Merge tag 'mm-hotfixes-stable-2026-08-19-21-33' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm

Pull hotfixes from Andrew Morton:
 "8 hotfixes. 5 are cc:stable. 5 are for MM.

  All are singletons, please see their changelogs for details"

* tag 'mm-hotfixes-stable-2026-08-19-21-33' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm:
  mm/pagewalk: fix stale walk->action escaping walk_pmd_range()
  mm, swap: don't free a hibernation slot that is in the swap cache
  mm: memcg-v1: fix memsw and TCP failcnt accounting
  mm/vmscan: report RCU-tasks quiescent states in shrink_lruvec()
  mailmap: add entries for Guodong Xu
  MAINTAINERS, mailmap: update email address for JP Kobryn
  MAINTAINERS: remove git URL for Squashfs
  memcg: keep folio's objcg same as its node
This commit is contained in:
Linus Torvalds
2026-08-20 20:05:59 -07:00
6 changed files with 100 additions and 25 deletions
+3
View File
@@ -331,6 +331,8 @@ Gokul Sriram Palanisamy <quic_gokulsri@quicinc.com> <gokulsri@codeaurora.org>
Govindaraj Saminathan <quic_gsamin@quicinc.com> <gsamin@codeaurora.org>
Guo Ren <guoren@kernel.org> <guoren@linux.alibaba.com>
Guo Ren <guoren@kernel.org> <ren_guo@c-sky.com>
Guodong Xu <docular.xu@gmail.com> <guodong.xu@linaro.org>
Guodong Xu <docular.xu@gmail.com> <guodong@riscstar.com>
Guru Das Srinagesh <linux@gurudas.dev>
Guru Das Srinagesh <linux@gurudas.dev> <quic_gurus@quicinc.com>
Guru Das Srinagesh <linux@gurudas.dev> <gurus@codeaurora.org>
@@ -465,6 +467,7 @@ Jorge Ramirez-Ortiz <jorge.ramirez@oss.qualcomm.com> <jorge.ramirez-ortiz@linaro
Josh Poimboeuf <jpoimboe@kernel.org> <jpoimboe@redhat.com>
Josh Poimboeuf <jpoimboe@kernel.org> <jpoimboe@us.ibm.com>
Jouni Malinen <quic_jouni@quicinc.com> <jouni@codeaurora.org>
JP Kobryn <jp.kobryn@linux.dev> <inwardvessel@gmail.com>
Juha Yrjola <at solidboot.com>
Juha Yrjola <juha.yrjola@nokia.com>
Juha Yrjola <juha.yrjola@solidboot.com>
+1 -2
View File
@@ -4985,7 +4985,7 @@ F: tools/lib/bpf/
BPF [MEMORY MANAGEMENT EXTENSIONS]
M: Roman Gushchin <roman.gushchin@linux.dev>
M: JP Kobryn <inwardvessel@gmail.com>
M: JP Kobryn <jp.kobryn@linux.dev>
M: Shakeel Butt <shakeel.butt@linux.dev>
L: bpf@vger.kernel.org
L: linux-mm@kvack.org
@@ -25672,7 +25672,6 @@ M: Phillip Lougher <phillip@squashfs.org.uk>
L: squashfs-devel@lists.sourceforge.net (subscribers-only)
S: Maintained
W: http://squashfs.org.uk
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pkl/squashfs-next.git
F: Documentation/filesystems/squashfs.rst
F: fs/squashfs/
+85 -17
View File
@@ -2912,10 +2912,9 @@ struct mem_cgroup *mem_cgroup_from_virt(void *p)
return folio_memcg_check(virt_to_folio(p));
}
static struct obj_cgroup *__get_obj_cgroup_from_memcg(struct mem_cgroup *memcg)
static struct obj_cgroup *__get_obj_cgroup_from_memcg(struct mem_cgroup *memcg,
int nid)
{
int nid = numa_node_id();
for (; memcg; memcg = parent_mem_cgroup(memcg)) {
struct obj_cgroup *objcg = rcu_dereference(memcg->nodeinfo[nid]->objcg);
@@ -2926,12 +2925,13 @@ static struct obj_cgroup *__get_obj_cgroup_from_memcg(struct mem_cgroup *memcg)
return NULL;
}
static inline struct obj_cgroup *get_obj_cgroup_from_memcg(struct mem_cgroup *memcg)
static inline struct obj_cgroup *get_obj_cgroup_from_memcg(struct mem_cgroup *memcg,
int nid)
{
struct obj_cgroup *objcg;
rcu_read_lock();
objcg = __get_obj_cgroup_from_memcg(memcg);
objcg = __get_obj_cgroup_from_memcg(memcg, nid);
rcu_read_unlock();
return objcg;
@@ -2975,7 +2975,7 @@ static struct obj_cgroup *current_objcg_update(void)
rcu_read_lock();
memcg = mem_cgroup_from_task(current);
objcg = __get_obj_cgroup_from_memcg(memcg);
objcg = __get_obj_cgroup_from_memcg(memcg, numa_node_id());
rcu_read_unlock();
/*
@@ -4182,9 +4182,11 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
page_counter_init(&memcg->swap, &parent->swap, false);
#ifdef CONFIG_MEMCG_V1
memcg->memory.track_failcnt = !memcg_on_dfl;
memcg->memsw.track_failcnt = !memcg_on_dfl;
WRITE_ONCE(memcg->oom_kill_disable, READ_ONCE(parent->oom_kill_disable));
page_counter_init(&memcg->kmem, &parent->kmem, false);
page_counter_init(&memcg->tcpmem, &parent->tcpmem, false);
memcg->tcpmem.track_failcnt = !memcg_on_dfl;
#endif
} else {
init_memcg_stats();
@@ -5138,7 +5140,7 @@ static int charge_memcg(struct folio *folio, struct mem_cgroup *memcg,
int ret = 0;
struct obj_cgroup *objcg;
objcg = get_obj_cgroup_from_memcg(memcg);
objcg = get_obj_cgroup_from_memcg(memcg, folio_nid(folio));
/* Do not account at the root objcg level. */
if (!obj_cgroup_is_root(objcg))
ret = try_charge_memcg(memcg, gfp, folio_nr_pages(folio));
@@ -5337,6 +5339,46 @@ void __mem_cgroup_uncharge_folios(struct folio_batch *folios)
uncharge_batch(&ug);
}
/*
* An LRU folio must hold the objcg belonging to its own node.
*
* memcg_reparent_objcgs() reparents a dying cgroup one node at a time: the
* folios on that node's LRU lists move to the parent and that node's objcg is
* redirected to the parent, atomically under the node's lru_lock.
* folio_lruvec_lock() relies on this to provide a stable folio<->lruvec
* binding. If a folio holds another node's objcg, its list membership and its
* lruvec resolution change in separate lock sections, and an LRU operation in
* between can re-add the folio to, and strand it on, the LRU list of a dead
* memcg.
*
* So when migration transfers the memcg state to a folio on another node,
* re-derive the objcg for the destination node. If the memcg is dying and the
* destination node has already been reparented, the lookup walks up to the
* nearest live ancestor - which is also where that node's LRU lists went.
*
* Returns the objcg to commit to @new, with a reference for the caller.
*/
static struct obj_cgroup *get_migration_objcg(struct folio *old,
struct folio *new)
{
struct obj_cgroup *old_objcg, *new_objcg;
int new_nid = folio_nid(new);
old_objcg = get_obj_cgroup_from_folio(old);
if (folio_nid(old) == new_nid)
return old_objcg;
rcu_read_lock();
new_objcg = __get_obj_cgroup_from_memcg(obj_cgroup_memcg(old_objcg),
new_nid);
rcu_read_unlock();
obj_cgroup_put(old_objcg);
return new_objcg;
}
/**
* mem_cgroup_replace_folio - Charge a folio's replacement.
* @old: Currently circulating folio.
@@ -5365,21 +5407,28 @@ void mem_cgroup_replace_folio(struct folio *old, struct folio *new)
if (folio_memcg_charged(new))
return;
objcg = folio_objcg(old);
VM_WARN_ON_ONCE_FOLIO(!objcg, old);
if (!objcg)
VM_WARN_ON_ONCE_FOLIO(!folio_objcg(old), old);
if (!folio_objcg(old))
return;
objcg = get_migration_objcg(old, new);
rcu_read_lock();
memcg = obj_cgroup_memcg(objcg);
/* Force-charge the new page. The old one will be freed soon */
/*
* Force-charge the new page. The old one will be freed soon.
*
* The rootness of the committed objcg decides whether the final
* uncharge of @new goes through the page counters (see
* uncharge_folio()); charge them only if the uncharge will.
*/
if (!obj_cgroup_is_root(objcg)) {
page_counter_charge(&memcg->memory, nr_pages);
if (do_memsw_account())
page_counter_charge(&memcg->memsw, nr_pages);
}
obj_cgroup_get(objcg);
commit_charge(new, objcg);
memcg1_commit_charge(new, memcg);
rcu_read_unlock();
@@ -5391,14 +5440,15 @@ void mem_cgroup_replace_folio(struct folio *old, struct folio *new)
* @new: Replacement folio.
*
* Transfer the memcg data from the old folio to the new folio for migration.
* The old folio's data info will be cleared. Note that the memory counters
* will remain unchanged throughout the process.
* The old folio's data info will be cleared. The memory counters remain
* unchanged, unless the charge moves out of a fully reparented ancestry
* and has to be settled (see below).
*
* Both folios must be locked, @new->mapping must be set up.
*/
void mem_cgroup_migrate(struct folio *old, struct folio *new)
{
struct obj_cgroup *objcg;
struct obj_cgroup *objcg, *new_objcg;
VM_BUG_ON_FOLIO(!folio_test_locked(old), old);
VM_BUG_ON_FOLIO(!folio_test_locked(new), new);
@@ -5419,12 +5469,30 @@ void mem_cgroup_migrate(struct folio *old, struct folio *new)
if (!objcg)
return;
/* Transfer the charge and the objcg ref */
commit_charge(new, objcg);
new_objcg = get_migration_objcg(old, new);
/*
* @old was charged through a non-root objcg, so its charge is in the
* page counters. If the re-derivation walked up to the root objcg -
* @old's entire ancestry is dying and already reparented - the final
* uncharge of @new will skip the page counters (see uncharge_folio()).
* Settle them now: this is @old's eventual uncharge, moved up to the
* point where its charge record ends.
*/
if (obj_cgroup_is_root(new_objcg) && !obj_cgroup_is_root(objcg)) {
rcu_read_lock();
memcg_uncharge(obj_cgroup_memcg(objcg), folio_nr_pages(old));
rcu_read_unlock();
}
commit_charge(new, new_objcg);
/* Warning should never happen, so don't worry about refcount non-0 */
WARN_ON_ONCE(folio_unqueue_deferred_split(old));
old->memcg_data = 0;
/* @new holds its own reference now, drop @old's */
obj_cgroup_put(objcg);
}
DEFINE_STATIC_KEY_FALSE(memcg_sockets_enabled_key);
+2 -4
View File
@@ -126,6 +126,7 @@ static int walk_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,
pmd = pmd_offset(pud, addr);
do {
again:
walk->action = ACTION_SUBTREE;
next = pmd_addr_end(addr, end);
if (pmd_none(*pmd)) {
if (has_install)
@@ -138,8 +139,6 @@ again:
continue;
}
walk->action = ACTION_SUBTREE;
/*
* This implies that each ->pmd_entry() handler
* needs to know about pmd_trans_huge() pmds
@@ -196,6 +195,7 @@ static int walk_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end,
pud = pud_offset(p4d, addr);
do {
again:
walk->action = ACTION_SUBTREE;
next = pud_addr_end(addr, end);
if (pud_none(*pud)) {
if (has_install)
@@ -208,8 +208,6 @@ static int walk_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end,
continue;
}
walk->action = ACTION_SUBTREE;
if (ops->pud_entry)
err = ops->pud_entry(pud, addr, next, walk);
if (err)
+8 -1
View File
@@ -2202,7 +2202,14 @@ void swap_free_hibernation_slot(swp_entry_t entry)
ci = swap_cluster_lock(si, offset);
__swap_cluster_put_entry(ci, offset % SWAPFILE_CLUSTER);
__swap_cluster_free_entries(si, ci, offset % SWAPFILE_CLUSTER, 1);
/*
* A slot with a folio in the swap cache is freed when the folio
* leaves the cache, the same rule swap_put_entries_cluster() follows.
* Readahead can put a folio here, and freeing the slot now would
* leave that folio with no entry behind it.
*/
if (!swp_tb_is_folio(__swap_table_get(ci, offset % SWAPFILE_CLUSTER)))
__swap_cluster_free_entries(si, ci, offset % SWAPFILE_CLUSTER, 1);
swap_cluster_unlock(ci);
/* In theory readahead might add it to the swap cache by accident */
+1 -1
View File
@@ -5948,7 +5948,7 @@ static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
}
}
cond_resched();
cond_resched_tasks_rcu_qs();
if (nr_reclaimed < nr_to_reclaim || proportional_reclaim)
continue;