mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge branch 'mm-hotfixes-unstable' of https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
This commit is contained in:
@@ -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
@@ -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>
|
||||
|
||||
@@ -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,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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
|
||||
@@ -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
@@ -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]);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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) {}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
Reference in New Issue
Block a user