This commit is contained in:
Mark Brown
2026-07-31 13:58:38 +01:00
27 changed files with 319 additions and 179 deletions
+4
View File
@@ -171,12 +171,14 @@ Boris Brezillon <bbrezillon@kernel.org> <b.brezillon@overkiz.com>
Boris Brezillon <bbrezillon@kernel.org> <boris.brezillon@bootlin.com>
Boris Brezillon <bbrezillon@kernel.org> <boris.brezillon@free-electrons.com>
Brendan Higgins <brendan.higgins@linux.dev> <brendanhiggins@google.com>
Brendan Jackman <brendan.jackman@linux.dev> <jackmanb@google.com>
Brian Avery <b.avery@hp.com>
Brian Cain <bcain@kernel.org> <brian.cain@oss.qualcomm.com>
Brian Cain <bcain@kernel.org> <bcain@quicinc.com>
Brian King <brking@us.ibm.com>
Brian Silverman <bsilver16384@gmail.com> <brian.silverman@bluerivertech.com>
Bryan Tan <bryan-bt.tan@broadcom.com> <bryantan@vmware.com>
Burak Emir <burak.emir@gmail.com> <bqe@google.com>
Cai Huoqing <cai.huoqing@linux.dev> <caihuoqing@baidu.com>
Casey Connolly <casey.connolly@linaro.org> <caleb.connolly@linaro.org>
Casey Connolly <casey.connolly@linaro.org> <caleb@connolly.tech>
@@ -650,6 +652,7 @@ Nicholas Piggin <npiggin@gmail.com> <npiggin@suse.de>
Nicholas Piggin <npiggin@gmail.com> <nickpiggin@yahoo.com.au>
Nicholas Piggin <npiggin@gmail.com> <piggin@cyberone.com.au>
Nicolas Ferre <nicolas.ferre@microchip.com> <nicolas.ferre@atmel.com>
Nico Pache <nico.pache@linux.dev> <npache@redhat.com>
Nicolas Pitre <nico@fluxnic.net> <nicolas.pitre@linaro.org>
Nicolas Pitre <nico@fluxnic.net> <nico@linaro.org>
Nicolas Saenz Julienne <nsaenz@kernel.org> <nsaenzjulienne@suse.de>
@@ -697,6 +700,7 @@ Paulo Alcantara <pc@manguebit.org> <palcantara@suse.com>
Paulo Alcantara <pc@manguebit.org> <pc@manguebit.com>
Pavankumar Kondeti <quic_pkondeti@quicinc.com> <pkondeti@codeaurora.org>
Peter A Jonsson <pj@ludd.ltu.se>
Peter Collingbourne <peter@pcc.me.uk> <pcc@google.com>
Peter Hilber <peter.hilber@oss.qualcomm.com> <quic_philber@quicinc.com>
Peter Oruba <peter.oruba@amd.com>
Peter Oruba <peter@oruba.de>
+3 -3
View File
@@ -4634,7 +4634,7 @@ F: rust/helpers/cpumask.c
BITMAP API [RUST]
M: Alice Ryhl <aliceryhl@google.com>
M: Burak Emir <bqe@google.com>
M: Burak Emir <burak.emir@gmail.com>
R: Yury Norov <yury.norov@gmail.com>
S: Maintained
F: lib/find_bit_benchmark_rust.rs
@@ -17160,7 +17160,7 @@ M: Andrew Morton <akpm@linux-foundation.org>
M: Vlastimil Babka <vbabka@kernel.org>
R: Suren Baghdasaryan <surenb@google.com>
R: Michal Hocko <mhocko@suse.com>
R: Brendan Jackman <jackmanb@google.com>
R: Brendan Jackman <brendan.jackman@linux.dev>
R: Johannes Weiner <hannes@cmpxchg.org>
R: Zi Yan <ziy@nvidia.com>
L: linux-mm@kvack.org
@@ -17253,7 +17253,7 @@ M: Lorenzo Stoakes <ljs@kernel.org>
R: Zi Yan <ziy@nvidia.com>
R: Baolin Wang <baolin.wang@linux.alibaba.com>
R: Liam R. Howlett <liam@infradead.org>
R: Nico Pache <npache@redhat.com>
R: Nico Pache <nico.pache@linux.dev>
R: Ryan Roberts <ryan.roberts@arm.com>
R: Dev Jain <dev.jain@arm.com>
R: Barry Song <baohua@kernel.org>
-2
View File
@@ -7,8 +7,6 @@
#include <linux/ptdump.h>
DECLARE_STATIC_KEY_FALSE(arm64_ptdump_lock_key);
#ifdef CONFIG_PTDUMP
#include <linux/mm_types.h>
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
// Copyright 2023 Google LLC
// Authors: Ard Biesheuvel <ardb@google.com>
// Peter Collingbourne <pcc@google.com>
// Peter Collingbourne <peter@pcc.me.uk>
#include <linux/elf.h>
#include <linux/init.h>
+4 -39
View File
@@ -49,8 +49,6 @@
#define NO_CONT_MAPPINGS BIT(1)
#define NO_EXEC_MAPPINGS BIT(2) /* assumes FEAT_HPDS is not used */
DEFINE_STATIC_KEY_FALSE(arm64_ptdump_lock_key);
u64 kimage_voffset __ro_after_init;
EXPORT_SYMBOL(kimage_voffset);
@@ -1864,8 +1862,7 @@ int pmd_clear_huge(pmd_t *pmdp)
return 1;
}
static int __pmd_free_pte_page(pmd_t *pmdp, unsigned long addr,
bool acquire_mmap_lock)
int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr)
{
pte_t *table;
pmd_t pmd;
@@ -1877,25 +1874,13 @@ static int __pmd_free_pte_page(pmd_t *pmdp, unsigned long addr,
return 1;
}
/* See comment in pud_free_pmd_page for static key logic */
table = pte_offset_kernel(pmdp, addr);
pmd_clear(pmdp);
__flush_tlb_kernel_pgtable(addr);
if (static_branch_unlikely(&arm64_ptdump_lock_key) && acquire_mmap_lock) {
mmap_read_lock(&init_mm);
mmap_read_unlock(&init_mm);
}
pte_free_kernel(NULL, table);
return 1;
}
int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr)
{
/* If ptdump is walking the pagetables, acquire init_mm.mmap_lock */
return __pmd_free_pte_page(pmdp, addr, /* acquire_mmap_lock = */ true);
}
int pud_free_pmd_page(pud_t *pudp, unsigned long addr)
{
pmd_t *table;
@@ -1911,36 +1896,16 @@ int pud_free_pmd_page(pud_t *pudp, unsigned long addr)
}
table = pmd_offset(pudp, addr);
/*
* Our objective is to prevent ptdump from reading a PMD table which has
* been freed. In this race, if pud_free_pmd_page observes the key on
* (which got flipped by ptdump) then the mmap lock sequence here will,
* as a result of the mmap write lock/unlock sequence in ptdump, give
* us the correct synchronization. If not, this means that ptdump has
* yet not started walking the pagetables - the sequence of barriers
* issued by __flush_tlb_kernel_pgtable() guarantees that ptdump will
* observe an empty PUD.
*/
pud_clear(pudp);
__flush_tlb_kernel_pgtable(addr);
if (static_branch_unlikely(&arm64_ptdump_lock_key)) {
mmap_read_lock(&init_mm);
mmap_read_unlock(&init_mm);
}
pmdp = table;
next = addr;
end = addr + PUD_SIZE;
do {
if (pmd_present(pmdp_get(pmdp)))
/*
* PMD has been isolated, so ptdump won't see it. No
* need to acquire init_mm.mmap_lock.
*/
__pmd_free_pte_page(pmdp, next, /* acquire_mmap_lock = */ false);
pmd_free_pte_page(pmdp, next);
} while (pmdp++, next += PMD_SIZE, next != end);
pud_clear(pudp);
__flush_tlb_kernel_pgtable(addr);
pmd_free(NULL, table);
return 1;
}
+2 -9
View File
@@ -283,13 +283,6 @@ void note_page_flush(struct ptdump_state *pt_st)
note_page(pt_st, 0, -1, pte_val(pte_zero));
}
static void arm64_ptdump_walk_pgd(struct ptdump_state *st, struct mm_struct *mm)
{
static_branch_inc(&arm64_ptdump_lock_key);
ptdump_walk_pgd(st, mm, NULL);
static_branch_dec(&arm64_ptdump_lock_key);
}
void ptdump_walk(struct seq_file *s, struct ptdump_info *info)
{
unsigned long end = ~0UL;
@@ -318,7 +311,7 @@ void ptdump_walk(struct seq_file *s, struct ptdump_info *info)
}
};
arm64_ptdump_walk_pgd(&st.ptdump, info->mm);
ptdump_walk_pgd(&st.ptdump, info->mm, NULL);
}
static void __init ptdump_initialize(void)
@@ -360,7 +353,7 @@ bool ptdump_check_wx(void)
}
};
arm64_ptdump_walk_pgd(&st.ptdump, &init_mm);
ptdump_walk_pgd(&st.ptdump, &init_mm, NULL);
if (st.wx_pages || st.uxn_pages) {
pr_warn("Checked W+X mappings: FAILED, %lu W+X pages found, %lu non-UXN pages found\n",
+2
View File
@@ -39,6 +39,8 @@
#include <asm/processor.h>
.section .data
/* The MMU requires a page aligned page directory. */
.align 12
.global swapper_pg_dir
swapper_pg_dir:
.space PAGE_SIZE
+2 -1
View File
@@ -63,7 +63,8 @@ EXPORT_SYMBOL(phys_ram_base);
#ifdef CONFIG_SPARSEMEM_VMEMMAP
#define VMEMMAP_ADDR_ALIGN max(1ULL << SECTION_SIZE_BITS, \
MAX_FOLIO_VMEMMAP_ALIGN)
PFN_PHYS(MAX_FOLIO_VMEMMAP_ALIGN / \
sizeof(struct page)))
unsigned long vmemmap_start_pfn __ro_after_init;
EXPORT_SYMBOL(vmemmap_start_pfn);
+41 -12
View File
@@ -22,6 +22,7 @@
#include <linux/cc_platform.h>
#include <linux/set_memory.h>
#include <linux/memregion.h>
#include <linux/cleanup.h>
#include <asm/e820/api.h>
#include <asm/processor.h>
@@ -49,7 +50,8 @@ struct cpa_data {
unsigned int flags;
unsigned int force_split : 1,
force_static_prot : 1,
force_flush_all : 1;
force_flush_all : 1,
init_mm_read_locked : 1;
struct page **pages;
};
@@ -410,7 +412,7 @@ static void __cpa_flush_tlb(void *data)
static int collapse_large_pages(unsigned long addr, struct list_head *pgtables);
static void cpa_collapse_large_pages(struct cpa_data *cpa)
static void __cpa_collapse_large_pages(struct cpa_data *cpa)
{
unsigned long start, addr, end;
struct ptdesc *ptdesc, *tmp;
@@ -438,10 +440,30 @@ static void cpa_collapse_large_pages(struct cpa_data *cpa)
list_for_each_entry_safe(ptdesc, tmp, &pgtables, pt_list) {
list_del(&ptdesc->pt_list);
pagetable_free(ptdesc);
/*
* Only early alloc'd direct map should not be flagged PG_table
* here and those shouldn't be collapsed. However be abundantly
* cautious and handle the !PG_table case too.
*/
if (PageTable((ptdesc_page(ptdesc))))
pagetable_dtor_free(ptdesc);
else
pagetable_free(ptdesc);
}
}
static void cpa_collapse_large_pages(struct cpa_data *cpa)
{
/*
* Take the mmap write lock on init_mm to:
* - Avoid a use-after-free if raced by ptdump (which takes its own
* write lock on init_mm).
* - Serialise concurrent CPA walkers.
*/
scoped_guard(mmap_write_lock, &init_mm)
__cpa_collapse_large_pages(cpa);
}
static void cpa_flush(struct cpa_data *cpa, int cache)
{
unsigned int i;
@@ -1125,11 +1147,10 @@ set:
static int
__split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address,
struct ptdesc *ptdesc)
pte_t *pbase)
{
unsigned long lpaddr, lpinc, ref_pfn, pfn, pfninc = 1;
struct page *base = ptdesc_page(ptdesc);
pte_t *pbase = (pte_t *)page_address(base);
struct page *base = virt_to_page(pbase);
unsigned int i, level;
pgprot_t ref_prot;
bool nx, rw;
@@ -1233,18 +1254,22 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address,
static int split_large_page(struct cpa_data *cpa, pte_t *kpte,
unsigned long address)
{
struct ptdesc *ptdesc;
pte_t *pte;
if (!debug_pagealloc_enabled())
spin_unlock(&cpa_lock);
ptdesc = pagetable_alloc(GFP_KERNEL, 0);
if (cpa->init_mm_read_locked)
mmap_read_unlock(&init_mm);
pte = pte_alloc_one_kernel(&init_mm);
if (cpa->init_mm_read_locked)
mmap_read_lock(&init_mm);
if (!debug_pagealloc_enabled())
spin_lock(&cpa_lock);
if (!ptdesc)
if (!pte)
return -ENOMEM;
if (__split_large_page(cpa, kpte, address, ptdesc))
pagetable_free(ptdesc);
if (__split_large_page(cpa, kpte, address, pte))
pte_free_kernel(&init_mm, pte);
return 0;
}
@@ -2109,7 +2134,11 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages,
cpa.curpage = 0;
cpa.force_split = force_split;
ret = __change_page_attr_set_clr(&cpa, 1);
/* Avoid race with concurrent CPA collapse. */
cpa.init_mm_read_locked = true;
scoped_guard(mmap_read_lock, &init_mm)
ret = __change_page_attr_set_clr(&cpa, 1);
cpa.init_mm_read_locked = false;
/*
* Check whether we really changed something:
+3
View File
@@ -621,6 +621,9 @@ static inline void mmap_read_unlock(struct mm_struct *mm)
DEFINE_GUARD(mmap_read_lock, struct mm_struct *,
mmap_read_lock(_T), mmap_read_unlock(_T))
DEFINE_GUARD(mmap_write_lock, struct mm_struct *,
mmap_write_lock(_T), mmap_write_unlock(_T))
DEFINE_GUARD_COND(mmap_read_lock, _try, mmap_read_trylock(_T))
static inline void mmap_read_unlock_non_owner(struct mm_struct *mm)
{
+12 -1
View File
@@ -375,6 +375,8 @@ keep:
while (!list_empty(folio_list)) {
folio = lru_to_folio(folio_list);
list_del(&folio->lru);
node_stat_sub_folio(folio, NR_ISOLATED_ANON +
folio_is_file_lru(folio));
folio_putback_lru(folio);
}
@@ -392,8 +394,17 @@ unsigned long damon_migrate_pages(struct list_head *folio_list, int target_nid)
return nr_migrated;
if (target_nid < 0 || target_nid >= MAX_NUMNODES ||
!node_state(target_nid, N_MEMORY))
!node_state(target_nid, N_MEMORY)) {
while (!list_empty(folio_list)) {
struct folio *folio = lru_to_folio(folio_list);
list_del(&folio->lru);
node_stat_sub_folio(folio, NR_ISOLATED_ANON +
folio_is_file_lru(folio));
folio_putback_lru(folio);
}
return nr_migrated;
}
noreclaim_flag = memalloc_noreclaim_save();
+2
View File
@@ -350,6 +350,8 @@ static unsigned long damon_pa_migrate(struct damon_region *r,
if (!folio_isolate_lru(folio))
goto put_folio;
node_stat_add_folio(folio, NR_ISOLATED_ANON +
folio_is_file_lru(folio));
list_add(&folio->lru, &folio_list);
put_folio:
addr += folio_size(folio);
+2 -1
View File
@@ -649,7 +649,8 @@ static void damos_va_migrate_dests_add(struct folio *folio,
isolate:
if (!folio_isolate_lru(folio))
return;
node_stat_add_folio(folio, NR_ISOLATED_ANON +
folio_is_file_lru(folio));
list_add(&folio->lru, &migration_lists[i]);
}
+6
View File
@@ -931,6 +931,12 @@ unlock:
if (!xas_nomem(&xas, gfp))
break;
/*
* Lock has been dropped: start again with the original index
* and order (but now with the memory reserved by xas_nomem()).
*/
xas_set_order(&xas, index, forder);
}
if (xas_error(&xas))
+131 -74
View File
@@ -41,6 +41,7 @@
#include <linux/pgalloc.h>
#include <linux/pgalloc_tag.h>
#include <linux/pagewalk.h>
#include <linux/cleanup.h>
#include <asm/tlb.h>
#include "internal.h"
@@ -77,9 +78,15 @@ static unsigned long deferred_split_scan(struct shrinker *shrink,
struct shrink_control *sc);
static bool split_underused_thp = true;
static atomic_t huge_zero_refcount;
#define HUGE_ZERO_UNSET_PFN (~0UL)
struct folio *huge_zero_folio __read_mostly;
unsigned long huge_zero_pfn __read_mostly = ~0UL;
unsigned long huge_zero_pfn __read_mostly = HUGE_ZERO_UNSET_PFN;
#ifndef CONFIG_PERSISTENT_HUGE_ZERO_FOLIO
static atomic_t huge_zero_refcount;
static DEFINE_SPINLOCK(huge_zero_lock);
static struct shrinker *huge_zero_folio_shrinker;
#endif
unsigned long huge_anon_orders_always __read_mostly;
unsigned long huge_anon_orders_madvise __read_mostly;
unsigned long huge_anon_orders_inherit __read_mostly;
@@ -221,33 +228,74 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma,
return orders;
}
static bool get_huge_zero_folio(void)
static struct folio *alloc_huge_zero_folio(void)
{
struct folio *zero_folio;
retry:
if (likely(atomic_inc_not_zero(&huge_zero_refcount)))
return true;
zero_folio = folio_alloc((GFP_TRANSHUGE | __GFP_ZERO | __GFP_ZEROTAGS) &
~__GFP_MOVABLE,
HPAGE_PMD_ORDER);
if (!zero_folio) {
count_vm_event(THP_ZERO_PAGE_ALLOC_FAILED);
return false;
return NULL;
}
/* Ensure zero folio won't have large_rmappable flag set. */
folio_clear_large_rmappable(zero_folio);
preempt_disable();
if (cmpxchg(&huge_zero_folio, NULL, zero_folio)) {
preempt_enable();
folio_clear_large_rmappable(zero_folio); /* Explicitly not rmappable. */
return zero_folio;
}
#ifdef CONFIG_PERSISTENT_HUGE_ZERO_FOLIO
static int __init huge_zero_init(void)
{
huge_zero_folio = alloc_huge_zero_folio();
if (!huge_zero_folio) {
pr_warn("Allocating persistent huge zero folio failed\n");
} else {
huge_zero_pfn = folio_pfn(huge_zero_folio);
count_vm_event(THP_ZERO_PAGE_ALLOC);
}
return 0;
}
static void __init huge_zero_shrinker_exit(void)
{
}
struct folio *mm_get_huge_zero_folio(struct mm_struct *mm)
{
return huge_zero_folio;
}
void mm_put_huge_zero_folio(struct mm_struct *mm)
{
}
#else
static bool get_huge_zero_folio(void)
{
struct folio *zero_folio;
/* Paired with atomic_set_release(). */
if (likely(atomic_inc_not_zero(&huge_zero_refcount)))
return true;
zero_folio = alloc_huge_zero_folio();
if (unlikely(!zero_folio))
return false;
/* Paired with critical section in shrink_huge_zero_folio_scan(). */
spin_lock(&huge_zero_lock);
if (huge_zero_folio) {
/* Somebody else already installed it. */
atomic_inc(&huge_zero_refcount);
spin_unlock(&huge_zero_lock);
folio_put(zero_folio);
goto retry;
return true;
}
WRITE_ONCE(huge_zero_folio, zero_folio);
WRITE_ONCE(huge_zero_pfn, folio_pfn(zero_folio));
/* Paired with atomic_inc_not_zero(). +1 for shrinker pin. */
atomic_set_release(&huge_zero_refcount, 2);
spin_unlock(&huge_zero_lock);
/* We take additional reference here. It will be put back by shrinker */
atomic_set(&huge_zero_refcount, 2);
preempt_enable();
count_vm_event(THP_ZERO_PAGE_ALLOC);
return true;
}
@@ -258,14 +306,59 @@ static void put_huge_zero_folio(void)
* Counter should never go to zero here. Only shrinker can put
* last reference.
*/
BUG_ON(atomic_dec_and_test(&huge_zero_refcount));
WARN_ON_ONCE(atomic_dec_and_test(&huge_zero_refcount));
}
static unsigned long shrink_huge_zero_folio_count(struct shrinker *shrink,
struct shrink_control *sc)
{
/* we can free zero page only if last reference remains */
return atomic_read(&huge_zero_refcount) == 1 ? HPAGE_PMD_NR : 0;
}
static unsigned long shrink_huge_zero_folio_scan(struct shrinker *shrink,
struct shrink_control *sc)
{
struct folio *zero_folio;
/* Paired with critical section in get_huge_zero_folio(). */
scoped_guard(spinlock, &huge_zero_lock) {
/* Paired with atomic_inc_not_zero() in get_huge_zero_folio(). */
if (atomic_cmpxchg(&huge_zero_refcount, 1, 0) != 1)
return 0;
zero_folio = huge_zero_folio;
VM_WARN_ON_ONCE(!zero_folio);
WRITE_ONCE(huge_zero_folio, NULL);
WRITE_ONCE(huge_zero_pfn, HUGE_ZERO_UNSET_PFN);
}
folio_put(zero_folio);
return HPAGE_PMD_NR;
}
static int __init huge_zero_init(void)
{
huge_zero_folio_shrinker = shrinker_alloc(0, "thp-zero");
if (!huge_zero_folio_shrinker) {
shrinker_free(deferred_split_shrinker);
list_lru_destroy(&deferred_split_lru);
return -ENOMEM;
}
huge_zero_folio_shrinker->count_objects = shrink_huge_zero_folio_count;
huge_zero_folio_shrinker->scan_objects = shrink_huge_zero_folio_scan;
shrinker_register(huge_zero_folio_shrinker);
return 0;
}
static void __init huge_zero_shrinker_exit(void)
{
shrinker_free(huge_zero_folio_shrinker);
}
struct folio *mm_get_huge_zero_folio(struct mm_struct *mm)
{
if (IS_ENABLED(CONFIG_PERSISTENT_HUGE_ZERO_FOLIO))
return huge_zero_folio;
if (mm_flags_test(MMF_HUGE_ZERO_FOLIO, mm))
return READ_ONCE(huge_zero_folio);
@@ -280,35 +373,10 @@ struct folio *mm_get_huge_zero_folio(struct mm_struct *mm)
void mm_put_huge_zero_folio(struct mm_struct *mm)
{
if (IS_ENABLED(CONFIG_PERSISTENT_HUGE_ZERO_FOLIO))
return;
if (mm_flags_test(MMF_HUGE_ZERO_FOLIO, mm))
put_huge_zero_folio();
}
static unsigned long shrink_huge_zero_folio_count(struct shrinker *shrink,
struct shrink_control *sc)
{
/* we can free zero page only if last reference remains */
return atomic_read(&huge_zero_refcount) == 1 ? HPAGE_PMD_NR : 0;
}
static unsigned long shrink_huge_zero_folio_scan(struct shrinker *shrink,
struct shrink_control *sc)
{
if (atomic_cmpxchg(&huge_zero_refcount, 1, 0) == 1) {
struct folio *zero_folio = xchg(&huge_zero_folio, NULL);
BUG_ON(zero_folio == NULL);
WRITE_ONCE(huge_zero_pfn, ~0UL);
folio_put(zero_folio);
return HPAGE_PMD_NR;
}
return 0;
}
static struct shrinker *huge_zero_folio_shrinker;
#endif /* CONFIG_PERSISTENT_HUGE_ZERO_FOLIO */
#ifdef CONFIG_SYSFS
static ssize_t enabled_show(struct kobject *kobj,
@@ -972,39 +1040,14 @@ static int __init thp_shrinker_init(void)
deferred_split_shrinker->scan_objects = deferred_split_scan;
shrinker_register(deferred_split_shrinker);
if (IS_ENABLED(CONFIG_PERSISTENT_HUGE_ZERO_FOLIO)) {
/*
* Bump the reference of the huge_zero_folio and do not
* initialize the shrinker.
*
* huge_zero_folio will always be NULL on failure. We assume
* that get_huge_zero_folio() will most likely not fail as
* thp_shrinker_init() is invoked early on during boot.
*/
if (!get_huge_zero_folio())
pr_warn("Allocating persistent huge zero folio failed\n");
return 0;
}
huge_zero_folio_shrinker = shrinker_alloc(0, "thp-zero");
if (!huge_zero_folio_shrinker) {
shrinker_free(deferred_split_shrinker);
list_lru_destroy(&deferred_split_lru);
return -ENOMEM;
}
huge_zero_folio_shrinker->count_objects = shrink_huge_zero_folio_count;
huge_zero_folio_shrinker->scan_objects = shrink_huge_zero_folio_scan;
shrinker_register(huge_zero_folio_shrinker);
return 0;
return huge_zero_init();
}
static void __init thp_shrinker_exit(void)
{
shrinker_free(huge_zero_folio_shrinker);
shrinker_free(deferred_split_shrinker);
list_lru_destroy(&deferred_split_lru);
huge_zero_shrinker_exit();
}
static int __init hugepage_init(void)
@@ -4033,7 +4076,7 @@ static int __folio_split(struct folio *folio, unsigned int new_order,
gfp_t gfp;
mapping = folio->mapping;
min_order = mapping_min_folio_order(folio->mapping);
min_order = mapping_min_folio_order(mapping);
if (new_order < min_order) {
ret = -EINVAL;
goto out;
@@ -4047,6 +4090,8 @@ static int __folio_split(struct folio *folio, unsigned int new_order,
goto out;
}
mapping_set_update(&xas, mapping);
if (split_type == SPLIT_TYPE_UNIFORM) {
xas_set_order(&xas, folio->index, new_order);
xas_split_alloc(&xas, folio, old_order, gfp);
@@ -4113,6 +4158,18 @@ fail:
remap_page(folio, 1 << old_order, ttu_flags);
/*
* Drop the mapping while the inode is still pinned. @folio stays
* locked and present in the page cache until the loop below, so
* eviction cannot free the inode yet; @lock_at is not enough, it may
* be a tail beyond EOF that the split already dropped from the page
* cache. Nothing past this point may touch the inode or the mapping.
*/
if (mapping) {
i_mmap_unlock_read(mapping);
mapping = NULL;
}
/*
* Unlock all after-split folios except the one containing
* @lock_at page. If @folio is not split, it will be kept locked.
+4 -1
View File
@@ -34,7 +34,10 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask,
if (!hugetlb_cma_size)
return NULL;
if (hugetlb_cma[nid])
if (!nodemask)
nodemask = &node_states[N_MEMORY];
if (hugetlb_cma[nid] && node_isset(nid, *nodemask))
page = cma_alloc_frozen_compound(hugetlb_cma[nid], order);
if (!page && !(gfp_mask & __GFP_THISNODE)) {
+5 -1
View File
@@ -107,7 +107,11 @@ static inline void memcg1_remove_from_trees(struct mem_cgroup *memcg) {}
static inline void memcg1_soft_limit_reset(struct mem_cgroup *memcg) {}
static inline void memcg1_css_offline(struct mem_cgroup *memcg) {}
static inline bool memcg1_oom_prepare(struct mem_cgroup *memcg, bool *locked) { return true; }
static inline bool memcg1_oom_prepare(struct mem_cgroup *memcg, bool *locked)
{
*locked = false;
return true;
}
static inline void memcg1_oom_finish(struct mem_cgroup *memcg, bool locked) {}
static inline void memcg1_oom_recover(struct mem_cgroup *memcg) {}
+1
View File
@@ -425,6 +425,7 @@ void free_zone_device_folio(struct folio *folio)
mem_cgroup_uncharge(folio);
if (folio_test_anon(folio)) {
mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON, -1);
for (i = 0; i < nr; i++)
__ClearPageAnonExclusive(folio_page(folio, i));
}
+2 -1
View File
@@ -590,7 +590,8 @@ static int __folio_migrate_mapping(struct address_space *mapping,
/* No turning back from here */
newfolio->index = folio->index;
newfolio->mapping = folio->mapping;
if (folio_test_anon(folio) && folio_test_large(folio))
if (folio_test_anon(folio) && folio_test_large(folio) &&
!folio_test_hugetlb(folio))
mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON, 1);
if (folio_test_swapbacked(folio))
__folio_set_swapbacked(newfolio);
+4 -2
View File
@@ -80,7 +80,8 @@ __page_reporting_request(struct page_reporting_dev_info *prdev)
* now we are limiting this to running no more than once every
* couple of seconds.
*/
schedule_delayed_work(&prdev->work, PAGE_REPORTING_DELAY);
queue_delayed_work(system_freezable_wq, &prdev->work,
PAGE_REPORTING_DELAY);
}
/* notify prdev of free page reporting request */
@@ -340,7 +341,8 @@ err_out:
*/
state = atomic_cmpxchg(&prdev->state, state, PAGE_REPORTING_IDLE);
if (state == PAGE_REPORTING_REQUESTED)
schedule_delayed_work(&prdev->work, PAGE_REPORTING_DELAY);
queue_delayed_work(system_freezable_wq, &prdev->work,
PAGE_REPORTING_DELAY);
}
static DEFINE_MUTEX(page_reporting_mutex);

Some files were not shown because too many files have changed in this diff Show More