mm: Squashed revert of MGLRU backports
We have previously disabled MGLRU in defconfig, but now the actual changes are causing conflicts with upstream kernel and breaking the build. Rip out motorola's backports to fix it. Revert "mm:vmscan:Update the page scan prioritiy" This reverts commitedb74fec61. Revert "mm: mglru fix on qcom baseline" This reverts commitdc9d750ffe. Revert "FROMLIST: mm: multi-gen LRU: design doc" This reverts commitd3d2daf25e. Revert "FROMLIST: mm: multi-gen LRU: admin guide" This reverts commita764ce0e0e. Revert "FROMLIST: mm: multi-gen LRU: debugfs interface" This reverts commit0ed1bd23b9. Revert "FROMLIST: mm: multi-gen LRU: thrashing prevention" This reverts commit73bbccfe48. Revert "FROMLIST: mm: multi-gen LRU: kill switch" This reverts commita9ef9de5f8. Revert "FROMLIST: mm: multi-gen LRU: optimize multiple memcgs" This reverts commit834be483c2. Revert "FROMLIST: mm: multi-gen LRU: support page table walks" This reverts commit40f2a1fb74. Revert "FROMLIST: mm: multi-gen LRU: exploit locality in rmap" This reverts commit6ad1e20b6f. Revert "FROMLIST: mm: multi-gen LRU: minimal implementation" This reverts commit6a1feddd23. Revert "FROMLIST: mm: multi-gen LRU: groundwork" This reverts commit81b0532ae5. Revert "FROMLIST: mm: x86: add CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG" This reverts commit62f7a9e8c9. Revert "FROMLIST: mm: x86, arm64: add arch_has_hw_pte_young()" This reverts commitd024618471. Revert "BACKPORT: include/linux/mm_inline.h: fold page_lru_base_type() into its sole caller" This reverts commitd527bf664c. Revert "UPSTREAM: mm: VM_BUG_ON lru page flags" This reverts commit044fa650a0. Revert "BACKPORT: mm: add __clear_page_lru_flags() to replace page_off_lru()" This reverts commit7bb98cd997. Revert "BACKPORT: mm/swap.c: don't pass "enum lru_list" to del_page_from_lru_list()" This reverts commitd2b8328404. Revert "UPSTREAM: mm/swap.c: don't pass "enum lru_list" to trace_mm_lru_insertion()" This reverts commit73deefe8e0. Revert "BACKPORT: mm: don't pass "enum lru_list" to lru list addition functions" This reverts commitcf21693b5f. Revert "BACKPORT: include/linux/mm_inline.h: shuffle lru list addition and deletion functions" This reverts commit146fe87b58. Revert "BACKPORT: mm/vmscan.c: use add_page_to_lru_list()" This reverts commitf2635ff884. Revert "BACKPORT: mm: use self-explanatory macros rather than "2"" This reverts commit2acc85af94. Revert "BACKPORT: mm: remove superfluous __ClearPageActive()" This reverts commit9a5dbac637. Revert "BACKPORT: mm/swapcache: support to handle the shadow entries" This reverts commit41f9ce4a59. Revert "UPSTREAM: mm/vmscan.c: use update_lru_size() in update_lru_sizes()" This reverts commit9cce3ee5a3. Revert "UPSTREAM: arm64: cpufeature: introduce helper cpu_has_hw_af()" This reverts commit7360b99ad2. Revert "UPSTREAM: mm: replace list_move_tail() with add_page_to_lru_list_tail()" This reverts commitf1e989bead. Change-Id: I21068ef69f87d4596efc7858882e2726a57e85ec
This commit is contained in:
50 files changed
+193
-4152
No files matched your search
@@ -29,7 +29,6 @@ the Linux memory management.
|
||||
hugetlbpage
|
||||
idle_page_tracking
|
||||
ksm
|
||||
multigen_lru
|
||||
numa_memory_policy
|
||||
pagemap
|
||||
soft-dirty
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
=============
|
||||
Multi-Gen LRU
|
||||
=============
|
||||
The multi-gen LRU is an alternative LRU implementation that optimizes
|
||||
page reclaim and improves performance under memory pressure. Page
|
||||
reclaim decides the kernel's caching policy and ability to overcommit
|
||||
memory. It directly impacts the kswapd CPU usage and RAM efficiency.
|
||||
|
||||
Quick start
|
||||
===========
|
||||
Build the kernel with the following configurations.
|
||||
|
||||
* ``CONFIG_LRU_GEN=y``
|
||||
* ``CONFIG_LRU_GEN_ENABLED=y``
|
||||
|
||||
All set!
|
||||
|
||||
Runtime options
|
||||
===============
|
||||
``/sys/kernel/mm/lru_gen/`` contains stable ABIs described in the
|
||||
following subsections.
|
||||
|
||||
Kill switch
|
||||
-----------
|
||||
``enable`` accepts different values to enable or disable the following
|
||||
components. Its default value depends on ``CONFIG_LRU_GEN_ENABLED``.
|
||||
All the components should be enabled unless some of them have
|
||||
unforeseen side effects. Writing to ``enable`` has no effect when a
|
||||
component is not supported by the hardware, and valid values will be
|
||||
accepted even when the main switch is off.
|
||||
|
||||
====== ===============================================================
|
||||
Values Components
|
||||
====== ===============================================================
|
||||
0x0001 The main switch for the multi-gen LRU.
|
||||
0x0002 Clearing the accessed bit in leaf page table entries in large
|
||||
batches, when MMU sets it (e.g., on x86). This behavior can
|
||||
theoretically worsen lock contention (mmap_lock). If it is
|
||||
disabled, the multi-gen LRU will suffer a minor performance
|
||||
degradation.
|
||||
0x0004 Clearing the accessed bit in non-leaf page table entries as
|
||||
well, when MMU sets it (e.g., on x86). This behavior was not
|
||||
verified on x86 varieties other than Intel and AMD. If it is
|
||||
disabled, the multi-gen LRU will suffer a negligible
|
||||
performance degradation.
|
||||
[yYnN] Apply to all the components above.
|
||||
====== ===============================================================
|
||||
|
||||
E.g.,
|
||||
::
|
||||
|
||||
echo y >/sys/kernel/mm/lru_gen/enabled
|
||||
cat /sys/kernel/mm/lru_gen/enabled
|
||||
0x0007
|
||||
echo 5 >/sys/kernel/mm/lru_gen/enabled
|
||||
cat /sys/kernel/mm/lru_gen/enabled
|
||||
0x0005
|
||||
|
||||
Thrashing prevention
|
||||
--------------------
|
||||
Personal computers are more sensitive to thrashing because it can
|
||||
cause janks (lags when rendering UI) and negatively impact user
|
||||
experience. The multi-gen LRU offers thrashing prevention to the
|
||||
majority of laptop and desktop users who do not have ``oomd``.
|
||||
|
||||
Users can write ``N`` to ``min_ttl_ms`` to prevent the working set of
|
||||
``N`` milliseconds from getting evicted. The OOM killer is triggered
|
||||
if this working set cannot be kept in memory. In other words, this
|
||||
option works as an adjustable pressure relief valve, and when open, it
|
||||
terminates applications that are hopefully not being used.
|
||||
|
||||
Based on the average human detectable lag (~100ms), ``N=1000`` usually
|
||||
eliminates intolerable janks due to thrashing. Larger values like
|
||||
``N=3000`` make janks less noticeable at the risk of premature OOM
|
||||
kills.
|
||||
|
||||
The default value ``0`` means disabled.
|
||||
|
||||
Experimental features
|
||||
=====================
|
||||
``/sys/kernel/debug/lru_gen`` accepts commands described in the
|
||||
following subsections. Multiple command lines are supported, so does
|
||||
concatenation with delimiters ``,`` and ``;``.
|
||||
|
||||
``/sys/kernel/debug/lru_gen_full`` provides additional stats for
|
||||
debugging. ``CONFIG_LRU_GEN_STATS=y`` keeps historical stats from
|
||||
evicted generations in this file.
|
||||
|
||||
Working set estimation
|
||||
----------------------
|
||||
Working set estimation measures how much memory an application
|
||||
requires in a given time interval, and it is usually done with little
|
||||
impact on the performance of the application. E.g., data centers want
|
||||
to optimize job scheduling (bin packing) to improve memory
|
||||
utilizations. When a new job comes in, the job scheduler needs to find
|
||||
out whether each server it manages can allocate a certain amount of
|
||||
memory for this new job before it can pick a candidate. To do so, this
|
||||
job scheduler needs to estimate the working sets of the existing jobs.
|
||||
|
||||
When it is read, ``lru_gen`` returns a histogram of numbers of pages
|
||||
accessed over different time intervals for each memcg and node.
|
||||
``MAX_NR_GENS`` decides the number of bins for each histogram.
|
||||
::
|
||||
|
||||
memcg memcg_id memcg_path
|
||||
node node_id
|
||||
min_gen_nr age_in_ms nr_anon_pages nr_file_pages
|
||||
...
|
||||
max_gen_nr age_in_ms nr_anon_pages nr_file_pages
|
||||
|
||||
Each generation contains an estimated number of pages that have been
|
||||
accessed within ``age_in_ms`` non-cumulatively. E.g., ``min_gen_nr``
|
||||
contains the coldest pages and ``max_gen_nr`` contains the hottest
|
||||
pages, since ``age_in_ms`` of the former is the largest and that of
|
||||
the latter is the smallest.
|
||||
|
||||
Users can write ``+ memcg_id node_id max_gen_nr
|
||||
[can_swap[full_scan]]`` to ``lru_gen`` to create a new generation
|
||||
``max_gen_nr+1``. ``can_swap`` defaults to the swap setting and, if it
|
||||
is set to ``1``, it forces the scan of anon pages when swap is off.
|
||||
``full_scan`` defaults to ``1`` and, if it is set to ``0``, it reduces
|
||||
the overhead as well as the coverage when scanning page tables.
|
||||
|
||||
A typical use case is that a job scheduler writes to ``lru_gen`` at a
|
||||
certain time interval to create new generations, and it ranks the
|
||||
servers it manages based on the sizes of their cold memory defined by
|
||||
this time interval.
|
||||
|
||||
Proactive reclaim
|
||||
-----------------
|
||||
Proactive reclaim induces memory reclaim when there is no memory
|
||||
pressure and usually targets cold memory only. E.g., when a new job
|
||||
comes in, the job scheduler wants to proactively reclaim memory on the
|
||||
server it has selected to improve the chance of successfully landing
|
||||
this new job.
|
||||
|
||||
Users can write ``- memcg_id node_id min_gen_nr [swappiness
|
||||
[nr_to_reclaim]]`` to ``lru_gen`` to evict generations less than or
|
||||
equal to ``min_gen_nr``. Note that ``min_gen_nr`` should be less than
|
||||
``max_gen_nr-1`` as ``max_gen_nr`` and ``max_gen_nr-1`` are not fully
|
||||
aged and therefore cannot be evicted. ``swappiness`` overrides the
|
||||
default value in ``/proc/sys/vm/swappiness``. ``nr_to_reclaim`` limits
|
||||
the number of pages to evict.
|
||||
|
||||
A typical use case is that a job scheduler writes to ``lru_gen``
|
||||
before it tries to land a new job on a server, and if it fails to
|
||||
materialize the cold memory without impacting the existing jobs on
|
||||
this server, it retries on the next server according to the ranking
|
||||
result obtained from the working set estimation step described
|
||||
earlier.
|
||||
@@ -36,7 +36,6 @@ descriptions of data structures and algorithms.
|
||||
hugetlbfs_reserv
|
||||
ksm
|
||||
mmu_notifier
|
||||
multigen_lru
|
||||
numa
|
||||
overcommit-accounting
|
||||
page_migration
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
=============
|
||||
Multi-Gen LRU
|
||||
=============
|
||||
The multi-gen LRU is an alternative LRU implementation that optimizes
|
||||
page reclaim and improves performance under memory pressure. Page
|
||||
reclaim decides the kernel's caching policy and ability to overcommit
|
||||
memory. It directly impacts the kswapd CPU usage and RAM efficiency.
|
||||
|
||||
Design overview
|
||||
===============
|
||||
Objectives
|
||||
----------
|
||||
The design objectives are:
|
||||
|
||||
* Good representation of access recency
|
||||
* Try to profit from spatial locality
|
||||
* Fast paths to make obvious choices
|
||||
* Simple self-correcting heuristics
|
||||
|
||||
The representation of access recency is at the core of all LRU
|
||||
implementations. In the multi-gen LRU, each generation represents a
|
||||
group of pages with similar access recency. Generations establish a
|
||||
common frame of reference and therefore help make better choices,
|
||||
e.g., between different memcgs on a computer or different computers in
|
||||
a data center (for job scheduling).
|
||||
|
||||
Exploiting spatial locality improves efficiency when gathering the
|
||||
accessed bit. A rmap walk targets a single page and does not try to
|
||||
profit from discovering a young PTE. A page table walk can sweep all
|
||||
the young PTEs in an address space, but the address space can be too
|
||||
large to make a profit. The key is to optimize both methods and use
|
||||
them in combination.
|
||||
|
||||
Fast paths reduce code complexity and runtime overhead. Unmapped pages
|
||||
do not require TLB flushes; clean pages do not require writeback.
|
||||
These facts are only helpful when other conditions, e.g., access
|
||||
recency, are similar. With generations as a common frame of reference,
|
||||
additional factors stand out. But obvious choices might not be good
|
||||
choices; thus self-correction is required.
|
||||
|
||||
The benefits of simple self-correcting heuristics are self-evident.
|
||||
Again, with generations as a common frame of reference, this becomes
|
||||
attainable. Specifically, pages in the same generation can be
|
||||
categorized based on additional factors, and a feedback loop can
|
||||
statistically compare the refault percentages across those categories
|
||||
and infer which of them are better choices.
|
||||
|
||||
Assumptions
|
||||
-----------
|
||||
The protection of hot pages and the selection of cold pages are based
|
||||
on page access channels and patterns. There are two access channels:
|
||||
|
||||
* Accesses through page tables
|
||||
* Accesses through file descriptors
|
||||
|
||||
The protection of the former channel is by design stronger because:
|
||||
|
||||
1. The uncertainty in determining the access patterns of the former
|
||||
channel is higher due to the approximation of the accessed bit.
|
||||
2. The cost of evicting the former channel is higher due to the TLB
|
||||
flushes required and the likelihood of encountering the dirty bit.
|
||||
3. The penalty of underprotecting the former channel is higher because
|
||||
applications usually do not prepare themselves for major page
|
||||
faults like they do for blocked I/O. E.g., GUI applications
|
||||
commonly use dedicated I/O threads to avoid blocking the rendering
|
||||
threads.
|
||||
|
||||
There are also two access patterns:
|
||||
|
||||
* Accesses exhibiting temporal locality
|
||||
* Accesses not exhibiting temporal locality
|
||||
|
||||
For the reasons listed above, the former channel is assumed to follow
|
||||
the former pattern unless ``VM_SEQ_READ`` or ``VM_RAND_READ`` is
|
||||
present, and the latter channel is assumed to follow the latter
|
||||
pattern unless outlying refaults have been observed.
|
||||
|
||||
Workflow overview
|
||||
=================
|
||||
Evictable pages are divided into multiple generations for each
|
||||
``lruvec``. The youngest generation number is stored in
|
||||
``lrugen->max_seq`` for both anon and file types as they are aged on
|
||||
an equal footing. The oldest generation numbers are stored in
|
||||
``lrugen->min_seq[]`` separately for anon and file types as clean file
|
||||
pages can be evicted regardless of swap constraints. These three
|
||||
variables are monotonically increasing.
|
||||
|
||||
Generation numbers are truncated into ``order_base_2(MAX_NR_GENS+1)``
|
||||
bits in order to fit into the gen counter in ``page->flags``. Each
|
||||
truncated generation number is an index to ``lrugen->lists[]``. The
|
||||
sliding window technique is used to track at least ``MIN_NR_GENS`` and
|
||||
at most ``MAX_NR_GENS`` generations. The gen counter stores a value
|
||||
within ``[1, MAX_NR_GENS]`` while a page is on one of
|
||||
``lrugen->lists[]``; otherwise it stores zero.
|
||||
|
||||
Each generation is divided into multiple tiers. Tiers represent
|
||||
different ranges of numbers of accesses through file descriptors. A
|
||||
page accessed ``N`` times through file descriptors is in tier
|
||||
``order_base_2(N)``. In contrast to moving across generations, which
|
||||
requires the LRU lock, moving across tiers only requires operations on
|
||||
``page->flags`` and therefore has a negligible cost. A feedback loop
|
||||
modeled after the PID controller monitors refaults over all the tiers
|
||||
from anon and file types and decides which tiers from which types to
|
||||
evict or protect.
|
||||
|
||||
There are two conceptually independent procedures: the aging and the
|
||||
eviction. They form a closed-loop system, i.e., the page reclaim.
|
||||
|
||||
Aging
|
||||
-----
|
||||
The aging produces young generations. Given an ``lruvec``, it
|
||||
increments ``max_seq`` when ``max_seq-min_seq+1`` approaches
|
||||
``MIN_NR_GENS``. The aging promotes hot pages to the youngest
|
||||
generation when it finds them accessed through page tables; the
|
||||
demotion of cold pages happens consequently when it increments
|
||||
``max_seq``. The aging uses page table walks and rmap walks to find
|
||||
young PTEs. For the former, it iterates ``lruvec_memcg()->mm_list``
|
||||
and calls ``walk_page_range()`` with each ``mm_struct`` on this list
|
||||
to scan PTEs. On finding a young PTE, it clears the accessed bit and
|
||||
updates the gen counter of the page mapped by this PTE to
|
||||
``(max_seq%MAX_NR_GENS)+1``. After each iteration of this list, it
|
||||
increments ``max_seq``. For the latter, when the eviction walks the
|
||||
rmap and finds a young PTE, the aging scans the adjacent PTEs and
|
||||
follows the same steps just described.
|
||||
|
||||
Eviction
|
||||
--------
|
||||
The eviction consumes old generations. Given an ``lruvec``, it
|
||||
increments ``min_seq`` when ``lrugen->lists[]`` indexed by
|
||||
``min_seq%MAX_NR_GENS`` becomes empty. To select a type and a tier to
|
||||
evict from, it first compares ``min_seq[]`` to select the older type.
|
||||
If both types are equally old, it selects the one whose first tier has
|
||||
a lower refault percentage. The first tier contains single-use
|
||||
unmapped clean pages, which are the best bet. The eviction sorts a
|
||||
page according to the gen counter if the aging has found this page
|
||||
accessed through page tables and updated the gen counter. It also
|
||||
moves a page to the next generation, i.e., ``min_seq+1``, if this page
|
||||
was accessed multiple times through file descriptors and the feedback
|
||||
loop has detected outlying refaults from the tier this page is in. To
|
||||
do this, the feedback loop uses the first tier as the baseline, for
|
||||
the reason stated earlier.
|
||||
|
||||
Summary
|
||||
-------
|
||||
The multi-gen LRU can be disassembled into the following parts:
|
||||
|
||||
* Generations
|
||||
* Page table walks
|
||||
* Rmap walks
|
||||
* Bloom filters
|
||||
* The PID controller
|
||||
|
||||
The aging and the eviction is a producer-consumer model; specifically,
|
||||
the latter drives the former by the sliding window over generations.
|
||||
Within the aging, rmap walks drive page table walks by inserting hot
|
||||
densely populated page tables to the Bloom filters. Within the
|
||||
eviction, the PID controller uses refaults as the feedback to select
|
||||
types to evict and tiers to protect.
|
||||
@@ -1033,15 +1033,6 @@ config RELR
|
||||
well as compatible NM and OBJCOPY utilities (llvm-nm and llvm-objcopy
|
||||
are compatible).
|
||||
|
||||
config ARCH_HAS_NONLEAF_PMD_YOUNG
|
||||
bool
|
||||
depends on PGTABLE_LEVELS > 2
|
||||
help
|
||||
Architectures that select this option are capable of setting the
|
||||
accessed bit in non-leaf PMD entries when using them as part of linear
|
||||
address translations. Page table walkers that clear the accessed bit
|
||||
may use this capability to reduce their search space.
|
||||
|
||||
source "kernel/gcov/Kconfig"
|
||||
|
||||
source "scripts/gcc-plugins/Kconfig"
|
||||
|
||||
@@ -595,19 +595,6 @@ bool is_spectre_bhb_affected(const struct arm64_cpu_capabilities *entry, int sco
|
||||
u8 spectre_bhb_loop_affected(int scope);
|
||||
void spectre_bhb_enable_mitigation(const struct arm64_cpu_capabilities *__unused);
|
||||
|
||||
/* Check whether hardware update of the Access flag is supported */
|
||||
static inline bool cpu_has_hw_af(void)
|
||||
{
|
||||
u64 mmfr1;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_ARM64_HW_AFDBM))
|
||||
return false;
|
||||
|
||||
mmfr1 = read_cpuid(ID_AA64MMFR1_EL1);
|
||||
return cpuid_feature_extract_unsigned_field(mmfr1,
|
||||
ID_AA64MMFR1_HADBS_SHIFT);
|
||||
}
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif
|
||||
@@ -32,7 +32,6 @@
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/pgtable-types.h>
|
||||
|
||||
#define _PROT_DEFAULT (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED)
|
||||
|
||||
@@ -820,7 +820,6 @@ extern pgd_t tramp_pg_dir[PTRS_PER_PGD];
|
||||
|
||||
extern int kern_addr_valid(unsigned long addr);
|
||||
|
||||
#define arch_has_hw_pte_young cpu_has_hw_af
|
||||
|
||||
void pgd_cache_init(void);
|
||||
#define pgtable_cache_init pgd_cache_init
|
||||
|
||||
@@ -64,7 +64,6 @@ config X86
|
||||
select ARCH_HAS_PMEM_API if X86_64
|
||||
select ARCH_HAS_PTE_SPECIAL
|
||||
select ARCH_HAS_REFCOUNT
|
||||
select ARCH_HAS_NONLEAF_PMD_YOUNG
|
||||
select ARCH_HAS_UACCESS_FLUSHCACHE if X86_64
|
||||
select ARCH_HAS_UACCESS_MCSAFE if X86_64 && X86_MCE
|
||||
select ARCH_HAS_SET_MEMORY
|
||||
|
||||
@@ -826,8 +826,7 @@ static inline pte_t *pte_offset_kernel(pmd_t *pmd, unsigned long address)
|
||||
|
||||
static inline int pmd_bad(pmd_t pmd)
|
||||
{
|
||||
return (pmd_flags(pmd) & ~(_PAGE_USER | _PAGE_ACCESSED)) !=
|
||||
(_KERNPG_TABLE & ~_PAGE_ACCESSED);
|
||||
return (pmd_flags(pmd) & ~_PAGE_USER) != _KERNPG_TABLE;
|
||||
}
|
||||
|
||||
static inline unsigned long pages_to_mb(unsigned long npg)
|
||||
@@ -1443,11 +1442,6 @@ static inline bool arch_has_pfn_modify_check(void)
|
||||
return boot_cpu_has_bug(X86_BUG_L1TF);
|
||||
}
|
||||
|
||||
#define arch_has_hw_pte_young arch_has_hw_pte_young
|
||||
static inline bool arch_has_hw_pte_young(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif /* _ASM_X86_PGTABLE_H */
|
||||
@@ -560,7 +560,7 @@ int ptep_test_and_clear_young(struct vm_area_struct *vma,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG)
|
||||
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||
int pmdp_test_and_clear_young(struct vm_area_struct *vma,
|
||||
unsigned long addr, pmd_t *pmdp)
|
||||
{
|
||||
@@ -572,9 +572,6 @@ int pmdp_test_and_clear_young(struct vm_area_struct *vma,
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||
int pudp_test_and_clear_young(struct vm_area_struct *vma,
|
||||
unsigned long addr, pud_t *pudp)
|
||||
{
|
||||
|
||||
@@ -1034,7 +1034,6 @@ static int exec_mmap(struct mm_struct *mm)
|
||||
active_mm = tsk->active_mm;
|
||||
tsk->active_mm = mm;
|
||||
tsk->mm = mm;
|
||||
lru_gen_add_mm(mm);
|
||||
/*
|
||||
* This prevents preemption while active_mm is being loaded and
|
||||
* it and mm are being updated, which could cause problems for
|
||||
@@ -1047,7 +1046,6 @@ static int exec_mmap(struct mm_struct *mm)
|
||||
activate_mm(active_mm, mm);
|
||||
if (IS_ENABLED(CONFIG_ARCH_WANT_IRQS_OFF_ACTIVATE_MM))
|
||||
local_irq_enable();
|
||||
lru_gen_use_mm(mm);
|
||||
tsk->mm->vmacache_seqnum = 0;
|
||||
vmacache_flush(tsk);
|
||||
task_unlock(tsk);
|
||||
|
||||
+1
-2
@@ -799,8 +799,7 @@ static int fuse_check_page(struct page *page)
|
||||
1 << PG_active |
|
||||
1 << PG_workingset |
|
||||
1 << PG_reclaim |
|
||||
1 << PG_waiters |
|
||||
LRU_GEN_MASK | LRU_REFS_MASK))) {
|
||||
1 << PG_waiters))) {
|
||||
dump_page(page, "fuse: trying to steal weird page");
|
||||
return 1;
|
||||
}
|
||||
|
||||
+1
-14
@@ -429,18 +429,6 @@ static inline void cgroup_put(struct cgroup *cgrp)
|
||||
css_put(&cgrp->self);
|
||||
}
|
||||
|
||||
extern struct mutex cgroup_mutex;
|
||||
|
||||
static inline void cgroup_lock(void)
|
||||
{
|
||||
mutex_lock(&cgroup_mutex);
|
||||
}
|
||||
|
||||
static inline void cgroup_unlock(void)
|
||||
{
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* task_css_set_check - obtain a task's css_set with extra access conditions
|
||||
* @task: the task to obtain css_set for
|
||||
@@ -455,6 +443,7 @@ static inline void cgroup_unlock(void)
|
||||
* as locks used during the cgroup_subsys::attach() methods.
|
||||
*/
|
||||
#ifdef CONFIG_PROVE_RCU
|
||||
extern struct mutex cgroup_mutex;
|
||||
extern spinlock_t css_set_lock;
|
||||
#define task_css_set_check(task, __c) \
|
||||
rcu_dereference_check((task)->cgroups, \
|
||||
@@ -714,8 +703,6 @@ struct cgroup;
|
||||
static inline u64 cgroup_id(struct cgroup *cgrp) { return 1; }
|
||||
static inline void css_get(struct cgroup_subsys_state *css) {}
|
||||
static inline void css_put(struct cgroup_subsys_state *css) {}
|
||||
static inline void cgroup_lock(void) {}
|
||||
static inline void cgroup_unlock(void) {}
|
||||
static inline int cgroup_attach_task_all(struct task_struct *from,
|
||||
struct task_struct *t) { return 0; }
|
||||
static inline int cgroupstats_build(struct cgroupstats *stats,
|
||||
|
||||
@@ -311,11 +311,6 @@ struct mem_cgroup {
|
||||
struct list_head event_list;
|
||||
spinlock_t event_list_lock;
|
||||
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
/* per-memcg mm_struct list */
|
||||
struct lru_gen_mm_list mm_list;
|
||||
#endif
|
||||
|
||||
struct mem_cgroup_per_node *nodeinfo[0];
|
||||
/* WARNING: nodeinfo must be the last member here */
|
||||
};
|
||||
@@ -564,23 +559,6 @@ struct mem_cgroup *lock_page_memcg(struct page *page);
|
||||
void __unlock_page_memcg(struct mem_cgroup *memcg);
|
||||
void unlock_page_memcg(struct page *page);
|
||||
|
||||
/* try to stablize page_memcg() for all the pages in a memcg */
|
||||
static inline bool mem_cgroup_trylock_pages(struct mem_cgroup *memcg)
|
||||
{
|
||||
rcu_read_lock();
|
||||
|
||||
if (mem_cgroup_disabled() || !atomic_read(&memcg->moving_account))
|
||||
return true;
|
||||
|
||||
rcu_read_unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void mem_cgroup_unlock_pages(void)
|
||||
{
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/*
|
||||
* idx can be of type enum memcg_stat_item or node_stat_item.
|
||||
* Keep in sync with memcg_exact_page_state().
|
||||
@@ -1020,18 +998,6 @@ static inline void unlock_page_memcg(struct page *page)
|
||||
{
|
||||
}
|
||||
|
||||
static inline bool mem_cgroup_trylock_pages(struct mem_cgroup *memcg)
|
||||
{
|
||||
/* to match page_memcg_rcu() */
|
||||
rcu_read_lock();
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void mem_cgroup_unlock_pages(void)
|
||||
{
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static inline void mem_cgroup_handle_over_high(void)
|
||||
{
|
||||
}
|
||||
@@ -1136,12 +1102,6 @@ static inline void mem_cgroup_split_huge_fixup(struct page *head)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void __count_memcg_events(struct mem_cgroup *memcg,
|
||||
enum vm_event_item idx,
|
||||
unsigned long count)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void count_memcg_events(struct mem_cgroup *memcg,
|
||||
enum vm_event_item idx,
|
||||
unsigned long count)
|
||||
|
||||
@@ -912,8 +912,6 @@ vm_fault_t finish_mkwrite_fault(struct vm_fault *vmf);
|
||||
#define ZONES_PGOFF (NODES_PGOFF - ZONES_WIDTH)
|
||||
#define LAST_CPUPID_PGOFF (ZONES_PGOFF - LAST_CPUPID_WIDTH)
|
||||
#define KASAN_TAG_PGOFF (LAST_CPUPID_PGOFF - KASAN_TAG_WIDTH)
|
||||
#define LRU_GEN_PGOFF (KASAN_TAG_PGOFF - LRU_GEN_WIDTH)
|
||||
#define LRU_REFS_PGOFF (LRU_GEN_PGOFF - LRU_REFS_WIDTH)
|
||||
|
||||
/*
|
||||
* Define the bit shifts to access each section. For non-existent
|
||||
@@ -1465,8 +1463,6 @@ void unmap_vmas(struct mmu_gather *tlb, struct vm_area_struct *start_vma,
|
||||
* (see the comment on walk_page_range() for more details)
|
||||
*/
|
||||
struct mm_walk {
|
||||
int (*p4d_entry)(p4d_t *p4d, unsigned long addr,
|
||||
unsigned long next, struct mm_walk *walk);
|
||||
int (*pud_entry)(pud_t *pud, unsigned long addr,
|
||||
unsigned long next, struct mm_walk *walk);
|
||||
int (*pmd_entry)(pmd_t *pmd, unsigned long addr,
|
||||
|
||||
+60
-251
@@ -29,8 +29,6 @@ static __always_inline void __update_lru_size(struct lruvec *lruvec,
|
||||
{
|
||||
struct pglist_data *pgdat = lruvec_pgdat(lruvec);
|
||||
|
||||
lockdep_assert_held(&pgdat->lru_lock);
|
||||
|
||||
__mod_node_page_state(pgdat, NR_LRU_BASE + lru, nr_pages);
|
||||
__mod_zone_page_state(&pgdat->node_zones[zid],
|
||||
NR_ZONE_LRU_BASE + lru, nr_pages);
|
||||
@@ -46,22 +44,64 @@ static __always_inline void update_lru_size(struct lruvec *lruvec,
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* __clear_page_lru_flags - clear page lru flags before releasing a page
|
||||
* @page: the page that was on lru and now has a zero reference
|
||||
*/
|
||||
static __always_inline void __clear_page_lru_flags(struct page *page)
|
||||
static __always_inline void add_page_to_lru_list(struct page *page,
|
||||
struct lruvec *lruvec, enum lru_list lru)
|
||||
{
|
||||
VM_BUG_ON_PAGE(!PageLRU(page), page);
|
||||
update_lru_size(lruvec, lru, page_zonenum(page), hpage_nr_pages(page));
|
||||
list_add(&page->lru, &lruvec->lists[lru]);
|
||||
}
|
||||
|
||||
__ClearPageLRU(page);
|
||||
static __always_inline void add_page_to_lru_list_tail(struct page *page,
|
||||
struct lruvec *lruvec, enum lru_list lru)
|
||||
{
|
||||
update_lru_size(lruvec, lru, page_zonenum(page), hpage_nr_pages(page));
|
||||
list_add_tail(&page->lru, &lruvec->lists[lru]);
|
||||
}
|
||||
|
||||
/* this shouldn't happen, so leave the flags to bad_page() */
|
||||
if (PageActive(page) && PageUnevictable(page))
|
||||
return;
|
||||
static __always_inline void del_page_from_lru_list(struct page *page,
|
||||
struct lruvec *lruvec, enum lru_list lru)
|
||||
{
|
||||
list_del(&page->lru);
|
||||
update_lru_size(lruvec, lru, page_zonenum(page), -hpage_nr_pages(page));
|
||||
}
|
||||
|
||||
__ClearPageActive(page);
|
||||
__ClearPageUnevictable(page);
|
||||
/**
|
||||
* page_lru_base_type - which LRU list type should a page be on?
|
||||
* @page: the page to test
|
||||
*
|
||||
* Used for LRU list index arithmetic.
|
||||
*
|
||||
* Returns the base LRU type - file or anon - @page should be on.
|
||||
*/
|
||||
static inline enum lru_list page_lru_base_type(struct page *page)
|
||||
{
|
||||
if (page_is_file_cache(page))
|
||||
return LRU_INACTIVE_FILE;
|
||||
return LRU_INACTIVE_ANON;
|
||||
}
|
||||
|
||||
/**
|
||||
* page_off_lru - which LRU list was page on? clearing its lru flags.
|
||||
* @page: the page to test
|
||||
*
|
||||
* Returns the LRU list a page was on, as an index into the array of LRU
|
||||
* lists; and clears its Unevictable or Active flags, ready for freeing.
|
||||
*/
|
||||
static __always_inline enum lru_list page_off_lru(struct page *page)
|
||||
{
|
||||
enum lru_list lru;
|
||||
|
||||
if (PageUnevictable(page)) {
|
||||
__ClearPageUnevictable(page);
|
||||
lru = LRU_UNEVICTABLE;
|
||||
} else {
|
||||
lru = page_lru_base_type(page);
|
||||
if (PageActive(page)) {
|
||||
__ClearPageActive(page);
|
||||
lru += LRU_ACTIVE;
|
||||
}
|
||||
}
|
||||
return lru;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,247 +115,16 @@ static __always_inline enum lru_list page_lru(struct page *page)
|
||||
{
|
||||
enum lru_list lru;
|
||||
|
||||
VM_BUG_ON_PAGE(PageActive(page) && PageUnevictable(page), page);
|
||||
|
||||
if (PageUnevictable(page))
|
||||
return LRU_UNEVICTABLE;
|
||||
|
||||
lru = page_is_file_cache(page) ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON;
|
||||
if (PageActive(page))
|
||||
lru += LRU_ACTIVE;
|
||||
|
||||
lru = LRU_UNEVICTABLE;
|
||||
else {
|
||||
lru = page_lru_base_type(page);
|
||||
if (PageActive(page))
|
||||
lru += LRU_ACTIVE;
|
||||
}
|
||||
return lru;
|
||||
}
|
||||
|
||||
#define lru_to_page(head) (list_entry((head)->prev, struct page, lru))
|
||||
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
|
||||
static inline bool lru_gen_enabled(void)
|
||||
{
|
||||
#ifdef CONFIG_LRU_GEN_ENABLED
|
||||
DECLARE_STATIC_KEY_TRUE(lru_gen_caps[NR_LRU_GEN_CAPS]);
|
||||
|
||||
return static_branch_likely(&lru_gen_caps[LRU_GEN_CORE]);
|
||||
#else
|
||||
DECLARE_STATIC_KEY_FALSE(lru_gen_caps[NR_LRU_GEN_CAPS]);
|
||||
|
||||
return static_branch_unlikely(&lru_gen_caps[LRU_GEN_CORE]);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool lru_gen_in_fault(void)
|
||||
{
|
||||
return current->in_lru_fault;
|
||||
}
|
||||
|
||||
static inline int lru_gen_from_seq(unsigned long seq)
|
||||
{
|
||||
return seq % MAX_NR_GENS;
|
||||
}
|
||||
|
||||
static inline int lru_hist_from_seq(unsigned long seq)
|
||||
{
|
||||
return seq % NR_HIST_GENS;
|
||||
}
|
||||
|
||||
static inline int lru_tier_from_refs(int refs)
|
||||
{
|
||||
VM_BUG_ON(refs > BIT(LRU_REFS_WIDTH));
|
||||
|
||||
/* see the comment on MAX_NR_TIERS */
|
||||
return order_base_2(refs + 1);
|
||||
}
|
||||
|
||||
static inline bool lru_gen_is_active(struct lruvec *lruvec, int gen)
|
||||
{
|
||||
unsigned long max_seq = lruvec->lrugen.max_seq;
|
||||
|
||||
VM_BUG_ON(gen >= MAX_NR_GENS);
|
||||
|
||||
/* see the comment on MIN_NR_GENS */
|
||||
return gen == lru_gen_from_seq(max_seq) || gen == lru_gen_from_seq(max_seq - 1);
|
||||
}
|
||||
|
||||
static inline void lru_gen_update_size(struct lruvec *lruvec, struct page *page,
|
||||
int old_gen, int new_gen)
|
||||
{
|
||||
int type = page_is_file_cache(page);
|
||||
int zone = page_zonenum(page);
|
||||
int delta = hpage_nr_pages(page);
|
||||
enum lru_list lru = type * LRU_INACTIVE_FILE;
|
||||
struct lru_gen_struct *lrugen = &lruvec->lrugen;
|
||||
|
||||
VM_BUG_ON(old_gen != -1 && old_gen >= MAX_NR_GENS);
|
||||
VM_BUG_ON(new_gen != -1 && new_gen >= MAX_NR_GENS);
|
||||
VM_BUG_ON(old_gen == -1 && new_gen == -1);
|
||||
|
||||
if (old_gen >= 0)
|
||||
WRITE_ONCE(lrugen->nr_pages[old_gen][type][zone],
|
||||
lrugen->nr_pages[old_gen][type][zone] - delta);
|
||||
if (new_gen >= 0)
|
||||
WRITE_ONCE(lrugen->nr_pages[new_gen][type][zone],
|
||||
lrugen->nr_pages[new_gen][type][zone] + delta);
|
||||
|
||||
/* addition */
|
||||
if (old_gen < 0) {
|
||||
if (lru_gen_is_active(lruvec, new_gen))
|
||||
lru += LRU_ACTIVE;
|
||||
update_lru_size(lruvec, lru, zone, delta);
|
||||
return;
|
||||
}
|
||||
|
||||
/* deletion */
|
||||
if (new_gen < 0) {
|
||||
if (lru_gen_is_active(lruvec, old_gen))
|
||||
lru += LRU_ACTIVE;
|
||||
update_lru_size(lruvec, lru, zone, -delta);
|
||||
return;
|
||||
}
|
||||
|
||||
/* promotion */
|
||||
if (!lru_gen_is_active(lruvec, old_gen) && lru_gen_is_active(lruvec, new_gen)) {
|
||||
update_lru_size(lruvec, lru, zone, -delta);
|
||||
update_lru_size(lruvec, lru + LRU_ACTIVE, zone, delta);
|
||||
}
|
||||
|
||||
/* demotion requires isolation, e.g., lru_deactivate_fn() */
|
||||
VM_BUG_ON(lru_gen_is_active(lruvec, old_gen) && !lru_gen_is_active(lruvec, new_gen));
|
||||
}
|
||||
|
||||
static inline bool lru_gen_add_page(struct lruvec *lruvec, struct page *page, bool reclaiming)
|
||||
{
|
||||
int gen;
|
||||
unsigned long old_flags, new_flags;
|
||||
int type = page_is_file_cache(page);
|
||||
int zone = page_zonenum(page);
|
||||
struct lru_gen_struct *lrugen = &lruvec->lrugen;
|
||||
|
||||
if (PageUnevictable(page) || !lrugen->enabled)
|
||||
return false;
|
||||
/*
|
||||
* There are three common cases for this page:
|
||||
* 1. If it's hot, e.g., freshly faulted in or previously hot and
|
||||
* migrated, add it to the youngest generation.
|
||||
* 2. If it's cold but can't be evicted immediately, i.e., an anon page
|
||||
* not in swapcache or a dirty page pending writeback, add it to the
|
||||
* second oldest generation.
|
||||
* 3. Everything else (clean, cold) is added to the oldest generation.
|
||||
*/
|
||||
if (PageActive(page))
|
||||
gen = lru_gen_from_seq(lrugen->max_seq);
|
||||
else if ((type == LRU_GEN_ANON && !PageSwapCache(page)) ||
|
||||
(PageReclaim(page) && (PageDirty(page) || PageWriteback(page))))
|
||||
gen = lru_gen_from_seq(lrugen->min_seq[type] + 1);
|
||||
else
|
||||
gen = lru_gen_from_seq(lrugen->min_seq[type]);
|
||||
|
||||
do {
|
||||
new_flags = old_flags = READ_ONCE(page->flags);
|
||||
VM_BUG_ON_PAGE(new_flags & LRU_GEN_MASK, page);
|
||||
|
||||
/* see the comment on MIN_NR_GENS */
|
||||
new_flags &= ~(LRU_GEN_MASK | BIT(PG_active));
|
||||
new_flags |= (gen + 1UL) << LRU_GEN_PGOFF;
|
||||
} while (cmpxchg(&page->flags, old_flags, new_flags) != old_flags);
|
||||
|
||||
lru_gen_update_size(lruvec, page, -1, gen);
|
||||
/* for rotate_reclaimable_page() */
|
||||
if (reclaiming)
|
||||
list_add_tail(&page->lru, &lrugen->lists[gen][type][zone]);
|
||||
else
|
||||
list_add(&page->lru, &lrugen->lists[gen][type][zone]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool lru_gen_del_page(struct lruvec *lruvec, struct page *page, bool reclaiming)
|
||||
{
|
||||
int gen;
|
||||
unsigned long old_flags, new_flags;
|
||||
|
||||
do {
|
||||
new_flags = old_flags = READ_ONCE(page->flags);
|
||||
if (!(new_flags & LRU_GEN_MASK))
|
||||
return false;
|
||||
|
||||
VM_BUG_ON_PAGE(PageActive(page), page);
|
||||
VM_BUG_ON_PAGE(PageUnevictable(page), page);
|
||||
|
||||
gen = ((new_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
|
||||
|
||||
new_flags &= ~LRU_GEN_MASK;
|
||||
if (!(new_flags & BIT(PG_referenced)))
|
||||
new_flags &= ~(LRU_REFS_MASK | LRU_REFS_FLAGS);
|
||||
/* for shrink_page_list() */
|
||||
if (reclaiming)
|
||||
new_flags &= ~(BIT(PG_referenced) | BIT(PG_reclaim));
|
||||
else if (lru_gen_is_active(lruvec, gen))
|
||||
new_flags |= BIT(PG_active);
|
||||
} while (cmpxchg(&page->flags, old_flags, new_flags) != old_flags);
|
||||
|
||||
lru_gen_update_size(lruvec, page, gen, -1);
|
||||
list_del(&page->lru);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline bool lru_gen_enabled(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool lru_gen_in_fault(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool lru_gen_add_page(struct lruvec *lruvec, struct page *page, bool reclaiming)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool lru_gen_del_page(struct lruvec *lruvec, struct page *page, bool reclaiming)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_LRU_GEN */
|
||||
|
||||
static __always_inline void add_page_to_lru_list(struct page *page,
|
||||
struct lruvec *lruvec)
|
||||
{
|
||||
enum lru_list lru = page_lru(page);
|
||||
|
||||
if (lru_gen_add_page(lruvec, page, false))
|
||||
return;
|
||||
|
||||
update_lru_size(lruvec, lru, page_zonenum(page), hpage_nr_pages(page));
|
||||
list_add(&page->lru, &lruvec->lists[lru]);
|
||||
}
|
||||
|
||||
static __always_inline void add_page_to_lru_list_tail(struct page *page,
|
||||
struct lruvec *lruvec)
|
||||
{
|
||||
enum lru_list lru = page_lru(page);
|
||||
|
||||
if (lru_gen_add_page(lruvec, page, true))
|
||||
return;
|
||||
|
||||
update_lru_size(lruvec, lru, page_zonenum(page), hpage_nr_pages(page));
|
||||
list_add_tail(&page->lru, &lruvec->lists[lru]);
|
||||
}
|
||||
|
||||
static __always_inline void del_page_from_lru_list(struct page *page,
|
||||
struct lruvec *lruvec)
|
||||
{
|
||||
if (lru_gen_del_page(lruvec, page, false))
|
||||
return;
|
||||
|
||||
list_del(&page->lru);
|
||||
update_lru_size(lruvec, page_lru(page), page_zonenum(page),
|
||||
-hpage_nr_pages(page));
|
||||
}
|
||||
#endif
|
||||
@@ -3,7 +3,6 @@
|
||||
#define _LINUX_MM_TYPES_H
|
||||
|
||||
#include <linux/mm_types_task.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include <linux/auxvec.h>
|
||||
#include <linux/list.h>
|
||||
@@ -15,8 +14,6 @@
|
||||
#include <linux/uprobes.h>
|
||||
#include <linux/page-flags-layout.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/nodemask.h>
|
||||
#include <linux/mmdebug.h>
|
||||
#include <linux/android_kabi.h>
|
||||
|
||||
#include <asm/mmu.h>
|
||||
@@ -518,23 +515,6 @@ struct mm_struct {
|
||||
/* HMM needs to track a few things per mm */
|
||||
struct hmm *hmm;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
struct {
|
||||
/* this mm_struct is on lru_gen_mm_list */
|
||||
struct list_head list;
|
||||
#ifdef CONFIG_MEMCG
|
||||
/* points to the memcg of "owner" above */
|
||||
struct mem_cgroup *memcg;
|
||||
#endif
|
||||
/*
|
||||
* Set when switching to this mm_struct, as a hint of
|
||||
* whether it has been used since the last time per-node
|
||||
* page table walkers cleared the corresponding bits.
|
||||
*/
|
||||
nodemask_t nodes;
|
||||
} lru_gen;
|
||||
#endif /* CONFIG_LRU_GEN */
|
||||
} __randomize_layout;
|
||||
|
||||
/*
|
||||
@@ -561,65 +541,6 @@ static inline cpumask_t *mm_cpumask(struct mm_struct *mm)
|
||||
return (struct cpumask *)&mm->cpu_bitmap;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
|
||||
struct lru_gen_mm_list {
|
||||
/* mm_struct list for page table walkers */
|
||||
struct list_head fifo;
|
||||
/* protects the list above */
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
void lru_gen_add_mm(struct mm_struct *mm);
|
||||
void lru_gen_del_mm(struct mm_struct *mm);
|
||||
#ifdef CONFIG_MEMCG
|
||||
void lru_gen_migrate_mm(struct mm_struct *mm);
|
||||
#endif
|
||||
|
||||
static inline void lru_gen_init_mm(struct mm_struct *mm)
|
||||
{
|
||||
INIT_LIST_HEAD(&mm->lru_gen.list);
|
||||
#ifdef CONFIG_MEMCG
|
||||
mm->lru_gen.memcg = NULL;
|
||||
#endif
|
||||
nodes_clear(mm->lru_gen.nodes);
|
||||
}
|
||||
|
||||
static inline void lru_gen_use_mm(struct mm_struct *mm)
|
||||
{
|
||||
/* unlikely but not a bug when racing with lru_gen_migrate_mm() */
|
||||
VM_WARN_ON(list_empty(&mm->lru_gen.list));
|
||||
|
||||
if (!(current->flags & PF_KTHREAD) && !nodes_full(mm->lru_gen.nodes))
|
||||
nodes_setall(mm->lru_gen.nodes);
|
||||
}
|
||||
|
||||
#else /* !CONFIG_LRU_GEN */
|
||||
|
||||
static inline void lru_gen_add_mm(struct mm_struct *mm)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void lru_gen_del_mm(struct mm_struct *mm)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MEMCG
|
||||
static inline void lru_gen_migrate_mm(struct mm_struct *mm)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void lru_gen_init_mm(struct mm_struct *mm)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void lru_gen_use_mm(struct mm_struct *mm)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_LRU_GEN */
|
||||
|
||||
struct mmu_gather;
|
||||
extern void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm,
|
||||
unsigned long start, unsigned long end);
|
||||
|
||||
+6
-230
@@ -232,7 +232,6 @@ static inline int is_active_lru(enum lru_list lru)
|
||||
{
|
||||
return (lru == LRU_ACTIVE_ANON || lru == LRU_ACTIVE_FILE);
|
||||
}
|
||||
#define ANON_AND_FILE 2
|
||||
|
||||
struct zone_reclaim_stat {
|
||||
/*
|
||||
@@ -240,223 +239,13 @@ struct zone_reclaim_stat {
|
||||
* mem/swap backed and file backed pages are referenced.
|
||||
* The higher the rotated/scanned ratio, the more valuable
|
||||
* that cache is.
|
||||
*
|
||||
* The anon LRU stats live in [0], file LRU stats in [1]
|
||||
*/
|
||||
unsigned long recent_rotated[ANON_AND_FILE];
|
||||
unsigned long recent_scanned[ANON_AND_FILE];
|
||||
unsigned long recent_rotated[2];
|
||||
unsigned long recent_scanned[2];
|
||||
};
|
||||
|
||||
#endif /* !__GENERATING_BOUNDS_H */
|
||||
|
||||
/*
|
||||
* Evictable pages are divided into multiple generations. The youngest and the
|
||||
* oldest generation numbers, max_seq and min_seq, are monotonically increasing.
|
||||
* They form a sliding window of a variable size [MIN_NR_GENS, MAX_NR_GENS]. An
|
||||
* offset within MAX_NR_GENS, gen, indexes the LRU list of the corresponding
|
||||
* generation. The gen counter in page->flags stores gen+1 while a page is on
|
||||
* one of lrugen->lists[]. Otherwise it stores 0.
|
||||
*
|
||||
* A page is added to the youngest generation on faulting. The aging needs to
|
||||
* check the accessed bit at least twice before handing this page over to the
|
||||
* eviction. The first check takes care of the accessed bit set on the initial
|
||||
* fault; the second check makes sure this page hasn't been used since then.
|
||||
* This process, AKA second chance, requires a minimum of two generations,
|
||||
* hence MIN_NR_GENS. And to maintain ABI compatibility with the active/inactive
|
||||
* LRU, these two generations are considered active; the rest of generations, if
|
||||
* they exist, are considered inactive. See lru_gen_is_active(). PG_active is
|
||||
* always cleared while a page is on one of lrugen->lists[] so that the aging
|
||||
* needs not to worry about it. And it's set again when a page considered active
|
||||
* is isolated for non-reclaiming purposes, e.g., migration. See
|
||||
* lru_gen_add_page() and lru_gen_del_page().
|
||||
*
|
||||
* MAX_NR_GENS is set to 4 so that the multi-gen LRU can support twice of the
|
||||
* categories of the active/inactive LRU when keeping track of accesses through
|
||||
* page tables. It requires order_base_2(MAX_NR_GENS+1) bits in page->flags.
|
||||
*/
|
||||
#define MIN_NR_GENS 2U
|
||||
#define MAX_NR_GENS 4U
|
||||
|
||||
/*
|
||||
* Each generation is divided into multiple tiers. Tiers represent different
|
||||
* ranges of numbers of accesses through file descriptors. A page accessed N
|
||||
* times through file descriptors is in tier order_base_2(N). A page in the
|
||||
* first tier (N=0,1) is marked by PG_referenced unless it was faulted in
|
||||
* though page tables or read ahead. A page in any other tier (N>1) is marked
|
||||
* by PG_referenced and PG_workingset.
|
||||
*
|
||||
* In contrast to moving across generations which requires the LRU lock, moving
|
||||
* across tiers only requires operations on page->flags and therefore has a
|
||||
* negligible cost in the buffered access path. In the eviction path,
|
||||
* comparisons of refaulted/(evicted+protected) from the first tier and the
|
||||
* rest infer whether pages accessed multiple times through file descriptors
|
||||
* are statistically hot and thus worth protecting.
|
||||
*
|
||||
* MAX_NR_TIERS is set to 4 so that the multi-gen LRU can support twice of the
|
||||
* categories of the active/inactive LRU when keeping track of accesses through
|
||||
* file descriptors. It requires MAX_NR_TIERS-2 additional bits in page->flags.
|
||||
*/
|
||||
#define MAX_NR_TIERS 4U
|
||||
|
||||
#ifndef __GENERATING_BOUNDS_H
|
||||
|
||||
struct lruvec;
|
||||
struct mem_cgroup;
|
||||
struct page_vma_mapped_walk;
|
||||
|
||||
#define LRU_GEN_MASK ((BIT(LRU_GEN_WIDTH) - 1) << LRU_GEN_PGOFF)
|
||||
#define LRU_REFS_MASK ((BIT(LRU_REFS_WIDTH) - 1) << LRU_REFS_PGOFF)
|
||||
#define LRU_REFS_FLAGS (BIT(PG_referenced) | BIT(PG_workingset))
|
||||
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
|
||||
enum {
|
||||
LRU_GEN_ANON,
|
||||
LRU_GEN_FILE,
|
||||
};
|
||||
|
||||
enum {
|
||||
LRU_GEN_CORE,
|
||||
LRU_GEN_MM_WALK,
|
||||
LRU_GEN_NONLEAF_YOUNG,
|
||||
NR_LRU_GEN_CAPS
|
||||
};
|
||||
|
||||
#define MIN_LRU_BATCH BITS_PER_LONG
|
||||
#define MAX_LRU_BATCH (MIN_LRU_BATCH * 128)
|
||||
|
||||
/* whether to keep historical stats from evicted generations */
|
||||
#ifdef CONFIG_LRU_GEN_STATS
|
||||
#define NR_HIST_GENS MAX_NR_GENS
|
||||
#else
|
||||
#define NR_HIST_GENS 1U
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The youngest generation number is stored in max_seq for both anon and file
|
||||
* types as they are aged on an equal footing. The oldest generation numbers are
|
||||
* stored in min_seq[] separately for anon and file types as clean file pages
|
||||
* can be evicted regardless of swap constraints.
|
||||
*
|
||||
* Normally anon and file min_seq are in sync. But if swapping is constrained,
|
||||
* e.g., out of swap space, file min_seq is allowed to advance and leave anon
|
||||
* min_seq behind.
|
||||
*/
|
||||
struct lru_gen_struct {
|
||||
/* the aging increments the youngest generation number */
|
||||
unsigned long max_seq;
|
||||
/* the eviction increments the oldest generation numbers */
|
||||
unsigned long min_seq[ANON_AND_FILE];
|
||||
/* the birth time of each generation in jiffies */
|
||||
unsigned long timestamps[MAX_NR_GENS];
|
||||
/* the multi-gen LRU lists */
|
||||
struct list_head lists[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES];
|
||||
/* the sizes of the above lists */
|
||||
unsigned long nr_pages[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES];
|
||||
/* the exponential moving average of refaulted */
|
||||
unsigned long avg_refaulted[ANON_AND_FILE][MAX_NR_TIERS];
|
||||
/* the exponential moving average of evicted+protected */
|
||||
unsigned long avg_total[ANON_AND_FILE][MAX_NR_TIERS];
|
||||
/* the first tier doesn't need protection, hence the minus one */
|
||||
unsigned long protected[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS - 1];
|
||||
/* can be modified without holding the LRU lock */
|
||||
atomic_long_t evicted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS];
|
||||
atomic_long_t refaulted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS];
|
||||
/* whether the multi-gen LRU is enabled */
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
enum {
|
||||
MM_PTE_TOTAL, /* total leaf entries */
|
||||
MM_PTE_OLD, /* old leaf entries */
|
||||
MM_PTE_YOUNG, /* young leaf entries */
|
||||
MM_PMD_TOTAL, /* total non-leaf entries */
|
||||
MM_PMD_FOUND, /* non-leaf entries found in Bloom filters */
|
||||
MM_PMD_ADDED, /* non-leaf entries added to Bloom filters */
|
||||
NR_MM_STATS
|
||||
};
|
||||
|
||||
/* mnemonic codes for the mm stats above */
|
||||
#define MM_STAT_CODES "toydfa"
|
||||
|
||||
/* double-buffering Bloom filters */
|
||||
#define NR_BLOOM_FILTERS 2
|
||||
|
||||
struct lru_gen_mm_state {
|
||||
/* set to max_seq after each iteration */
|
||||
unsigned long seq;
|
||||
/* where the current iteration starts (inclusive) */
|
||||
struct list_head *head;
|
||||
/* where the last iteration ends (exclusive) */
|
||||
struct list_head *tail;
|
||||
/* to wait for the last page table walker to finish */
|
||||
struct wait_queue_head wait;
|
||||
/* Bloom filters flip after each iteration */
|
||||
unsigned long *filters[NR_BLOOM_FILTERS];
|
||||
/* the mm stats for debugging */
|
||||
unsigned long stats[NR_HIST_GENS][NR_MM_STATS];
|
||||
/* the number of concurrent page table walkers */
|
||||
int nr_walkers;
|
||||
};
|
||||
|
||||
struct lru_gen_mm_walk {
|
||||
/* the lruvec under reclaim */
|
||||
struct lruvec *lruvec;
|
||||
/* unstable max_seq from lru_gen_struct */
|
||||
unsigned long max_seq;
|
||||
/* the next address within an mm to scan */
|
||||
unsigned long next_addr;
|
||||
/* to batch page table entries */
|
||||
unsigned long bitmap[BITS_TO_LONGS(MIN_LRU_BATCH)];
|
||||
/* to batch promoted pages */
|
||||
int nr_pages[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES];
|
||||
/* to batch the mm stats */
|
||||
int mm_stats[NR_MM_STATS];
|
||||
/* total batched items */
|
||||
int batched;
|
||||
bool can_swap;
|
||||
bool full_scan;
|
||||
};
|
||||
|
||||
void lru_gen_init_lruvec(struct lruvec *lruvec);
|
||||
void *lru_gen_eviction(struct page *page);
|
||||
void lru_gen_refault(struct page *page, void *shadow);
|
||||
void lru_gen_look_around(struct page_vma_mapped_walk *pvmw);
|
||||
|
||||
#ifdef CONFIG_MEMCG
|
||||
void lru_gen_init_memcg(struct mem_cgroup *memcg);
|
||||
void lru_gen_exit_memcg(struct mem_cgroup *memcg);
|
||||
#endif
|
||||
|
||||
#else /* !CONFIG_LRU_GEN */
|
||||
|
||||
static inline void lru_gen_init_lruvec(struct lruvec *lruvec)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void *lru_gen_eviction(struct page *page)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void lru_gen_refault(struct page *page, void *shadow)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void lru_gen_look_around(struct page_vma_mapped_walk *pvmw)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MEMCG
|
||||
static inline void lru_gen_init_memcg(struct mem_cgroup *memcg)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void lru_gen_exit_memcg(struct mem_cgroup *memcg)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_LRU_GEN */
|
||||
|
||||
struct lruvec {
|
||||
struct list_head lists[NR_LRU_LISTS];
|
||||
struct zone_reclaim_stat reclaim_stat;
|
||||
@@ -464,12 +253,6 @@ struct lruvec {
|
||||
atomic_long_t inactive_age;
|
||||
/* Refaults at the time of last reclaim cycle */
|
||||
unsigned long refaults;
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
/* evictable pages divided into generations */
|
||||
struct lru_gen_struct lrugen;
|
||||
/* to concurrently iterate lru_gen_mm_list */
|
||||
struct lru_gen_mm_state mm_state;
|
||||
#endif
|
||||
#ifdef CONFIG_MEMCG
|
||||
struct pglist_data *pgdat;
|
||||
#endif
|
||||
@@ -587,8 +370,6 @@ enum zone_type {
|
||||
|
||||
#ifndef __GENERATING_BOUNDS_H
|
||||
|
||||
#define ASYNC_AND_SYNC 2
|
||||
|
||||
struct zone {
|
||||
/* Read-mostly fields */
|
||||
|
||||
@@ -718,8 +499,8 @@ struct zone {
|
||||
#if defined CONFIG_COMPACTION || defined CONFIG_CMA
|
||||
/* pfn where compaction free scanner should start */
|
||||
unsigned long compact_cached_free_pfn;
|
||||
/* pfn where compaction migration scanner should start */
|
||||
unsigned long compact_cached_migrate_pfn[ASYNC_AND_SYNC];
|
||||
/* pfn where async and sync compaction migration scanner should start */
|
||||
unsigned long compact_cached_migrate_pfn[2];
|
||||
unsigned long compact_init_migrate_pfn;
|
||||
unsigned long compact_init_free_pfn;
|
||||
#endif
|
||||
@@ -961,11 +742,6 @@ typedef struct pglist_data {
|
||||
|
||||
unsigned long flags;
|
||||
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
/* kswap mm walk data */
|
||||
struct lru_gen_mm_walk mm_walk;
|
||||
#endif
|
||||
|
||||
ZONE_PADDING(_pad2_)
|
||||
|
||||
/* Per-node vmstats */
|
||||
|
||||
@@ -484,7 +484,6 @@ static inline int num_node_state(enum node_states state)
|
||||
#define first_online_node 0
|
||||
#define first_memory_node 0
|
||||
#define next_online_node(nid) (MAX_NUMNODES)
|
||||
#define next_memory_node(nid) (MAX_NUMNODES)
|
||||
#define nr_node_ids 1
|
||||
#define nr_online_nodes 1
|
||||
|
||||
|
||||
@@ -56,8 +56,7 @@
|
||||
|
||||
#define ZONES_WIDTH ZONES_SHIFT
|
||||
|
||||
#if SECTIONS_WIDTH+ZONES_WIDTH+NODES_SHIFT+LRU_GEN_WIDTH+LRU_REFS_WIDTH \
|
||||
<= BITS_PER_LONG - NR_PAGEFLAGS
|
||||
#if SECTIONS_WIDTH+ZONES_WIDTH+NODES_SHIFT <= BITS_PER_LONG - NR_PAGEFLAGS
|
||||
#define NODES_WIDTH NODES_SHIFT
|
||||
#else
|
||||
#ifdef CONFIG_SPARSEMEM_VMEMMAP
|
||||
@@ -84,15 +83,15 @@
|
||||
#define KASAN_TAG_WIDTH 0
|
||||
#endif
|
||||
|
||||
#if SECTIONS_WIDTH+ZONES_WIDTH+NODES_SHIFT+LAST_CPUPID_SHIFT+KASAN_TAG_WIDTH+ \
|
||||
LRU_GEN_WIDTH+LRU_REFS_WIDTH <= BITS_PER_LONG - NR_PAGEFLAGS
|
||||
#if SECTIONS_WIDTH+ZONES_WIDTH+NODES_SHIFT+LAST_CPUPID_SHIFT+KASAN_TAG_WIDTH \
|
||||
<= BITS_PER_LONG - NR_PAGEFLAGS
|
||||
#define LAST_CPUPID_WIDTH LAST_CPUPID_SHIFT
|
||||
#else
|
||||
#define LAST_CPUPID_WIDTH 0
|
||||
#endif
|
||||
|
||||
#if SECTIONS_WIDTH+ZONES_WIDTH+NODES_WIDTH+LAST_CPUPID_WIDTH+KASAN_TAG_WIDTH+ \
|
||||
LRU_GEN_WIDTH+LRU_REFS_WIDTH > BITS_PER_LONG - NR_PAGEFLAGS
|
||||
#if SECTIONS_WIDTH+NODES_WIDTH+ZONES_WIDTH+LAST_CPUPID_WIDTH+KASAN_TAG_WIDTH \
|
||||
> BITS_PER_LONG - NR_PAGEFLAGS
|
||||
#error "Not enough bits in page flags"
|
||||
#endif
|
||||
|
||||
|
||||
@@ -770,7 +770,7 @@ static inline void ClearPageSlabPfmemalloc(struct page *page)
|
||||
1UL << PG_private | 1UL << PG_private_2 | \
|
||||
1UL << PG_writeback | 1UL << PG_reserved | \
|
||||
1UL << PG_slab | 1UL << PG_active | \
|
||||
1UL << PG_unevictable | __PG_MLOCKED | LRU_GEN_MASK)
|
||||
1UL << PG_unevictable | __PG_MLOCKED)
|
||||
|
||||
/*
|
||||
* Flags checked when a page is prepped for return by the page allocator.
|
||||
@@ -781,7 +781,7 @@ static inline void ClearPageSlabPfmemalloc(struct page *page)
|
||||
* alloc-free cycle to prevent from reusing the page.
|
||||
*/
|
||||
#define PAGE_FLAGS_CHECK_AT_PREP \
|
||||
((((1UL << NR_PAGEFLAGS) - 1) & ~__PG_HWPOISON) | LRU_GEN_MASK | LRU_REFS_MASK)
|
||||
(((1UL << NR_PAGEFLAGS) - 1) & ~__PG_HWPOISON)
|
||||
|
||||
#define PAGE_FLAGS_PRIVATE \
|
||||
(1UL << PG_private | 1UL << PG_private_2)
|
||||
|
||||
+2
-15
@@ -75,7 +75,7 @@ static inline int ptep_test_and_clear_young(struct vm_area_struct *vma,
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_PMDP_TEST_AND_CLEAR_YOUNG
|
||||
#if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG)
|
||||
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||
static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma,
|
||||
unsigned long address,
|
||||
pmd_t *pmdp)
|
||||
@@ -96,7 +96,7 @@ static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma,
|
||||
BUILD_BUG();
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_TRANSPARENT_HUGEPAGE || CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG */
|
||||
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH
|
||||
@@ -122,19 +122,6 @@ static inline int pmdp_clear_flush_young(struct vm_area_struct *vma,
|
||||
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
|
||||
#endif
|
||||
|
||||
#ifndef arch_has_hw_pte_young
|
||||
/*
|
||||
* Return whether the accessed bit is supported on the local CPU.
|
||||
*
|
||||
* This stub assumes accessing through an old PTE triggers a page fault.
|
||||
* Architectures that automatically set the access bit should overwrite it.
|
||||
*/
|
||||
static inline bool arch_has_hw_pte_young(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_PTEP_GET_AND_CLEAR
|
||||
static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
|
||||
unsigned long address,
|
||||
|
||||
@@ -1004,10 +1004,6 @@ struct task_struct {
|
||||
unsigned memcg_kmem_skip_account:1;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
/* whether the LRU algorithm may apply to this access */
|
||||
unsigned in_lru_fault:1;
|
||||
#endif
|
||||
#ifdef CONFIG_COMPAT_BRK
|
||||
unsigned brk_randomized:1;
|
||||
#endif
|
||||
|
||||
+3
-15
@@ -128,10 +128,6 @@ union swap_header {
|
||||
*/
|
||||
struct reclaim_state {
|
||||
unsigned long reclaimed_slab;
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
/* per-thread mm walk data */
|
||||
struct lru_gen_mm_walk *mm_walk;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef __KERNEL__
|
||||
@@ -413,12 +409,9 @@ extern unsigned long total_swapcache_pages(void);
|
||||
extern void show_swap_cache_info(void);
|
||||
extern int add_to_swap(struct page *page);
|
||||
extern int add_to_swap_cache(struct page *, swp_entry_t, gfp_t);
|
||||
extern int __add_to_swap_cache(struct page *page, swp_entry_t entry,
|
||||
void **shadowp);
|
||||
extern void __delete_from_swap_cache(struct page *page, void *shadow);
|
||||
extern int __add_to_swap_cache(struct page *page, swp_entry_t entry);
|
||||
extern void __delete_from_swap_cache(struct page *);
|
||||
extern void delete_from_swap_cache(struct page *);
|
||||
extern void clear_shadow_from_swap_cache(int type, unsigned long begin,
|
||||
unsigned long end);
|
||||
extern void free_page_and_swap_cache(struct page *);
|
||||
extern void free_pages_and_swap_cache(struct page **, int);
|
||||
extern struct page *lookup_swap_cache(swp_entry_t entry,
|
||||
@@ -571,7 +564,7 @@ static inline int add_to_swap_cache(struct page *page, swp_entry_t entry,
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline void __delete_from_swap_cache(struct page *page, void *shadow)
|
||||
static inline void __delete_from_swap_cache(struct page *page)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -579,11 +572,6 @@ static inline void delete_from_swap_cache(struct page *page)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void clear_shadow_from_swap_cache(int type, unsigned long begin,
|
||||
unsigned long end)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int page_swapcount(struct page *page)
|
||||
{
|
||||
return 0;
|
||||
|
||||
@@ -35,15 +35,6 @@ enum vm_event_item { PGPGIN, PGPGOUT, PGPGOUTCLEAN, PSWPIN, PSWPOUT,
|
||||
PGSCAN_KSWAPD,
|
||||
PGSCAN_DIRECT,
|
||||
PGSCAN_DIRECT_THROTTLE,
|
||||
LRU_KSWAPD_NO_PROGRESS,
|
||||
LRU_KSWAPD_ANON,
|
||||
LRU_KSWAPD_FILE,
|
||||
LRU_DIRECT_ANON,
|
||||
LRU_DIRECT_FILE,
|
||||
LRU_KSWAPD_SWAP_FULL,
|
||||
LRU_DIRECT_SWAP_FULL,
|
||||
LRU_NO_GFP_IO,
|
||||
LRU_NO_WRITEPAGE,
|
||||
#ifdef CONFIG_NUMA
|
||||
PGSCAN_ZONE_RECLAIM_FAILED,
|
||||
#endif
|
||||
|
||||
@@ -27,21 +27,24 @@
|
||||
|
||||
TRACE_EVENT(mm_lru_insertion,
|
||||
|
||||
TP_PROTO(struct page *page),
|
||||
TP_PROTO(
|
||||
struct page *page,
|
||||
int lru
|
||||
),
|
||||
|
||||
TP_ARGS(page),
|
||||
TP_ARGS(page, lru),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(struct page *, page )
|
||||
__field(unsigned long, pfn )
|
||||
__field(enum lru_list, lru )
|
||||
__field(int, lru )
|
||||
__field(unsigned long, flags )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->page = page;
|
||||
__entry->pfn = page_to_pfn(page);
|
||||
__entry->lru = page_lru(page);
|
||||
__entry->lru = lru;
|
||||
__entry->flags = trace_pagemap_flags(page);
|
||||
),
|
||||
|
||||
|
||||
@@ -22,13 +22,6 @@ int main(void)
|
||||
DEFINE(NR_CPUS_BITS, ilog2(CONFIG_NR_CPUS));
|
||||
#endif
|
||||
DEFINE(SPINLOCK_SIZE, sizeof(spinlock_t));
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
DEFINE(LRU_GEN_WIDTH, order_base_2(MAX_NR_GENS + 1));
|
||||
DEFINE(LRU_REFS_WIDTH, MAX_NR_TIERS - 2);
|
||||
#else
|
||||
DEFINE(LRU_GEN_WIDTH, 0);
|
||||
DEFINE(LRU_REFS_WIDTH, 0);
|
||||
#endif
|
||||
/* End of constants */
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -144,6 +144,7 @@ struct cgroup_sb_opts {
|
||||
bool none;
|
||||
};
|
||||
|
||||
extern struct mutex cgroup_mutex;
|
||||
extern spinlock_t css_set_lock;
|
||||
extern struct cgroup_subsys *cgroup_subsys[];
|
||||
extern struct list_head cgroup_roots;
|
||||
|
||||
@@ -539,7 +539,6 @@ assign_new_owner:
|
||||
goto retry;
|
||||
}
|
||||
mm->owner = c;
|
||||
lru_gen_migrate_mm(mm);
|
||||
task_unlock(c);
|
||||
put_task_struct(c);
|
||||
}
|
||||
|
||||
@@ -1002,7 +1002,6 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p,
|
||||
goto fail_nocontext;
|
||||
|
||||
mm->user_ns = get_user_ns(user_ns);
|
||||
lru_gen_init_mm(mm);
|
||||
return mm;
|
||||
|
||||
fail_nocontext:
|
||||
@@ -1045,7 +1044,6 @@ static inline void __mmput(struct mm_struct *mm)
|
||||
}
|
||||
if (mm->binfmt)
|
||||
module_put(mm->binfmt->module);
|
||||
lru_gen_del_mm(mm);
|
||||
mmdrop(mm);
|
||||
}
|
||||
|
||||
@@ -2381,13 +2379,6 @@ long _do_fork(struct kernel_clone_args *args)
|
||||
get_task_struct(p);
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_LRU_GEN) && !(clone_flags & CLONE_VM)) {
|
||||
/* lock the task to synchronize with memcg migration */
|
||||
task_lock(p);
|
||||
lru_gen_add_mm(p->mm);
|
||||
task_unlock(p);
|
||||
}
|
||||
|
||||
wake_up_new_task(p);
|
||||
|
||||
/* forking complete and child started to run, tell ptracer */
|
||||
|
||||
@@ -392,6 +392,8 @@ void __put_devmap_managed_page(struct page *page)
|
||||
* holds a reference on the page.
|
||||
*/
|
||||
if (count == 1) {
|
||||
/* Clear Active bit in case of parallel mark_page_accessed */
|
||||
__ClearPageActive(page);
|
||||
__ClearPageWaiters(page);
|
||||
|
||||
mem_cgroup_uncharge(page);
|
||||
|
||||
+1
-3
@@ -3634,10 +3634,8 @@ context_switch(struct rq *rq, struct task_struct *prev,
|
||||
next->active_mm = oldmm;
|
||||
mmgrab(oldmm);
|
||||
enter_lazy_tlb(oldmm, next);
|
||||
} else {
|
||||
} else
|
||||
switch_mm_irqs_off(oldmm, mm, next);
|
||||
lru_gen_use_mm(mm);
|
||||
}
|
||||
|
||||
if (!prev->mm) {
|
||||
prev->active_mm = NULL;
|
||||
|
||||
-26
@@ -842,32 +842,6 @@ config GUP_BENCHMARK
|
||||
config ARCH_HAS_PTE_SPECIAL
|
||||
bool
|
||||
|
||||
# multi-gen LRU {
|
||||
config LRU_GEN
|
||||
bool "Multi-Gen LRU"
|
||||
depends on MMU
|
||||
# the following options can use up the spare bits in page flags
|
||||
depends on !MAXSMP && (64BIT || !SPARSEMEM || SPARSEMEM_VMEMMAP)
|
||||
help
|
||||
A high performance LRU implementation to overcommit memory. See
|
||||
Documentation/admin-guide/mm/multigen_lru.rst for details.
|
||||
|
||||
config LRU_GEN_ENABLED
|
||||
bool "Enable by default"
|
||||
depends on LRU_GEN
|
||||
help
|
||||
This option enables the multi-gen LRU by default.
|
||||
|
||||
config LRU_GEN_STATS
|
||||
bool "Full stats for debugging"
|
||||
depends on LRU_GEN
|
||||
help
|
||||
Do not enable this option unless you plan to look at historical stats
|
||||
from evicted generations for debugging purpose.
|
||||
|
||||
This option has a per-memcg and per-node memory overhead.
|
||||
# }
|
||||
|
||||
endmenu
|
||||
|
||||
config PROCESS_RECLAIM
|
||||
|
||||
+1
-1
@@ -984,7 +984,7 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,
|
||||
VM_BUG_ON_PAGE(PageCompound(page), page);
|
||||
|
||||
/* Successfully isolated */
|
||||
del_page_from_lru_list(page, lruvec);
|
||||
del_page_from_lru_list(page, lruvec, page_lru(page));
|
||||
inc_node_page_state(page,
|
||||
NR_ISOLATED_ANON + page_is_file_cache(page));
|
||||
|
||||
|
||||
+1
-2
@@ -2473,8 +2473,7 @@ static void __split_huge_page_tail(struct page *head, int tail,
|
||||
(1L << PG_workingset) |
|
||||
(1L << PG_locked) |
|
||||
(1L << PG_unevictable) |
|
||||
(1L << PG_dirty) |
|
||||
LRU_GEN_MASK | LRU_REFS_MASK));
|
||||
(1L << PG_dirty)));
|
||||
|
||||
/* ->mapping in first tail page is compound_mapcount */
|
||||
VM_BUG_ON_PAGE(tail > 2 && page_tail->mapping != TAIL_MAPPING,
|
||||
|
||||
+4
-33
@@ -1184,14 +1184,12 @@ void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,
|
||||
*lru_size += nr_pages;
|
||||
|
||||
size = *lru_size;
|
||||
#ifndef CONFIG_LRU_GEN
|
||||
if (WARN_ONCE(size < 0,
|
||||
"%s(%p, %d, %d): lru_size %ld\n",
|
||||
__func__, lruvec, lru, nr_pages, size)) {
|
||||
VM_BUG_ON(1);
|
||||
*lru_size = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (nr_pages > 0)
|
||||
*lru_size += nr_pages;
|
||||
@@ -2401,7 +2399,7 @@ static void lock_page_lru(struct page *page, int *isolated)
|
||||
|
||||
lruvec = mem_cgroup_page_lruvec(page, zone->zone_pgdat);
|
||||
ClearPageLRU(page);
|
||||
del_page_from_lru_list(page, lruvec);
|
||||
del_page_from_lru_list(page, lruvec, page_lru(page));
|
||||
*isolated = 1;
|
||||
} else
|
||||
*isolated = 0;
|
||||
@@ -2417,7 +2415,7 @@ static void unlock_page_lru(struct page *page, int isolated)
|
||||
lruvec = mem_cgroup_page_lruvec(page, zone->zone_pgdat);
|
||||
VM_BUG_ON_PAGE(PageLRU(page), page);
|
||||
SetPageLRU(page);
|
||||
add_page_to_lru_list(page, lruvec);
|
||||
add_page_to_lru_list(page, lruvec, page_lru(page));
|
||||
}
|
||||
spin_unlock_irq(zone_lru_lock(zone));
|
||||
}
|
||||
@@ -2449,7 +2447,6 @@ static void commit_charge(struct page *page, struct mem_cgroup *memcg,
|
||||
*
|
||||
* - a page cache insertion, a swapin fault, or a migration
|
||||
* have the page locked
|
||||
* - mem_cgroup_trylock_pages()
|
||||
*/
|
||||
page->mem_cgroup = memcg;
|
||||
|
||||
@@ -3526,8 +3523,8 @@ static int memcg_stat_show(struct seq_file *m, void *v)
|
||||
pg_data_t *pgdat;
|
||||
struct mem_cgroup_per_node *mz;
|
||||
struct zone_reclaim_stat *rstat;
|
||||
unsigned long recent_rotated[ANON_AND_FILE] = {};
|
||||
unsigned long recent_scanned[ANON_AND_FILE] = {};
|
||||
unsigned long recent_rotated[2] = {0, 0};
|
||||
unsigned long recent_scanned[2] = {0, 0};
|
||||
|
||||
for_each_online_pgdat(pgdat) {
|
||||
mz = mem_cgroup_nodeinfo(memcg, pgdat->node_id);
|
||||
@@ -4525,7 +4522,6 @@ static void __mem_cgroup_free(struct mem_cgroup *memcg)
|
||||
|
||||
static void mem_cgroup_free(struct mem_cgroup *memcg)
|
||||
{
|
||||
lru_gen_exit_memcg(memcg);
|
||||
memcg_wb_domain_exit(memcg);
|
||||
__mem_cgroup_free(memcg);
|
||||
}
|
||||
@@ -4576,7 +4572,6 @@ static struct mem_cgroup *mem_cgroup_alloc(void)
|
||||
INIT_LIST_HEAD(&memcg->cgwb_list);
|
||||
#endif
|
||||
idr_replace(&mem_cgroup_idr, memcg, memcg->id.id);
|
||||
lru_gen_init_memcg(memcg);
|
||||
return memcg;
|
||||
fail:
|
||||
mem_cgroup_id_remove(memcg);
|
||||
@@ -5451,29 +5446,6 @@ static void mem_cgroup_move_task(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
static void mem_cgroup_attach(struct cgroup_taskset *tset)
|
||||
{
|
||||
struct cgroup_subsys_state *css;
|
||||
struct task_struct *task = NULL;
|
||||
|
||||
cgroup_taskset_for_each_leader(task, css, tset)
|
||||
break;
|
||||
|
||||
if (!task)
|
||||
return;
|
||||
|
||||
task_lock(task);
|
||||
if (task->mm && task->mm->owner == task)
|
||||
lru_gen_migrate_mm(task->mm);
|
||||
task_unlock(task);
|
||||
}
|
||||
#else
|
||||
static void mem_cgroup_attach(struct cgroup_taskset *tset)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_LRU_GEN */
|
||||
|
||||
/*
|
||||
* Cgroup retains root cgroups across [un]mount cycles making it necessary
|
||||
* to verify whether we're attached to the default hierarchy on each mount
|
||||
@@ -5843,7 +5815,6 @@ struct cgroup_subsys memory_cgrp_subsys = {
|
||||
.css_free = mem_cgroup_css_free,
|
||||
.css_reset = mem_cgroup_css_reset,
|
||||
.can_attach = mem_cgroup_can_attach,
|
||||
.attach = mem_cgroup_attach,
|
||||
.cancel_attach = mem_cgroup_cancel_attach,
|
||||
.post_attach = mem_cgroup_move_task,
|
||||
.bind = mem_cgroup_bind,
|
||||
|
||||
+1
-49
@@ -40,7 +40,6 @@
|
||||
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mm_inline.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/sched/coredump.h>
|
||||
#include <linux/sched/numa_balancing.h>
|
||||
@@ -2845,47 +2844,6 @@ void unmap_mapping_range(struct address_space *mapping,
|
||||
}
|
||||
EXPORT_SYMBOL(unmap_mapping_range);
|
||||
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
static void lru_gen_enter_fault(struct vm_area_struct *vma)
|
||||
{
|
||||
/* the LRU algorithm doesn't apply to sequential or random reads */
|
||||
current->in_lru_fault = !(vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ));
|
||||
}
|
||||
|
||||
static void lru_gen_exit_fault(void)
|
||||
{
|
||||
current->in_lru_fault = false;
|
||||
}
|
||||
|
||||
static void lru_gen_swap_refault(struct page *page, swp_entry_t entry)
|
||||
{
|
||||
void *item;
|
||||
struct address_space *mapping = swap_address_space(entry);
|
||||
pgoff_t index = swp_offset(entry);
|
||||
|
||||
if (!lru_gen_enabled())
|
||||
return;
|
||||
|
||||
rcu_read_lock();
|
||||
item = radix_tree_lookup(&mapping->i_pages, index);
|
||||
rcu_read_unlock();
|
||||
if (radix_tree_exceptional_entry(item))
|
||||
lru_gen_refault(page, item);
|
||||
}
|
||||
#else
|
||||
static void lru_gen_enter_fault(struct vm_area_struct *vma)
|
||||
{
|
||||
}
|
||||
|
||||
static void lru_gen_exit_fault(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void lru_gen_swap_refault(struct page *page, swp_entry_t entry)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_LRU_GEN */
|
||||
|
||||
/*
|
||||
* We enter with non-exclusive mmap_sem (to exclude vma changes,
|
||||
* but allow concurrent faults), and pte mapped but not yet locked.
|
||||
@@ -2958,7 +2916,6 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
|
||||
__SetPageLocked(page);
|
||||
__SetPageSwapBacked(page);
|
||||
set_page_private(page, entry.val);
|
||||
lru_gen_swap_refault(page, entry);
|
||||
lru_cache_add_anon(page);
|
||||
swap_readpage(page, true);
|
||||
}
|
||||
@@ -3073,8 +3030,7 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
|
||||
} else {
|
||||
do_page_add_anon_rmap(page, vma, vmf->address, exclusive);
|
||||
mem_cgroup_commit_charge(page, memcg, true, false);
|
||||
if (!lru_gen_enabled())
|
||||
activate_page(page);
|
||||
activate_page(page);
|
||||
}
|
||||
|
||||
swap_free(entry);
|
||||
@@ -4227,15 +4183,11 @@ vm_fault_t handle_mm_fault(struct vm_area_struct *vma, unsigned long address,
|
||||
if (flags & FAULT_FLAG_USER)
|
||||
mem_cgroup_enter_user_fault();
|
||||
|
||||
lru_gen_enter_fault(vma);
|
||||
|
||||
if (unlikely(is_vm_hugetlb_page(vma)))
|
||||
ret = hugetlb_fault(vma->vm_mm, vma, address, flags);
|
||||
else
|
||||
ret = __handle_mm_fault(vma, address, flags);
|
||||
|
||||
lru_gen_exit_fault();
|
||||
|
||||
if (flags & FAULT_FLAG_USER) {
|
||||
mem_cgroup_exit_user_fault();
|
||||
/*
|
||||
|
||||
+1
-1
@@ -115,7 +115,7 @@ static bool __munlock_isolate_lru_page(struct page *page, bool getpage)
|
||||
if (getpage)
|
||||
get_page(page);
|
||||
ClearPageLRU(page);
|
||||
del_page_from_lru_list(page, lruvec);
|
||||
del_page_from_lru_list(page, lruvec, page_lru(page));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+2
-6
@@ -66,17 +66,13 @@ void __init mminit_verify_pageflags_layout(void)
|
||||
unsigned long or_mask, add_mask;
|
||||
|
||||
shift = 8 * sizeof(unsigned long);
|
||||
width = shift - SECTIONS_WIDTH - NODES_WIDTH - ZONES_WIDTH
|
||||
- LAST_CPUPID_SHIFT - KASAN_TAG_WIDTH - LRU_GEN_WIDTH - LRU_REFS_WIDTH;
|
||||
width = shift - SECTIONS_WIDTH - NODES_WIDTH - ZONES_WIDTH - LAST_CPUPID_SHIFT;
|
||||
mminit_dprintk(MMINIT_TRACE, "pageflags_layout_widths",
|
||||
"Section %d Node %d Zone %d Lastcpupid %d Kasantag %d Gen %d Tier %d Flags %d\n",
|
||||
"Section %d Node %d Zone %d Lastcpupid %d Flags %d\n",
|
||||
SECTIONS_WIDTH,
|
||||
NODES_WIDTH,
|
||||
ZONES_WIDTH,
|
||||
LAST_CPUPID_WIDTH,
|
||||
KASAN_TAG_WIDTH,
|
||||
LRU_GEN_WIDTH,
|
||||
LRU_REFS_WIDTH,
|
||||
NR_PAGEFLAGS);
|
||||
mminit_dprintk(MMINIT_TRACE, "pageflags_layout_shifts",
|
||||
"Section %d Node %d Zone %d Lastcpupid %d\n",
|
||||
|
||||
@@ -95,8 +95,6 @@ void lruvec_init(struct lruvec *lruvec)
|
||||
|
||||
for_each_lru(lru)
|
||||
INIT_LIST_HEAD(&lruvec->lists[lru]);
|
||||
|
||||
lru_gen_init_lruvec(lruvec);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NUMA_BALANCING) && !defined(LAST_CPUPID_NOT_IN_PAGE_FLAGS)
|
||||
|
||||
+1
-1
@@ -4257,7 +4257,7 @@ static int
|
||||
__perform_reclaim(gfp_t gfp_mask, unsigned int order,
|
||||
const struct alloc_context *ac)
|
||||
{
|
||||
struct reclaim_state reclaim_state = {};
|
||||
struct reclaim_state reclaim_state;
|
||||
int progress;
|
||||
unsigned int noreclaim_flag;
|
||||
unsigned long pflags;
|
||||
|
||||
+1
-6
@@ -131,11 +131,6 @@ static int walk_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
if (walk->p4d_entry) {
|
||||
err = walk->p4d_entry(p4d, addr, next, walk);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
if (walk->pmd_entry || walk->pte_entry)
|
||||
err = walk_pud_range(p4d, addr, next, walk);
|
||||
if (err)
|
||||
@@ -162,7 +157,7 @@ static int walk_pgd_range(unsigned long addr, unsigned long end,
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
if (walk->p4d_entry || walk->pmd_entry || walk->pte_entry)
|
||||
if (walk->pmd_entry || walk->pte_entry)
|
||||
err = walk_p4d_range(pgd, addr, next, walk);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@@ -65,7 +65,6 @@
|
||||
#include <linux/page_idle.h>
|
||||
#include <linux/memremap.h>
|
||||
#include <linux/userfaultfd_k.h>
|
||||
#include <linux/mm_inline.h>
|
||||
|
||||
#include <asm/tlbflush.h>
|
||||
|
||||
@@ -773,12 +772,6 @@ static bool page_referenced_one(struct page *page, struct vm_area_struct *vma,
|
||||
}
|
||||
|
||||
if (pvmw.pte) {
|
||||
if (lru_gen_enabled() && pte_young(*pvmw.pte) &&
|
||||
!(vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ))) {
|
||||
lru_gen_look_around(&pvmw);
|
||||
referenced++;
|
||||
}
|
||||
|
||||
if (ptep_clear_flush_young_notify(vma, address,
|
||||
pvmw.pte)) {
|
||||
/*
|
||||
|
||||
@@ -65,8 +65,9 @@ static void __page_cache_release(struct page *page)
|
||||
|
||||
spin_lock_irqsave(zone_lru_lock(zone), flags);
|
||||
lruvec = mem_cgroup_page_lruvec(page, zone->zone_pgdat);
|
||||
del_page_from_lru_list(page, lruvec);
|
||||
__clear_page_lru_flags(page);
|
||||
VM_BUG_ON_PAGE(!PageLRU(page), page);
|
||||
__ClearPageLRU(page);
|
||||
del_page_from_lru_list(page, lruvec, page_off_lru(page));
|
||||
spin_unlock_irqrestore(zone_lru_lock(zone), flags);
|
||||
}
|
||||
__ClearPageWaiters(page);
|
||||
@@ -220,9 +221,9 @@ static void pagevec_move_tail_fn(struct page *page, struct lruvec *lruvec,
|
||||
int *pgmoved = arg;
|
||||
|
||||
if (PageLRU(page) && !PageUnevictable(page)) {
|
||||
del_page_from_lru_list(page, lruvec);
|
||||
del_page_from_lru_list(page, lruvec, page_lru(page));
|
||||
ClearPageActive(page);
|
||||
add_page_to_lru_list_tail(page, lruvec);
|
||||
add_page_to_lru_list_tail(page, lruvec, page_lru(page));
|
||||
(*pgmoved)++;
|
||||
}
|
||||
}
|
||||
@@ -275,10 +276,12 @@ static void __activate_page(struct page *page, struct lruvec *lruvec,
|
||||
{
|
||||
if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) {
|
||||
int file = page_is_file_cache(page);
|
||||
int lru = page_lru_base_type(page);
|
||||
|
||||
del_page_from_lru_list(page, lruvec);
|
||||
del_page_from_lru_list(page, lruvec, lru);
|
||||
SetPageActive(page);
|
||||
add_page_to_lru_list(page, lruvec);
|
||||
lru += LRU_ACTIVE;
|
||||
add_page_to_lru_list(page, lruvec, lru);
|
||||
trace_mm_lru_activate(page);
|
||||
|
||||
__count_vm_event(PGACTIVATE);
|
||||
@@ -356,43 +359,6 @@ static void __lru_cache_activate_page(struct page *page)
|
||||
put_cpu_var(lru_add_pvec);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
static void page_inc_refs(struct page *page)
|
||||
{
|
||||
unsigned long refs;
|
||||
unsigned long old_flags, new_flags;
|
||||
|
||||
if (PageUnevictable(page))
|
||||
return;
|
||||
|
||||
/* see the comment on MAX_NR_TIERS */
|
||||
do {
|
||||
new_flags = old_flags = READ_ONCE(page->flags);
|
||||
|
||||
if (!(new_flags & BIT(PG_referenced))) {
|
||||
new_flags |= BIT(PG_referenced);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(new_flags & BIT(PG_workingset))) {
|
||||
new_flags |= BIT(PG_workingset);
|
||||
continue;
|
||||
}
|
||||
|
||||
refs = new_flags & LRU_REFS_MASK;
|
||||
refs = min(refs + BIT(LRU_REFS_PGOFF), LRU_REFS_MASK);
|
||||
|
||||
new_flags &= ~LRU_REFS_MASK;
|
||||
new_flags |= refs;
|
||||
} while (new_flags != old_flags &&
|
||||
cmpxchg(&page->flags, old_flags, new_flags) != old_flags);
|
||||
}
|
||||
#else
|
||||
static void page_inc_refs(struct page *page)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_LRU_GEN */
|
||||
|
||||
/*
|
||||
* Mark a page as having seen activity.
|
||||
*
|
||||
@@ -406,12 +372,6 @@ static void page_inc_refs(struct page *page)
|
||||
void mark_page_accessed(struct page *page)
|
||||
{
|
||||
page = compound_head(page);
|
||||
|
||||
if (lru_gen_enabled()) {
|
||||
page_inc_refs(page);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PageActive(page) && !PageUnevictable(page) &&
|
||||
PageReferenced(page)) {
|
||||
|
||||
@@ -440,11 +400,6 @@ static void __lru_cache_add(struct page *page)
|
||||
{
|
||||
struct pagevec *pvec = &get_cpu_var(lru_add_pvec);
|
||||
|
||||
/* see the comment in lru_gen_add_page() */
|
||||
if (lru_gen_enabled() && !PageUnevictable(page) && !PageActive(page) &&
|
||||
lru_gen_in_fault() && !(current->flags & PF_MEMALLOC))
|
||||
SetPageActive(page);
|
||||
|
||||
get_page(page);
|
||||
if (!pagevec_add(pvec, page) || PageCompound(page))
|
||||
__pagevec_lru_add(pvec);
|
||||
@@ -501,10 +456,9 @@ void lru_cache_add_active_or_unevictable(struct page *page,
|
||||
{
|
||||
VM_BUG_ON_PAGE(PageLRU(page), page);
|
||||
|
||||
if (likely((vma->vm_flags & (VM_LOCKED | VM_SPECIAL)) != VM_LOCKED)) {
|
||||
if (!lru_gen_enabled())
|
||||
SetPageActive(page);
|
||||
} else if (!TestSetPageMlocked(page)) {
|
||||
if (likely((vma->vm_flags & (VM_LOCKED | VM_SPECIAL)) != VM_LOCKED))
|
||||
SetPageActive(page);
|
||||
else if (!TestSetPageMlocked(page)) {
|
||||
/*
|
||||
* We use the irq-unsafe __mod_zone_page_stat because this
|
||||
* counter is not modified from interrupt context, and the pte
|
||||
@@ -541,7 +495,7 @@ void lru_cache_add_active_or_unevictable(struct page *page,
|
||||
static void lru_deactivate_file_fn(struct page *page, struct lruvec *lruvec,
|
||||
void *arg)
|
||||
{
|
||||
int file;
|
||||
int lru, file;
|
||||
bool active;
|
||||
|
||||
if (!PageLRU(page))
|
||||
@@ -556,10 +510,12 @@ static void lru_deactivate_file_fn(struct page *page, struct lruvec *lruvec,
|
||||
|
||||
active = PageActive(page);
|
||||
file = page_is_file_cache(page);
|
||||
lru = page_lru_base_type(page);
|
||||
|
||||
del_page_from_lru_list(page, lruvec);
|
||||
del_page_from_lru_list(page, lruvec, lru + active);
|
||||
ClearPageActive(page);
|
||||
ClearPageReferenced(page);
|
||||
add_page_to_lru_list(page, lruvec, lru);
|
||||
|
||||
if (PageWriteback(page) || PageDirty(page)) {
|
||||
/*
|
||||
@@ -567,14 +523,13 @@ static void lru_deactivate_file_fn(struct page *page, struct lruvec *lruvec,
|
||||
* It can make readahead confusing. But race window
|
||||
* is _really_ small and it's non-critical problem.
|
||||
*/
|
||||
add_page_to_lru_list(page, lruvec);
|
||||
SetPageReclaim(page);
|
||||
} else {
|
||||
/*
|
||||
* The page's writeback ends up during pagevec
|
||||
* We moves tha page into tail of inactive.
|
||||
*/
|
||||
add_page_to_lru_list_tail(page, lruvec);
|
||||
list_move_tail(&page->lru, &lruvec->lists[lru]);
|
||||
__count_vm_event(PGROTATED);
|
||||
}
|
||||
|
||||
@@ -589,7 +544,10 @@ static void lru_lazyfree_fn(struct page *page, struct lruvec *lruvec,
|
||||
{
|
||||
if (PageLRU(page) && PageAnon(page) && PageSwapBacked(page) &&
|
||||
!PageSwapCache(page) && !PageUnevictable(page)) {
|
||||
del_page_from_lru_list(page, lruvec);
|
||||
bool active = PageActive(page);
|
||||
|
||||
del_page_from_lru_list(page, lruvec,
|
||||
LRU_INACTIVE_ANON + active);
|
||||
ClearPageActive(page);
|
||||
ClearPageReferenced(page);
|
||||
/*
|
||||
@@ -598,7 +556,7 @@ static void lru_lazyfree_fn(struct page *page, struct lruvec *lruvec,
|
||||
* pages
|
||||
*/
|
||||
ClearPageSwapBacked(page);
|
||||
add_page_to_lru_list(page, lruvec);
|
||||
add_page_to_lru_list(page, lruvec, LRU_INACTIVE_FILE);
|
||||
|
||||
__count_vm_events(PGLAZYFREE, hpage_nr_pages(page));
|
||||
count_memcg_page_event(page, PGLAZYFREE);
|
||||
@@ -824,10 +782,13 @@ void release_pages(struct page **pages, int nr)
|
||||
}
|
||||
|
||||
lruvec = mem_cgroup_page_lruvec(page, locked_pgdat);
|
||||
del_page_from_lru_list(page, lruvec);
|
||||
__clear_page_lru_flags(page);
|
||||
VM_BUG_ON_PAGE(!PageLRU(page), page);
|
||||
__ClearPageLRU(page);
|
||||
del_page_from_lru_list(page, lruvec, page_off_lru(page));
|
||||
}
|
||||
|
||||
/* Clear Active bit in case of parallel mark_page_accessed */
|
||||
__ClearPageActive(page);
|
||||
__ClearPageWaiters(page);
|
||||
|
||||
list_add(&page->lru, &pages_to_free);
|
||||
@@ -884,14 +845,17 @@ void lru_add_page_tail(struct page *page, struct page *page_tail,
|
||||
get_page(page_tail);
|
||||
list_add_tail(&page_tail->lru, list);
|
||||
} else {
|
||||
struct list_head *list_head;
|
||||
/*
|
||||
* Head page has not yet been counted, as an hpage,
|
||||
* so we must account for each subpage individually.
|
||||
*
|
||||
* Put page_tail on the list at the correct position
|
||||
* so they all end up in order.
|
||||
* Use the standard add function to put page_tail on the list,
|
||||
* but then correct its position so they all end up in order.
|
||||
*/
|
||||
add_page_to_lru_list_tail(page_tail, lruvec);
|
||||
add_page_to_lru_list(page_tail, lruvec, page_lru(page_tail));
|
||||
list_head = page_tail->lru.prev;
|
||||
list_move_tail(&page_tail->lru, list_head);
|
||||
}
|
||||
|
||||
if (!PageUnevictable(page))
|
||||
@@ -902,6 +866,7 @@ void lru_add_page_tail(struct page *page, struct page *page_tail,
|
||||
static void __pagevec_lru_add_fn(struct page *page, struct lruvec *lruvec,
|
||||
void *arg)
|
||||
{
|
||||
enum lru_list lru;
|
||||
int was_unevictable = TestClearPageUnevictable(page);
|
||||
|
||||
VM_BUG_ON_PAGE(PageLRU(page), page);
|
||||
@@ -936,19 +901,21 @@ static void __pagevec_lru_add_fn(struct page *page, struct lruvec *lruvec,
|
||||
smp_mb();
|
||||
|
||||
if (page_evictable(page)) {
|
||||
lru = page_lru(page);
|
||||
update_page_reclaim_stat(lruvec, page_is_file_cache(page),
|
||||
PageActive(page));
|
||||
if (was_unevictable)
|
||||
count_vm_event(UNEVICTABLE_PGRESCUED);
|
||||
} else {
|
||||
lru = LRU_UNEVICTABLE;
|
||||
ClearPageActive(page);
|
||||
SetPageUnevictable(page);
|
||||
if (!was_unevictable)
|
||||
count_vm_event(UNEVICTABLE_PGCULLED);
|
||||
}
|
||||
|
||||
add_page_to_lru_list(page, lruvec);
|
||||
trace_mm_lru_insertion(page);
|
||||
add_page_to_lru_list(page, lruvec, lru);
|
||||
trace_mm_lru_insertion(page, lru);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+9
-80
@@ -8,7 +8,6 @@
|
||||
* Rewritten to use page cache, (C) 1998 Stephen Tweedie
|
||||
*/
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mm_inline.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/swap.h>
|
||||
@@ -112,7 +111,7 @@ void show_swap_cache_info(void)
|
||||
* __add_to_swap_cache resembles add_to_page_cache_locked on swapper_space,
|
||||
* but sets SwapCache flag and private instead of mapping and index.
|
||||
*/
|
||||
int __add_to_swap_cache(struct page *page, swp_entry_t entry, void **shadowp)
|
||||
int __add_to_swap_cache(struct page *page, swp_entry_t entry)
|
||||
{
|
||||
int error, i, nr = hpage_nr_pages(page);
|
||||
struct address_space *address_space;
|
||||
@@ -128,30 +127,11 @@ int __add_to_swap_cache(struct page *page, swp_entry_t entry, void **shadowp)
|
||||
address_space = swap_address_space(entry);
|
||||
xa_lock_irq(&address_space->i_pages);
|
||||
for (i = 0; i < nr; i++) {
|
||||
void *item;
|
||||
void __rcu **slot;
|
||||
struct radix_tree_node *node;
|
||||
|
||||
set_page_private(page + i, entry.val + i);
|
||||
error = __radix_tree_create(&address_space->i_pages,
|
||||
idx + i, 0, &node, &slot);
|
||||
error = radix_tree_insert(&address_space->i_pages,
|
||||
idx + i, page + i);
|
||||
if (unlikely(error))
|
||||
break;
|
||||
|
||||
item = radix_tree_deref_slot_protected(slot,
|
||||
&address_space->i_pages.xa_lock);
|
||||
if (WARN_ON_ONCE(item && !radix_tree_exceptional_entry(item))) {
|
||||
error = -EEXIST;
|
||||
break;
|
||||
}
|
||||
|
||||
__radix_tree_replace(&address_space->i_pages, node, slot,
|
||||
page + i, NULL);
|
||||
|
||||
if (shadowp) {
|
||||
VM_BUG_ON(i);
|
||||
*shadowp = item;
|
||||
}
|
||||
}
|
||||
if (likely(!error)) {
|
||||
address_space->nrpages += nr;
|
||||
@@ -184,7 +164,7 @@ int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask)
|
||||
|
||||
error = radix_tree_maybe_preload_order(gfp_mask, compound_order(page));
|
||||
if (!error) {
|
||||
error = __add_to_swap_cache(page, entry, NULL);
|
||||
error = __add_to_swap_cache(page, entry);
|
||||
radix_tree_preload_end();
|
||||
}
|
||||
return error;
|
||||
@@ -194,7 +174,7 @@ int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask)
|
||||
* This must be called only on pages that have
|
||||
* been verified to be in the swap cache.
|
||||
*/
|
||||
void __delete_from_swap_cache(struct page *page, void *shadow)
|
||||
void __delete_from_swap_cache(struct page *page)
|
||||
{
|
||||
struct address_space *address_space;
|
||||
int i, nr = hpage_nr_pages(page);
|
||||
@@ -204,23 +184,12 @@ void __delete_from_swap_cache(struct page *page, void *shadow)
|
||||
VM_BUG_ON_PAGE(!PageLocked(page), page);
|
||||
VM_BUG_ON_PAGE(!PageSwapCache(page), page);
|
||||
VM_BUG_ON_PAGE(PageWriteback(page), page);
|
||||
VM_BUG_ON(shadow && !radix_tree_exceptional_entry(shadow));
|
||||
|
||||
entry.val = page_private(page);
|
||||
address_space = swap_address_space(entry);
|
||||
idx = swp_offset(entry);
|
||||
for (i = 0; i < nr; i++) {
|
||||
void *item;
|
||||
void __rcu **slot;
|
||||
struct radix_tree_node *node;
|
||||
|
||||
item = __radix_tree_lookup(&address_space->i_pages,
|
||||
idx + i, &node, &slot);
|
||||
if (WARN_ON_ONCE(item != page + i))
|
||||
continue;
|
||||
|
||||
__radix_tree_replace(&address_space->i_pages,
|
||||
node, slot, shadow, NULL);
|
||||
radix_tree_delete(&address_space->i_pages, idx + i);
|
||||
set_page_private(page + i, 0);
|
||||
}
|
||||
ClearPageSwapCache(page);
|
||||
@@ -302,47 +271,13 @@ void delete_from_swap_cache(struct page *page)
|
||||
|
||||
address_space = swap_address_space(entry);
|
||||
xa_lock_irq(&address_space->i_pages);
|
||||
__delete_from_swap_cache(page, NULL);
|
||||
__delete_from_swap_cache(page);
|
||||
xa_unlock_irq(&address_space->i_pages);
|
||||
|
||||
put_swap_page(page, entry);
|
||||
page_ref_sub(page, hpage_nr_pages(page));
|
||||
}
|
||||
|
||||
void clear_shadow_from_swap_cache(int type, unsigned long begin,
|
||||
unsigned long end)
|
||||
{
|
||||
unsigned long curr = begin;
|
||||
|
||||
for (;;) {
|
||||
void *item;
|
||||
void __rcu **slot;
|
||||
struct radix_tree_iter iter;
|
||||
swp_entry_t entry = swp_entry(type, curr);
|
||||
struct address_space *address_space = swap_address_space(entry);
|
||||
|
||||
xa_lock_irq(&address_space->i_pages);
|
||||
radix_tree_for_each_slot(slot, &address_space->i_pages,
|
||||
&iter, curr) {
|
||||
item = radix_tree_deref_slot_protected(slot,
|
||||
&address_space->i_pages.xa_lock);
|
||||
if (radix_tree_exceptional_entry(item))
|
||||
radix_tree_iter_delete(&address_space->i_pages,
|
||||
&iter, slot);
|
||||
if (iter.next_index > end)
|
||||
break;
|
||||
}
|
||||
xa_unlock_irq(&address_space->i_pages);
|
||||
|
||||
/* search the next swapcache until we meet end */
|
||||
curr >>= SWAP_ADDRESS_SPACE_SHIFT;
|
||||
curr++;
|
||||
curr <<= SWAP_ADDRESS_SPACE_SHIFT;
|
||||
if (curr > end)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are the only user, then try to free up the swap cache.
|
||||
*
|
||||
@@ -447,8 +382,6 @@ struct page *__read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask,
|
||||
struct page *found_page, *new_page = NULL;
|
||||
struct address_space *swapper_space = swap_address_space(entry);
|
||||
int err;
|
||||
void *shadow;
|
||||
|
||||
*new_page_allocated = false;
|
||||
|
||||
do {
|
||||
@@ -510,16 +443,13 @@ struct page *__read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask,
|
||||
/* May fail (-ENOMEM) if radix-tree node allocation failed. */
|
||||
__SetPageLocked(new_page);
|
||||
__SetPageSwapBacked(new_page);
|
||||
err = __add_to_swap_cache(new_page, entry, &shadow);
|
||||
err = __add_to_swap_cache(new_page, entry);
|
||||
if (likely(!err)) {
|
||||
radix_tree_preload_end();
|
||||
/*
|
||||
* Initiate read into locked page and return.
|
||||
*/
|
||||
if (!lru_gen_enabled())
|
||||
SetPageWorkingset(new_page);
|
||||
else if (shadow)
|
||||
lru_gen_refault(new_page, shadow);
|
||||
SetPageWorkingset(new_page);
|
||||
lru_cache_add_anon(new_page);
|
||||
*new_page_allocated = true;
|
||||
return new_page;
|
||||
@@ -650,7 +580,6 @@ struct page *swap_cluster_readahead(swp_entry_t entry, gfp_t gfp_mask,
|
||||
struct vm_area_struct *vma = vmf->vma;
|
||||
unsigned long addr = vmf->address;
|
||||
|
||||
|
||||
mask = swapin_nr_pages(offset) - 1;
|
||||
if (!mask)
|
||||
goto skip;
|
||||
|
||||
+2
-7
@@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mm_inline.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/sched/task.h>
|
||||
#include <linux/hugetlb.h>
|
||||
@@ -665,7 +664,6 @@ static void add_to_avail_list(struct swap_info_struct *p)
|
||||
static void swap_range_free(struct swap_info_struct *si, unsigned long offset,
|
||||
unsigned int nr_entries)
|
||||
{
|
||||
unsigned long begin = offset;
|
||||
unsigned long end = offset + nr_entries - 1;
|
||||
void (*swap_slot_free_notify)(struct block_device *, unsigned long);
|
||||
|
||||
@@ -691,7 +689,6 @@ static void swap_range_free(struct swap_info_struct *si, unsigned long offset,
|
||||
swap_slot_free_notify(si->bdev, offset);
|
||||
offset++;
|
||||
}
|
||||
clear_shadow_from_swap_cache(si->type, begin, end);
|
||||
}
|
||||
|
||||
static int scan_swap_map_slots(struct swap_info_struct *si,
|
||||
@@ -1835,8 +1832,7 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd,
|
||||
* Move the page to the active list so it is not
|
||||
* immediately swapped out again after swapon.
|
||||
*/
|
||||
if (!lru_gen_enabled())
|
||||
activate_page(page);
|
||||
activate_page(page);
|
||||
out:
|
||||
pte_unmap_unlock(pte, ptl);
|
||||
out_nolock:
|
||||
@@ -1985,8 +1981,7 @@ static int unuse_mm(struct mm_struct *mm,
|
||||
* Activate page so shrink_inactive_list is unlikely to unmap
|
||||
* its ptes while lock is dropped, so swapoff can make progress.
|
||||
*/
|
||||
if (!lru_gen_enabled())
|
||||
activate_page(page);
|
||||
activate_page(page);
|
||||
unlock_page(page);
|
||||
mmap_read_lock(mm);
|
||||
lock_page(page);
|
||||
|
||||
+37
-2693
File diff suppressed because it is too large.
Load diff
@@ -1203,15 +1203,6 @@ const char * const vmstat_text[] = {
|
||||
"pgscan_kswapd",
|
||||
"pgscan_direct",
|
||||
"pgscan_direct_throttle",
|
||||
"lru_kswapd_no_progress",
|
||||
"lru_kswapd_anon",
|
||||
"lru_kswapd_file",
|
||||
"lru_direct_anon",
|
||||
"lru_direct_file",
|
||||
"lru_kswapd_swap_full",
|
||||
"lru_direct_swap_full",
|
||||
"lru_no_gfp_io",
|
||||
"lru_no_writepage",
|
||||
|
||||
#ifdef CONFIG_NUMA
|
||||
"zone_reclaim_failed",
|
||||
|
||||
+2
-110
@@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
#include <linux/memcontrol.h>
|
||||
#include <linux/mm_inline.h>
|
||||
#include <linux/writeback.h>
|
||||
#include <linux/shmem_fs.h>
|
||||
#include <linux/pagemap.h>
|
||||
@@ -185,6 +184,7 @@ static unsigned int bucket_order __read_mostly;
|
||||
static void *pack_shadow(int memcgid, pg_data_t *pgdat, unsigned long eviction,
|
||||
bool workingset)
|
||||
{
|
||||
eviction >>= bucket_order;
|
||||
eviction &= EVICTION_MASK;
|
||||
eviction = (eviction << MEM_CGROUP_ID_SHIFT) | memcgid;
|
||||
eviction = (eviction << NODES_SHIFT) | pgdat->node_id;
|
||||
@@ -209,108 +209,10 @@ static void unpack_shadow(void *shadow, int *memcgidp, pg_data_t **pgdat,
|
||||
|
||||
*memcgidp = memcgid;
|
||||
*pgdat = NODE_DATA(nid);
|
||||
*evictionp = entry;
|
||||
*evictionp = entry << bucket_order;
|
||||
*workingsetp = workingset;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
|
||||
static int page_lru_refs(struct page *page)
|
||||
{
|
||||
unsigned long flags = READ_ONCE(page->flags);
|
||||
|
||||
BUILD_BUG_ON(LRU_GEN_WIDTH + LRU_REFS_WIDTH > BITS_PER_LONG - EVICTION_SHIFT);
|
||||
|
||||
/* see the comment on MAX_NR_TIERS */
|
||||
return flags & BIT(PG_workingset) ? (flags & LRU_REFS_MASK) >> LRU_REFS_PGOFF : 0;
|
||||
}
|
||||
|
||||
void *lru_gen_eviction(struct page *page)
|
||||
{
|
||||
int hist, tier;
|
||||
unsigned long token;
|
||||
unsigned long min_seq;
|
||||
struct lruvec *lruvec;
|
||||
struct lru_gen_struct *lrugen;
|
||||
int type = page_is_file_cache(page);
|
||||
int refs = page_lru_refs(page);
|
||||
int delta = hpage_nr_pages(page);
|
||||
bool workingset = PageWorkingset(page);
|
||||
struct mem_cgroup *memcg = page_memcg(page);
|
||||
struct pglist_data *pgdat = page_pgdat(page);
|
||||
|
||||
if (!mem_cgroup_disabled() && !memcg)
|
||||
return NULL;
|
||||
|
||||
lruvec = mem_cgroup_lruvec(pgdat, memcg);
|
||||
lrugen = &lruvec->lrugen;
|
||||
min_seq = READ_ONCE(lrugen->min_seq[type]);
|
||||
token = (min_seq << LRU_REFS_WIDTH) | refs;
|
||||
|
||||
hist = lru_hist_from_seq(min_seq);
|
||||
tier = lru_tier_from_refs(refs + workingset);
|
||||
atomic_long_add(delta, &lrugen->evicted[hist][type][tier]);
|
||||
|
||||
return pack_shadow(mem_cgroup_id(memcg), pgdat, token, workingset);
|
||||
}
|
||||
|
||||
void lru_gen_refault(struct page *page, void *shadow)
|
||||
{
|
||||
int hist, tier, refs;
|
||||
int memcg_id;
|
||||
bool workingset;
|
||||
unsigned long token;
|
||||
unsigned long min_seq;
|
||||
struct lruvec *lruvec;
|
||||
struct lru_gen_struct *lrugen;
|
||||
struct mem_cgroup *memcg;
|
||||
struct pglist_data *pgdat;
|
||||
int type = page_is_file_cache(page);
|
||||
int delta = hpage_nr_pages(page);
|
||||
|
||||
unpack_shadow(shadow, &memcg_id, &pgdat, &token, &workingset);
|
||||
|
||||
refs = token & (BIT(LRU_REFS_WIDTH) - 1);
|
||||
if (refs && !workingset)
|
||||
return;
|
||||
|
||||
if (page_pgdat(page) != pgdat)
|
||||
return;
|
||||
|
||||
rcu_read_lock();
|
||||
memcg = mem_cgroup_from_id(memcg_id);
|
||||
if (!mem_cgroup_disabled() && !memcg)
|
||||
goto unlock;
|
||||
|
||||
token >>= LRU_REFS_WIDTH;
|
||||
lruvec = mem_cgroup_lruvec(pgdat, memcg);
|
||||
lrugen = &lruvec->lrugen;
|
||||
min_seq = READ_ONCE(lrugen->min_seq[type]);
|
||||
if (token != (min_seq & (EVICTION_MASK >> LRU_REFS_WIDTH)))
|
||||
goto unlock;
|
||||
|
||||
hist = lru_hist_from_seq(min_seq);
|
||||
tier = lru_tier_from_refs(refs + workingset);
|
||||
atomic_long_add(delta, &lrugen->refaulted[hist][type][tier]);
|
||||
mod_lruvec_state(lruvec, WORKINGSET_REFAULT, delta);
|
||||
|
||||
/*
|
||||
* Count the following two cases as stalls:
|
||||
* 1. For pages accessed through page tables, hotter pages pushed out
|
||||
* hot pages which refaulted immediately.
|
||||
* 2. For pages accessed through file descriptors, numbers of accesses
|
||||
* might have been beyond the limit.
|
||||
*/
|
||||
if (lru_gen_in_fault() || refs + workingset == BIT(LRU_REFS_WIDTH)) {
|
||||
SetPageWorkingset(page);
|
||||
mod_lruvec_state(lruvec, WORKINGSET_RESTORE, delta);
|
||||
}
|
||||
unlock:
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
#endif /* CONFIG_LRU_GEN */
|
||||
|
||||
/**
|
||||
* workingset_eviction - note the eviction of a page from memory
|
||||
* @mapping: address space the page was backing
|
||||
@@ -332,12 +234,8 @@ void *workingset_eviction(struct address_space *mapping, struct page *page)
|
||||
VM_BUG_ON_PAGE(page_count(page), page);
|
||||
VM_BUG_ON_PAGE(!PageLocked(page), page);
|
||||
|
||||
if (lru_gen_enabled())
|
||||
return lru_gen_eviction(page);
|
||||
|
||||
lruvec = mem_cgroup_lruvec(pgdat, memcg);
|
||||
eviction = atomic_long_inc_return(&lruvec->inactive_age);
|
||||
eviction >>= bucket_order;
|
||||
return pack_shadow(memcgid, pgdat, eviction, PageWorkingset(page));
|
||||
}
|
||||
|
||||
@@ -361,13 +259,7 @@ void workingset_refault(struct page *page, void *shadow)
|
||||
bool workingset;
|
||||
int memcgid;
|
||||
|
||||
if (lru_gen_enabled()) {
|
||||
lru_gen_refault(page, shadow);
|
||||
return;
|
||||
}
|
||||
|
||||
unpack_shadow(shadow, &memcgid, &pgdat, &eviction, &workingset);
|
||||
eviction <<= bucket_order;
|
||||
|
||||
rcu_read_lock();
|
||||
/*
|
||||
|
||||
Reference in new issue
Block a user