mirror of
https://github.com/torvalds/linux.git
synced 2025-04-12 16:47:42 +00:00

Uros Bizjak uses x86 named address space qualifiers to provide compile-time checking of percpu area accesses. This has caused a small amount of fallout - two or three issues were reported. In all cases the calling code was founf to be incorrect. - The 4 patch series "Some cleanup for memcg" from Chen Ridong implements some relatively monir cleanups for the memcontrol code. - The 17 patch series "mm: fixes for device-exclusive entries (hmm)" from David Hildenbrand fixes a boatload of issues which David found then using device-exclusive PTE entries when THP is enabled. More work is needed, but this makes thins better - our own HMM selftests now succeed. - The 2 patch series "mm: zswap: remove z3fold and zbud" from Yosry Ahmed remove the z3fold and zbud implementations. They have been deprecated for half a year and nobody has complained. - The 5 patch series "mm: further simplify VMA merge operation" from Lorenzo Stoakes implements numerous simplifications in this area. No runtime effects are anticipated. - The 4 patch series "mm/madvise: remove redundant mmap_lock operations from process_madvise()" from SeongJae Park rationalizes the locking in the madvise() implementation. Performance gains of 20-25% were observed in one MADV_DONTNEED microbenchmark. - The 12 patch series "Tiny cleanup and improvements about SWAP code" from Baoquan He contains a number of touchups to issues which Baoquan noticed when working on the swap code. - The 2 patch series "mm: kmemleak: Usability improvements" from Catalin Marinas implements a couple of improvements to the kmemleak user-visible output. - The 2 patch series "mm/damon/paddr: fix large folios access and schemes handling" from Usama Arif provides a couple of fixes for DAMON's handling of large folios. - The 3 patch series "mm/damon/core: fix wrong and/or useless damos_walk() behaviors" from SeongJae Park fixes a few issues with the accuracy of kdamond's walking of DAMON regions. - The 3 patch series "expose mapping wrprotect, fix fb_defio use" from Lorenzo Stoakes changes the interaction between framebuffer deferred-io and core MM. No functional changes are anticipated - this is preparatory work for the future removal of page structure fields. - The 4 patch series "mm/damon: add support for hugepage_size DAMOS filter" from Usama Arif adds a DAMOS filter which permits the filtering by huge page sizes. - The 4 patch series "mm: permit guard regions for file-backed/shmem mappings" from Lorenzo Stoakes extends the guard region feature from its present "anon mappings only" state. The feature now covers shmem and file-backed mappings. - The 4 patch series "mm: batched unmap lazyfree large folios during reclamation" from Barry Song cleans up and speeds up the unmapping for pte-mapped large folios. - The 18 patch series "reimplement per-vma lock as a refcount" from Suren Baghdasaryan puts the vm_lock back into the vma. Our reasons for pulling it out were largely bogus and that change made the code more messy. This patchset provides small (0-10%) improvements on one microbenchmark. - The 5 patch series "Docs/mm/damon: misc DAMOS filters documentation fixes and improves" from SeongJae Park does some maintenance work on the DAMON docs. - The 27 patch series "hugetlb/CMA improvements for large systems" from Frank van der Linden addresses a pile of issues which have been observed when using CMA on large machines. - The 2 patch series "mm/damon: introduce DAMOS filter type for unmapped pages" from SeongJae Park enables users of DMAON/DAMOS to filter my the page's mapped/unmapped status. - The 19 patch series "zsmalloc/zram: there be preemption" from Sergey Senozhatsky teaches zram to run its compression and decompression operations preemptibly. - The 12 patch series "selftests/mm: Some cleanups from trying to run them" from Brendan Jackman fixes a pile of unrelated issues which Brendan encountered while runnimg our selftests. - The 2 patch series "fs/proc/task_mmu: add guard region bit to pagemap" from Lorenzo Stoakes permits userspace to use /proc/pid/pagemap to determine whether a particular page is a guard page. - The 7 patch series "mm, swap: remove swap slot cache" from Kairui Song removes the swap slot cache from the allocation path - it simply wasn't being effective. - The 5 patch series "mm: cleanups for device-exclusive entries (hmm)" from David Hildenbrand implements a number of unrelated cleanups in this code. - The 5 patch series "mm: Rework generic PTDUMP configs" from Anshuman Khandual implements a number of preparatoty cleanups to the GENERIC_PTDUMP Kconfig logic. - The 8 patch series "mm/damon: auto-tune aggregation interval" from SeongJae Park implements a feedback-driven automatic tuning feature for DAMON's aggregation interval tuning. - The 5 patch series "Fix lazy mmu mode" from Ryan Roberts fixes some issues in powerpc, sparc and x86 lazy MMU implementations. Ryan did this in preparation for implementing lazy mmu mode for arm64 to optimize vmalloc. - The 2 patch series "mm/page_alloc: Some clarifications for migratetype fallback" from Brendan Jackman reworks some commentary to make the code easier to follow. - The 3 patch series "page_counter cleanup and size reduction" from Shakeel Butt cleans up the page_counter code and fixes a size increase which we accidentally added late last year. - The 3 patch series "Add a command line option that enables control of how many threads should be used to allocate huge pages" from Thomas Prescher does that. It allows the careful operator to significantly reduce boot time by tuning the parallalization of huge page initialization. - The 3 patch series "Fix calculations in trace_balance_dirty_pages() for cgwb" from Tang Yizhou fixes the tracing output from the dirty page balancing code. - The 9 patch series "mm/damon: make allow filters after reject filters useful and intuitive" from SeongJae Park improves the handling of allow and reject filters. Behaviour is made more consistent and the documention is updated accordingly. - The 5 patch series "Switch zswap to object read/write APIs" from Yosry Ahmed updates zswap to the new object read/write APIs and thus permits the removal of some legacy code from zpool and zsmalloc. - The 6 patch series "Some trivial cleanups for shmem" from Baolin Wang does as it claims. - The 20 patch series "fs/dax: Fix ZONE_DEVICE page reference counts" from Alistair Popple regularizes the weird ZONE_DEVICE page refcount handling in DAX, permittig the removal of a number of special-case checks. - The 4 patch series "refactor mremap and fix bug" from Lorenzo Stoakes is a preparatoty refactoring and cleanup of the mremap() code. - The 20 patch series "mm: MM owner tracking for large folios (!hugetlb) + CONFIG_NO_PAGE_MAPCOUNT" from David Hildenbrand reworks the manner in which we determine whether a large folio is known to be mapped exclusively into a single MM. - The 8 patch series "mm/damon: add sysfs dirs for managing DAMOS filters based on handling layers" from SeongJae Park adds a couple of new sysfs directories to ease the management of DAMON/DAMOS filters. - The 13 patch series "arch, mm: reduce code duplication in mem_init()" from Mike Rapoport consolidates many per-arch implementations of mem_init() into code generic code, where that is practical. - The 13 patch series "mm/damon/sysfs: commit parameters online via damon_call()" from SeongJae Park continues the cleaning up of sysfs access to DAMON internal data. - The 3 patch series "mm: page_ext: Introduce new iteration API" from Luiz Capitulino reworks the page_ext initialization to fix a boot-time crash which was observed with an unusual combination of compile and cmdline options. - The 8 patch series "Buddy allocator like (or non-uniform) folio split" from Zi Yan reworks the code to split a folio into smaller folios. The main benefit is lessened memory consumption: fewer post-split folios are generated. - The 2 patch series "Minimize xa_node allocation during xarry split" from Zi Yan reduces the number of xarray xa_nodes which are generated during an xarray split. - The 2 patch series "drivers/base/memory: Two cleanups" from Gavin Shan performs some maintenance work on the drivers/base/memory code. - The 3 patch series "Add tracepoints for lowmem reserves, watermarks and totalreserve_pages" from Martin Liu adds some more tracepoints to the page allocator code. - The 4 patch series "mm/madvise: cleanup requests validations and classifications" from SeongJae Park cleans up some warts which SeongJae observed during his earlier madvise work. - The 3 patch series "mm/hwpoison: Fix regressions in memory failure handling" from Shuai Xue addresses two quite serious regressions which Shuai has observed in the memory-failure implementation. - The 5 patch series "mm: reliable huge page allocator" from Johannes Weiner makes huge page allocations cheaper and more reliable by reducing fragmentation. - The 5 patch series "Minor memcg cleanups & prep for memdescs" from Matthew Wilcox is preparatory work for the future implementation of memdescs. - The 4 patch series "track memory used by balloon drivers" from Nico Pache introduces a way to track memory used by our various balloon drivers. - The 2 patch series "mm/damon: introduce DAMOS filter type for active pages" from Nhat Pham permits users to filter for active/inactive pages, separately for file and anon pages. - The 2 patch series "Adding Proactive Memory Reclaim Statistics" from Hao Jia separates the proactive reclaim statistics from the direct reclaim statistics. - The 2 patch series "mm/vmscan: don't try to reclaim hwpoison folio" from Jinjiang Tu fixes our handling of hwpoisoned pages within the reclaim code. -----BEGIN PGP SIGNATURE----- iHQEABYKAB0WIQTTMBEPP41GrTpTJgfdBJ7gKXxAjgUCZ+nZaAAKCRDdBJ7gKXxA jsOWAPiP4r7CJHMZRK4eyJOkvS1a1r+TsIarrFZtjwvf/GIfAQCEG+JDxVfUaUSF Ee93qSSLR1BkNdDw+931Pu0mXfbnBw== =Pn2K -----END PGP SIGNATURE----- Merge tag 'mm-stable-2025-03-30-16-52' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm Pull MM updates from Andrew Morton: - The series "Enable strict percpu address space checks" from Uros Bizjak uses x86 named address space qualifiers to provide compile-time checking of percpu area accesses. This has caused a small amount of fallout - two or three issues were reported. In all cases the calling code was found to be incorrect. - The series "Some cleanup for memcg" from Chen Ridong implements some relatively monir cleanups for the memcontrol code. - The series "mm: fixes for device-exclusive entries (hmm)" from David Hildenbrand fixes a boatload of issues which David found then using device-exclusive PTE entries when THP is enabled. More work is needed, but this makes thins better - our own HMM selftests now succeed. - The series "mm: zswap: remove z3fold and zbud" from Yosry Ahmed remove the z3fold and zbud implementations. They have been deprecated for half a year and nobody has complained. - The series "mm: further simplify VMA merge operation" from Lorenzo Stoakes implements numerous simplifications in this area. No runtime effects are anticipated. - The series "mm/madvise: remove redundant mmap_lock operations from process_madvise()" from SeongJae Park rationalizes the locking in the madvise() implementation. Performance gains of 20-25% were observed in one MADV_DONTNEED microbenchmark. - The series "Tiny cleanup and improvements about SWAP code" from Baoquan He contains a number of touchups to issues which Baoquan noticed when working on the swap code. - The series "mm: kmemleak: Usability improvements" from Catalin Marinas implements a couple of improvements to the kmemleak user-visible output. - The series "mm/damon/paddr: fix large folios access and schemes handling" from Usama Arif provides a couple of fixes for DAMON's handling of large folios. - The series "mm/damon/core: fix wrong and/or useless damos_walk() behaviors" from SeongJae Park fixes a few issues with the accuracy of kdamond's walking of DAMON regions. - The series "expose mapping wrprotect, fix fb_defio use" from Lorenzo Stoakes changes the interaction between framebuffer deferred-io and core MM. No functional changes are anticipated - this is preparatory work for the future removal of page structure fields. - The series "mm/damon: add support for hugepage_size DAMOS filter" from Usama Arif adds a DAMOS filter which permits the filtering by huge page sizes. - The series "mm: permit guard regions for file-backed/shmem mappings" from Lorenzo Stoakes extends the guard region feature from its present "anon mappings only" state. The feature now covers shmem and file-backed mappings. - The series "mm: batched unmap lazyfree large folios during reclamation" from Barry Song cleans up and speeds up the unmapping for pte-mapped large folios. - The series "reimplement per-vma lock as a refcount" from Suren Baghdasaryan puts the vm_lock back into the vma. Our reasons for pulling it out were largely bogus and that change made the code more messy. This patchset provides small (0-10%) improvements on one microbenchmark. - The series "Docs/mm/damon: misc DAMOS filters documentation fixes and improves" from SeongJae Park does some maintenance work on the DAMON docs. - The series "hugetlb/CMA improvements for large systems" from Frank van der Linden addresses a pile of issues which have been observed when using CMA on large machines. - The series "mm/damon: introduce DAMOS filter type for unmapped pages" from SeongJae Park enables users of DMAON/DAMOS to filter my the page's mapped/unmapped status. - The series "zsmalloc/zram: there be preemption" from Sergey Senozhatsky teaches zram to run its compression and decompression operations preemptibly. - The series "selftests/mm: Some cleanups from trying to run them" from Brendan Jackman fixes a pile of unrelated issues which Brendan encountered while runnimg our selftests. - The series "fs/proc/task_mmu: add guard region bit to pagemap" from Lorenzo Stoakes permits userspace to use /proc/pid/pagemap to determine whether a particular page is a guard page. - The series "mm, swap: remove swap slot cache" from Kairui Song removes the swap slot cache from the allocation path - it simply wasn't being effective. - The series "mm: cleanups for device-exclusive entries (hmm)" from David Hildenbrand implements a number of unrelated cleanups in this code. - The series "mm: Rework generic PTDUMP configs" from Anshuman Khandual implements a number of preparatoty cleanups to the GENERIC_PTDUMP Kconfig logic. - The series "mm/damon: auto-tune aggregation interval" from SeongJae Park implements a feedback-driven automatic tuning feature for DAMON's aggregation interval tuning. - The series "Fix lazy mmu mode" from Ryan Roberts fixes some issues in powerpc, sparc and x86 lazy MMU implementations. Ryan did this in preparation for implementing lazy mmu mode for arm64 to optimize vmalloc. - The series "mm/page_alloc: Some clarifications for migratetype fallback" from Brendan Jackman reworks some commentary to make the code easier to follow. - The series "page_counter cleanup and size reduction" from Shakeel Butt cleans up the page_counter code and fixes a size increase which we accidentally added late last year. - The series "Add a command line option that enables control of how many threads should be used to allocate huge pages" from Thomas Prescher does that. It allows the careful operator to significantly reduce boot time by tuning the parallalization of huge page initialization. - The series "Fix calculations in trace_balance_dirty_pages() for cgwb" from Tang Yizhou fixes the tracing output from the dirty page balancing code. - The series "mm/damon: make allow filters after reject filters useful and intuitive" from SeongJae Park improves the handling of allow and reject filters. Behaviour is made more consistent and the documention is updated accordingly. - The series "Switch zswap to object read/write APIs" from Yosry Ahmed updates zswap to the new object read/write APIs and thus permits the removal of some legacy code from zpool and zsmalloc. - The series "Some trivial cleanups for shmem" from Baolin Wang does as it claims. - The series "fs/dax: Fix ZONE_DEVICE page reference counts" from Alistair Popple regularizes the weird ZONE_DEVICE page refcount handling in DAX, permittig the removal of a number of special-case checks. - The series "refactor mremap and fix bug" from Lorenzo Stoakes is a preparatoty refactoring and cleanup of the mremap() code. - The series "mm: MM owner tracking for large folios (!hugetlb) + CONFIG_NO_PAGE_MAPCOUNT" from David Hildenbrand reworks the manner in which we determine whether a large folio is known to be mapped exclusively into a single MM. - The series "mm/damon: add sysfs dirs for managing DAMOS filters based on handling layers" from SeongJae Park adds a couple of new sysfs directories to ease the management of DAMON/DAMOS filters. - The series "arch, mm: reduce code duplication in mem_init()" from Mike Rapoport consolidates many per-arch implementations of mem_init() into code generic code, where that is practical. - The series "mm/damon/sysfs: commit parameters online via damon_call()" from SeongJae Park continues the cleaning up of sysfs access to DAMON internal data. - The series "mm: page_ext: Introduce new iteration API" from Luiz Capitulino reworks the page_ext initialization to fix a boot-time crash which was observed with an unusual combination of compile and cmdline options. - The series "Buddy allocator like (or non-uniform) folio split" from Zi Yan reworks the code to split a folio into smaller folios. The main benefit is lessened memory consumption: fewer post-split folios are generated. - The series "Minimize xa_node allocation during xarry split" from Zi Yan reduces the number of xarray xa_nodes which are generated during an xarray split. - The series "drivers/base/memory: Two cleanups" from Gavin Shan performs some maintenance work on the drivers/base/memory code. - The series "Add tracepoints for lowmem reserves, watermarks and totalreserve_pages" from Martin Liu adds some more tracepoints to the page allocator code. - The series "mm/madvise: cleanup requests validations and classifications" from SeongJae Park cleans up some warts which SeongJae observed during his earlier madvise work. - The series "mm/hwpoison: Fix regressions in memory failure handling" from Shuai Xue addresses two quite serious regressions which Shuai has observed in the memory-failure implementation. - The series "mm: reliable huge page allocator" from Johannes Weiner makes huge page allocations cheaper and more reliable by reducing fragmentation. - The series "Minor memcg cleanups & prep for memdescs" from Matthew Wilcox is preparatory work for the future implementation of memdescs. - The series "track memory used by balloon drivers" from Nico Pache introduces a way to track memory used by our various balloon drivers. - The series "mm/damon: introduce DAMOS filter type for active pages" from Nhat Pham permits users to filter for active/inactive pages, separately for file and anon pages. - The series "Adding Proactive Memory Reclaim Statistics" from Hao Jia separates the proactive reclaim statistics from the direct reclaim statistics. - The series "mm/vmscan: don't try to reclaim hwpoison folio" from Jinjiang Tu fixes our handling of hwpoisoned pages within the reclaim code. * tag 'mm-stable-2025-03-30-16-52' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm: (431 commits) mm/page_alloc: remove unnecessary __maybe_unused in order_to_pindex() x86/mm: restore early initialization of high_memory for 32-bits mm/vmscan: don't try to reclaim hwpoison folio mm/hwpoison: introduce folio_contain_hwpoisoned_page() helper cgroup: docs: add pswpin and pswpout items in cgroup v2 doc mm: vmscan: split proactive reclaim statistics from direct reclaim statistics selftests/mm: speed up split_huge_page_test selftests/mm: uffd-unit-tests support for hugepages > 2M docs/mm/damon/design: document active DAMOS filter type mm/damon: implement a new DAMOS filter type for active pages fs/dax: don't disassociate zero page entries MM documentation: add "Unaccepted" meminfo entry selftests/mm: add commentary about 9pfs bugs fork: use __vmalloc_node() for stack allocation docs/mm: Physical Memory: Populate the "Zones" section xen: balloon: update the NR_BALLOON_PAGES state hv_balloon: update the NR_BALLOON_PAGES state balloon_compaction: update the NR_BALLOON_PAGES state meminfo: add a per node counter for balloon drivers mm: remove references to folio in __memcg_kmem_uncharge_page() ...
1108 lines
30 KiB
C
1108 lines
30 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* linux/mm/swap.c
|
|
*
|
|
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
|
|
*/
|
|
|
|
/*
|
|
* This file contains the default values for the operation of the
|
|
* Linux VM subsystem. Fine-tuning documentation can be found in
|
|
* Documentation/admin-guide/sysctl/vm.rst.
|
|
* Started 18.12.91
|
|
* Swap aging added 23.2.95, Stephen Tweedie.
|
|
* Buffermem limits added 12.3.98, Rik van Riel.
|
|
*/
|
|
|
|
#include <linux/mm.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/kernel_stat.h>
|
|
#include <linux/swap.h>
|
|
#include <linux/mman.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/pagevec.h>
|
|
#include <linux/init.h>
|
|
#include <linux/export.h>
|
|
#include <linux/mm_inline.h>
|
|
#include <linux/percpu_counter.h>
|
|
#include <linux/memremap.h>
|
|
#include <linux/percpu.h>
|
|
#include <linux/cpu.h>
|
|
#include <linux/notifier.h>
|
|
#include <linux/backing-dev.h>
|
|
#include <linux/memcontrol.h>
|
|
#include <linux/gfp.h>
|
|
#include <linux/uio.h>
|
|
#include <linux/hugetlb.h>
|
|
#include <linux/page_idle.h>
|
|
#include <linux/local_lock.h>
|
|
#include <linux/buffer_head.h>
|
|
|
|
#include "internal.h"
|
|
|
|
#define CREATE_TRACE_POINTS
|
|
#include <trace/events/pagemap.h>
|
|
|
|
/* How many pages do we try to swap or page in/out together? As a power of 2 */
|
|
int page_cluster;
|
|
static const int page_cluster_max = 31;
|
|
|
|
struct cpu_fbatches {
|
|
/*
|
|
* The following folio batches are grouped together because they are protected
|
|
* by disabling preemption (and interrupts remain enabled).
|
|
*/
|
|
local_lock_t lock;
|
|
struct folio_batch lru_add;
|
|
struct folio_batch lru_deactivate_file;
|
|
struct folio_batch lru_deactivate;
|
|
struct folio_batch lru_lazyfree;
|
|
#ifdef CONFIG_SMP
|
|
struct folio_batch lru_activate;
|
|
#endif
|
|
/* Protecting the following batches which require disabling interrupts */
|
|
local_lock_t lock_irq;
|
|
struct folio_batch lru_move_tail;
|
|
};
|
|
|
|
static DEFINE_PER_CPU(struct cpu_fbatches, cpu_fbatches) = {
|
|
.lock = INIT_LOCAL_LOCK(lock),
|
|
.lock_irq = INIT_LOCAL_LOCK(lock_irq),
|
|
};
|
|
|
|
static void __page_cache_release(struct folio *folio, struct lruvec **lruvecp,
|
|
unsigned long *flagsp)
|
|
{
|
|
if (folio_test_lru(folio)) {
|
|
folio_lruvec_relock_irqsave(folio, lruvecp, flagsp);
|
|
lruvec_del_folio(*lruvecp, folio);
|
|
__folio_clear_lru_flags(folio);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This path almost never happens for VM activity - pages are normally freed
|
|
* in batches. But it gets used by networking - and for compound pages.
|
|
*/
|
|
static void page_cache_release(struct folio *folio)
|
|
{
|
|
struct lruvec *lruvec = NULL;
|
|
unsigned long flags;
|
|
|
|
__page_cache_release(folio, &lruvec, &flags);
|
|
if (lruvec)
|
|
unlock_page_lruvec_irqrestore(lruvec, flags);
|
|
}
|
|
|
|
void __folio_put(struct folio *folio)
|
|
{
|
|
if (unlikely(folio_is_zone_device(folio))) {
|
|
free_zone_device_folio(folio);
|
|
return;
|
|
}
|
|
|
|
if (folio_test_hugetlb(folio)) {
|
|
free_huge_folio(folio);
|
|
return;
|
|
}
|
|
|
|
page_cache_release(folio);
|
|
folio_unqueue_deferred_split(folio);
|
|
mem_cgroup_uncharge(folio);
|
|
free_frozen_pages(&folio->page, folio_order(folio));
|
|
}
|
|
EXPORT_SYMBOL(__folio_put);
|
|
|
|
typedef void (*move_fn_t)(struct lruvec *lruvec, struct folio *folio);
|
|
|
|
static void lru_add(struct lruvec *lruvec, struct folio *folio)
|
|
{
|
|
int was_unevictable = folio_test_clear_unevictable(folio);
|
|
long nr_pages = folio_nr_pages(folio);
|
|
|
|
VM_BUG_ON_FOLIO(folio_test_lru(folio), folio);
|
|
|
|
/*
|
|
* Is an smp_mb__after_atomic() still required here, before
|
|
* folio_evictable() tests the mlocked flag, to rule out the possibility
|
|
* of stranding an evictable folio on an unevictable LRU? I think
|
|
* not, because __munlock_folio() only clears the mlocked flag
|
|
* while the LRU lock is held.
|
|
*
|
|
* (That is not true of __page_cache_release(), and not necessarily
|
|
* true of folios_put(): but those only clear the mlocked flag after
|
|
* folio_put_testzero() has excluded any other users of the folio.)
|
|
*/
|
|
if (folio_evictable(folio)) {
|
|
if (was_unevictable)
|
|
__count_vm_events(UNEVICTABLE_PGRESCUED, nr_pages);
|
|
} else {
|
|
folio_clear_active(folio);
|
|
folio_set_unevictable(folio);
|
|
/*
|
|
* folio->mlock_count = !!folio_test_mlocked(folio)?
|
|
* But that leaves __mlock_folio() in doubt whether another
|
|
* actor has already counted the mlock or not. Err on the
|
|
* safe side, underestimate, let page reclaim fix it, rather
|
|
* than leaving a page on the unevictable LRU indefinitely.
|
|
*/
|
|
folio->mlock_count = 0;
|
|
if (!was_unevictable)
|
|
__count_vm_events(UNEVICTABLE_PGCULLED, nr_pages);
|
|
}
|
|
|
|
lruvec_add_folio(lruvec, folio);
|
|
trace_mm_lru_insertion(folio);
|
|
}
|
|
|
|
static void folio_batch_move_lru(struct folio_batch *fbatch, move_fn_t move_fn)
|
|
{
|
|
int i;
|
|
struct lruvec *lruvec = NULL;
|
|
unsigned long flags = 0;
|
|
|
|
for (i = 0; i < folio_batch_count(fbatch); i++) {
|
|
struct folio *folio = fbatch->folios[i];
|
|
|
|
folio_lruvec_relock_irqsave(folio, &lruvec, &flags);
|
|
move_fn(lruvec, folio);
|
|
|
|
folio_set_lru(folio);
|
|
}
|
|
|
|
if (lruvec)
|
|
unlock_page_lruvec_irqrestore(lruvec, flags);
|
|
folios_put(fbatch);
|
|
}
|
|
|
|
static void __folio_batch_add_and_move(struct folio_batch __percpu *fbatch,
|
|
struct folio *folio, move_fn_t move_fn,
|
|
bool on_lru, bool disable_irq)
|
|
{
|
|
unsigned long flags;
|
|
|
|
if (on_lru && !folio_test_clear_lru(folio))
|
|
return;
|
|
|
|
folio_get(folio);
|
|
|
|
if (disable_irq)
|
|
local_lock_irqsave(&cpu_fbatches.lock_irq, flags);
|
|
else
|
|
local_lock(&cpu_fbatches.lock);
|
|
|
|
if (!folio_batch_add(this_cpu_ptr(fbatch), folio) || folio_test_large(folio) ||
|
|
lru_cache_disabled())
|
|
folio_batch_move_lru(this_cpu_ptr(fbatch), move_fn);
|
|
|
|
if (disable_irq)
|
|
local_unlock_irqrestore(&cpu_fbatches.lock_irq, flags);
|
|
else
|
|
local_unlock(&cpu_fbatches.lock);
|
|
}
|
|
|
|
#define folio_batch_add_and_move(folio, op, on_lru) \
|
|
__folio_batch_add_and_move( \
|
|
&cpu_fbatches.op, \
|
|
folio, \
|
|
op, \
|
|
on_lru, \
|
|
offsetof(struct cpu_fbatches, op) >= offsetof(struct cpu_fbatches, lock_irq) \
|
|
)
|
|
|
|
static void lru_move_tail(struct lruvec *lruvec, struct folio *folio)
|
|
{
|
|
if (folio_test_unevictable(folio))
|
|
return;
|
|
|
|
lruvec_del_folio(lruvec, folio);
|
|
folio_clear_active(folio);
|
|
lruvec_add_folio_tail(lruvec, folio);
|
|
__count_vm_events(PGROTATED, folio_nr_pages(folio));
|
|
}
|
|
|
|
/*
|
|
* Writeback is about to end against a folio which has been marked for
|
|
* immediate reclaim. If it still appears to be reclaimable, move it
|
|
* to the tail of the inactive list.
|
|
*
|
|
* folio_rotate_reclaimable() must disable IRQs, to prevent nasty races.
|
|
*/
|
|
void folio_rotate_reclaimable(struct folio *folio)
|
|
{
|
|
if (folio_test_locked(folio) || folio_test_dirty(folio) ||
|
|
folio_test_unevictable(folio))
|
|
return;
|
|
|
|
folio_batch_add_and_move(folio, lru_move_tail, true);
|
|
}
|
|
|
|
void lru_note_cost(struct lruvec *lruvec, bool file,
|
|
unsigned int nr_io, unsigned int nr_rotated)
|
|
{
|
|
unsigned long cost;
|
|
|
|
/*
|
|
* Reflect the relative cost of incurring IO and spending CPU
|
|
* time on rotations. This doesn't attempt to make a precise
|
|
* comparison, it just says: if reloads are about comparable
|
|
* between the LRU lists, or rotations are overwhelmingly
|
|
* different between them, adjust scan balance for CPU work.
|
|
*/
|
|
cost = nr_io * SWAP_CLUSTER_MAX + nr_rotated;
|
|
|
|
do {
|
|
unsigned long lrusize;
|
|
|
|
/*
|
|
* Hold lruvec->lru_lock is safe here, since
|
|
* 1) The pinned lruvec in reclaim, or
|
|
* 2) From a pre-LRU page during refault (which also holds the
|
|
* rcu lock, so would be safe even if the page was on the LRU
|
|
* and could move simultaneously to a new lruvec).
|
|
*/
|
|
spin_lock_irq(&lruvec->lru_lock);
|
|
/* Record cost event */
|
|
if (file)
|
|
lruvec->file_cost += cost;
|
|
else
|
|
lruvec->anon_cost += cost;
|
|
|
|
/*
|
|
* Decay previous events
|
|
*
|
|
* Because workloads change over time (and to avoid
|
|
* overflow) we keep these statistics as a floating
|
|
* average, which ends up weighing recent refaults
|
|
* more than old ones.
|
|
*/
|
|
lrusize = lruvec_page_state(lruvec, NR_INACTIVE_ANON) +
|
|
lruvec_page_state(lruvec, NR_ACTIVE_ANON) +
|
|
lruvec_page_state(lruvec, NR_INACTIVE_FILE) +
|
|
lruvec_page_state(lruvec, NR_ACTIVE_FILE);
|
|
|
|
if (lruvec->file_cost + lruvec->anon_cost > lrusize / 4) {
|
|
lruvec->file_cost /= 2;
|
|
lruvec->anon_cost /= 2;
|
|
}
|
|
spin_unlock_irq(&lruvec->lru_lock);
|
|
} while ((lruvec = parent_lruvec(lruvec)));
|
|
}
|
|
|
|
void lru_note_cost_refault(struct folio *folio)
|
|
{
|
|
lru_note_cost(folio_lruvec(folio), folio_is_file_lru(folio),
|
|
folio_nr_pages(folio), 0);
|
|
}
|
|
|
|
static void lru_activate(struct lruvec *lruvec, struct folio *folio)
|
|
{
|
|
long nr_pages = folio_nr_pages(folio);
|
|
|
|
if (folio_test_active(folio) || folio_test_unevictable(folio))
|
|
return;
|
|
|
|
|
|
lruvec_del_folio(lruvec, folio);
|
|
folio_set_active(folio);
|
|
lruvec_add_folio(lruvec, folio);
|
|
trace_mm_lru_activate(folio);
|
|
|
|
__count_vm_events(PGACTIVATE, nr_pages);
|
|
__count_memcg_events(lruvec_memcg(lruvec), PGACTIVATE, nr_pages);
|
|
}
|
|
|
|
#ifdef CONFIG_SMP
|
|
static void folio_activate_drain(int cpu)
|
|
{
|
|
struct folio_batch *fbatch = &per_cpu(cpu_fbatches.lru_activate, cpu);
|
|
|
|
if (folio_batch_count(fbatch))
|
|
folio_batch_move_lru(fbatch, lru_activate);
|
|
}
|
|
|
|
void folio_activate(struct folio *folio)
|
|
{
|
|
if (folio_test_active(folio) || folio_test_unevictable(folio))
|
|
return;
|
|
|
|
folio_batch_add_and_move(folio, lru_activate, true);
|
|
}
|
|
|
|
#else
|
|
static inline void folio_activate_drain(int cpu)
|
|
{
|
|
}
|
|
|
|
void folio_activate(struct folio *folio)
|
|
{
|
|
struct lruvec *lruvec;
|
|
|
|
if (!folio_test_clear_lru(folio))
|
|
return;
|
|
|
|
lruvec = folio_lruvec_lock_irq(folio);
|
|
lru_activate(lruvec, folio);
|
|
unlock_page_lruvec_irq(lruvec);
|
|
folio_set_lru(folio);
|
|
}
|
|
#endif
|
|
|
|
static void __lru_cache_activate_folio(struct folio *folio)
|
|
{
|
|
struct folio_batch *fbatch;
|
|
int i;
|
|
|
|
local_lock(&cpu_fbatches.lock);
|
|
fbatch = this_cpu_ptr(&cpu_fbatches.lru_add);
|
|
|
|
/*
|
|
* Search backwards on the optimistic assumption that the folio being
|
|
* activated has just been added to this batch. Note that only
|
|
* the local batch is examined as a !LRU folio could be in the
|
|
* process of being released, reclaimed, migrated or on a remote
|
|
* batch that is currently being drained. Furthermore, marking
|
|
* a remote batch's folio active potentially hits a race where
|
|
* a folio is marked active just after it is added to the inactive
|
|
* list causing accounting errors and BUG_ON checks to trigger.
|
|
*/
|
|
for (i = folio_batch_count(fbatch) - 1; i >= 0; i--) {
|
|
struct folio *batch_folio = fbatch->folios[i];
|
|
|
|
if (batch_folio == folio) {
|
|
folio_set_active(folio);
|
|
break;
|
|
}
|
|
}
|
|
|
|
local_unlock(&cpu_fbatches.lock);
|
|
}
|
|
|
|
#ifdef CONFIG_LRU_GEN
|
|
|
|
static void lru_gen_inc_refs(struct folio *folio)
|
|
{
|
|
unsigned long new_flags, old_flags = READ_ONCE(folio->flags);
|
|
|
|
if (folio_test_unevictable(folio))
|
|
return;
|
|
|
|
/* see the comment on LRU_REFS_FLAGS */
|
|
if (!folio_test_referenced(folio)) {
|
|
set_mask_bits(&folio->flags, LRU_REFS_MASK, BIT(PG_referenced));
|
|
return;
|
|
}
|
|
|
|
do {
|
|
if ((old_flags & LRU_REFS_MASK) == LRU_REFS_MASK) {
|
|
if (!folio_test_workingset(folio))
|
|
folio_set_workingset(folio);
|
|
return;
|
|
}
|
|
|
|
new_flags = old_flags + BIT(LRU_REFS_PGOFF);
|
|
} while (!try_cmpxchg(&folio->flags, &old_flags, new_flags));
|
|
}
|
|
|
|
static bool lru_gen_clear_refs(struct folio *folio)
|
|
{
|
|
struct lru_gen_folio *lrugen;
|
|
int gen = folio_lru_gen(folio);
|
|
int type = folio_is_file_lru(folio);
|
|
|
|
if (gen < 0)
|
|
return true;
|
|
|
|
set_mask_bits(&folio->flags, LRU_REFS_FLAGS | BIT(PG_workingset), 0);
|
|
|
|
lrugen = &folio_lruvec(folio)->lrugen;
|
|
/* whether can do without shuffling under the LRU lock */
|
|
return gen == lru_gen_from_seq(READ_ONCE(lrugen->min_seq[type]));
|
|
}
|
|
|
|
#else /* !CONFIG_LRU_GEN */
|
|
|
|
static void lru_gen_inc_refs(struct folio *folio)
|
|
{
|
|
}
|
|
|
|
static bool lru_gen_clear_refs(struct folio *folio)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#endif /* CONFIG_LRU_GEN */
|
|
|
|
/**
|
|
* folio_mark_accessed - Mark a folio as having seen activity.
|
|
* @folio: The folio to mark.
|
|
*
|
|
* This function will perform one of the following transitions:
|
|
*
|
|
* * inactive,unreferenced -> inactive,referenced
|
|
* * inactive,referenced -> active,unreferenced
|
|
* * active,unreferenced -> active,referenced
|
|
*
|
|
* When a newly allocated folio is not yet visible, so safe for non-atomic ops,
|
|
* __folio_set_referenced() may be substituted for folio_mark_accessed().
|
|
*/
|
|
void folio_mark_accessed(struct folio *folio)
|
|
{
|
|
if (folio_test_dropbehind(folio))
|
|
return;
|
|
if (lru_gen_enabled()) {
|
|
lru_gen_inc_refs(folio);
|
|
return;
|
|
}
|
|
|
|
if (!folio_test_referenced(folio)) {
|
|
folio_set_referenced(folio);
|
|
} else if (folio_test_unevictable(folio)) {
|
|
/*
|
|
* Unevictable pages are on the "LRU_UNEVICTABLE" list. But,
|
|
* this list is never rotated or maintained, so marking an
|
|
* unevictable page accessed has no effect.
|
|
*/
|
|
} else if (!folio_test_active(folio)) {
|
|
/*
|
|
* If the folio is on the LRU, queue it for activation via
|
|
* cpu_fbatches.lru_activate. Otherwise, assume the folio is in a
|
|
* folio_batch, mark it active and it'll be moved to the active
|
|
* LRU on the next drain.
|
|
*/
|
|
if (folio_test_lru(folio))
|
|
folio_activate(folio);
|
|
else
|
|
__lru_cache_activate_folio(folio);
|
|
folio_clear_referenced(folio);
|
|
workingset_activation(folio);
|
|
}
|
|
if (folio_test_idle(folio))
|
|
folio_clear_idle(folio);
|
|
}
|
|
EXPORT_SYMBOL(folio_mark_accessed);
|
|
|
|
/**
|
|
* folio_add_lru - Add a folio to an LRU list.
|
|
* @folio: The folio to be added to the LRU.
|
|
*
|
|
* Queue the folio for addition to the LRU. The decision on whether
|
|
* to add the page to the [in]active [file|anon] list is deferred until the
|
|
* folio_batch is drained. This gives a chance for the caller of folio_add_lru()
|
|
* have the folio added to the active list using folio_mark_accessed().
|
|
*/
|
|
void folio_add_lru(struct folio *folio)
|
|
{
|
|
VM_BUG_ON_FOLIO(folio_test_active(folio) &&
|
|
folio_test_unevictable(folio), folio);
|
|
VM_BUG_ON_FOLIO(folio_test_lru(folio), folio);
|
|
|
|
/* see the comment in lru_gen_folio_seq() */
|
|
if (lru_gen_enabled() && !folio_test_unevictable(folio) &&
|
|
lru_gen_in_fault() && !(current->flags & PF_MEMALLOC))
|
|
folio_set_active(folio);
|
|
|
|
folio_batch_add_and_move(folio, lru_add, false);
|
|
}
|
|
EXPORT_SYMBOL(folio_add_lru);
|
|
|
|
/**
|
|
* folio_add_lru_vma() - Add a folio to the appropate LRU list for this VMA.
|
|
* @folio: The folio to be added to the LRU.
|
|
* @vma: VMA in which the folio is mapped.
|
|
*
|
|
* If the VMA is mlocked, @folio is added to the unevictable list.
|
|
* Otherwise, it is treated the same way as folio_add_lru().
|
|
*/
|
|
void folio_add_lru_vma(struct folio *folio, struct vm_area_struct *vma)
|
|
{
|
|
VM_BUG_ON_FOLIO(folio_test_lru(folio), folio);
|
|
|
|
if (unlikely((vma->vm_flags & (VM_LOCKED | VM_SPECIAL)) == VM_LOCKED))
|
|
mlock_new_folio(folio);
|
|
else
|
|
folio_add_lru(folio);
|
|
}
|
|
|
|
/*
|
|
* If the folio cannot be invalidated, it is moved to the
|
|
* inactive list to speed up its reclaim. It is moved to the
|
|
* head of the list, rather than the tail, to give the flusher
|
|
* threads some time to write it out, as this is much more
|
|
* effective than the single-page writeout from reclaim.
|
|
*
|
|
* If the folio isn't mapped and dirty/writeback, the folio
|
|
* could be reclaimed asap using the reclaim flag.
|
|
*
|
|
* 1. active, mapped folio -> none
|
|
* 2. active, dirty/writeback folio -> inactive, head, reclaim
|
|
* 3. inactive, mapped folio -> none
|
|
* 4. inactive, dirty/writeback folio -> inactive, head, reclaim
|
|
* 5. inactive, clean -> inactive, tail
|
|
* 6. Others -> none
|
|
*
|
|
* In 4, it moves to the head of the inactive list so the folio is
|
|
* written out by flusher threads as this is much more efficient
|
|
* than the single-page writeout from reclaim.
|
|
*/
|
|
static void lru_deactivate_file(struct lruvec *lruvec, struct folio *folio)
|
|
{
|
|
bool active = folio_test_active(folio) || lru_gen_enabled();
|
|
long nr_pages = folio_nr_pages(folio);
|
|
|
|
if (folio_test_unevictable(folio))
|
|
return;
|
|
|
|
/* Some processes are using the folio */
|
|
if (folio_mapped(folio))
|
|
return;
|
|
|
|
lruvec_del_folio(lruvec, folio);
|
|
folio_clear_active(folio);
|
|
folio_clear_referenced(folio);
|
|
|
|
if (folio_test_writeback(folio) || folio_test_dirty(folio)) {
|
|
/*
|
|
* Setting the reclaim flag could race with
|
|
* folio_end_writeback() and confuse readahead. But the
|
|
* race window is _really_ small and it's not a critical
|
|
* problem.
|
|
*/
|
|
lruvec_add_folio(lruvec, folio);
|
|
folio_set_reclaim(folio);
|
|
} else {
|
|
/*
|
|
* The folio's writeback ended while it was in the batch.
|
|
* We move that folio to the tail of the inactive list.
|
|
*/
|
|
lruvec_add_folio_tail(lruvec, folio);
|
|
__count_vm_events(PGROTATED, nr_pages);
|
|
}
|
|
|
|
if (active) {
|
|
__count_vm_events(PGDEACTIVATE, nr_pages);
|
|
__count_memcg_events(lruvec_memcg(lruvec), PGDEACTIVATE,
|
|
nr_pages);
|
|
}
|
|
}
|
|
|
|
static void lru_deactivate(struct lruvec *lruvec, struct folio *folio)
|
|
{
|
|
long nr_pages = folio_nr_pages(folio);
|
|
|
|
if (folio_test_unevictable(folio) || !(folio_test_active(folio) || lru_gen_enabled()))
|
|
return;
|
|
|
|
lruvec_del_folio(lruvec, folio);
|
|
folio_clear_active(folio);
|
|
folio_clear_referenced(folio);
|
|
lruvec_add_folio(lruvec, folio);
|
|
|
|
__count_vm_events(PGDEACTIVATE, nr_pages);
|
|
__count_memcg_events(lruvec_memcg(lruvec), PGDEACTIVATE, nr_pages);
|
|
}
|
|
|
|
static void lru_lazyfree(struct lruvec *lruvec, struct folio *folio)
|
|
{
|
|
long nr_pages = folio_nr_pages(folio);
|
|
|
|
if (!folio_test_anon(folio) || !folio_test_swapbacked(folio) ||
|
|
folio_test_swapcache(folio) || folio_test_unevictable(folio))
|
|
return;
|
|
|
|
lruvec_del_folio(lruvec, folio);
|
|
folio_clear_active(folio);
|
|
if (lru_gen_enabled())
|
|
lru_gen_clear_refs(folio);
|
|
else
|
|
folio_clear_referenced(folio);
|
|
/*
|
|
* Lazyfree folios are clean anonymous folios. They have
|
|
* the swapbacked flag cleared, to distinguish them from normal
|
|
* anonymous folios
|
|
*/
|
|
folio_clear_swapbacked(folio);
|
|
lruvec_add_folio(lruvec, folio);
|
|
|
|
__count_vm_events(PGLAZYFREE, nr_pages);
|
|
__count_memcg_events(lruvec_memcg(lruvec), PGLAZYFREE, nr_pages);
|
|
}
|
|
|
|
/*
|
|
* Drain pages out of the cpu's folio_batch.
|
|
* Either "cpu" is the current CPU, and preemption has already been
|
|
* disabled; or "cpu" is being hot-unplugged, and is already dead.
|
|
*/
|
|
void lru_add_drain_cpu(int cpu)
|
|
{
|
|
struct cpu_fbatches *fbatches = &per_cpu(cpu_fbatches, cpu);
|
|
struct folio_batch *fbatch = &fbatches->lru_add;
|
|
|
|
if (folio_batch_count(fbatch))
|
|
folio_batch_move_lru(fbatch, lru_add);
|
|
|
|
fbatch = &fbatches->lru_move_tail;
|
|
/* Disabling interrupts below acts as a compiler barrier. */
|
|
if (data_race(folio_batch_count(fbatch))) {
|
|
unsigned long flags;
|
|
|
|
/* No harm done if a racing interrupt already did this */
|
|
local_lock_irqsave(&cpu_fbatches.lock_irq, flags);
|
|
folio_batch_move_lru(fbatch, lru_move_tail);
|
|
local_unlock_irqrestore(&cpu_fbatches.lock_irq, flags);
|
|
}
|
|
|
|
fbatch = &fbatches->lru_deactivate_file;
|
|
if (folio_batch_count(fbatch))
|
|
folio_batch_move_lru(fbatch, lru_deactivate_file);
|
|
|
|
fbatch = &fbatches->lru_deactivate;
|
|
if (folio_batch_count(fbatch))
|
|
folio_batch_move_lru(fbatch, lru_deactivate);
|
|
|
|
fbatch = &fbatches->lru_lazyfree;
|
|
if (folio_batch_count(fbatch))
|
|
folio_batch_move_lru(fbatch, lru_lazyfree);
|
|
|
|
folio_activate_drain(cpu);
|
|
}
|
|
|
|
/**
|
|
* deactivate_file_folio() - Deactivate a file folio.
|
|
* @folio: Folio to deactivate.
|
|
*
|
|
* This function hints to the VM that @folio is a good reclaim candidate,
|
|
* for example if its invalidation fails due to the folio being dirty
|
|
* or under writeback.
|
|
*
|
|
* Context: Caller holds a reference on the folio.
|
|
*/
|
|
void deactivate_file_folio(struct folio *folio)
|
|
{
|
|
/* Deactivating an unevictable folio will not accelerate reclaim */
|
|
if (folio_test_unevictable(folio))
|
|
return;
|
|
|
|
if (lru_gen_enabled() && lru_gen_clear_refs(folio))
|
|
return;
|
|
|
|
folio_batch_add_and_move(folio, lru_deactivate_file, true);
|
|
}
|
|
|
|
/*
|
|
* folio_deactivate - deactivate a folio
|
|
* @folio: folio to deactivate
|
|
*
|
|
* folio_deactivate() moves @folio to the inactive list if @folio was on the
|
|
* active list and was not unevictable. This is done to accelerate the
|
|
* reclaim of @folio.
|
|
*/
|
|
void folio_deactivate(struct folio *folio)
|
|
{
|
|
if (folio_test_unevictable(folio))
|
|
return;
|
|
|
|
if (lru_gen_enabled() ? lru_gen_clear_refs(folio) : !folio_test_active(folio))
|
|
return;
|
|
|
|
folio_batch_add_and_move(folio, lru_deactivate, true);
|
|
}
|
|
|
|
/**
|
|
* folio_mark_lazyfree - make an anon folio lazyfree
|
|
* @folio: folio to deactivate
|
|
*
|
|
* folio_mark_lazyfree() moves @folio to the inactive file list.
|
|
* This is done to accelerate the reclaim of @folio.
|
|
*/
|
|
void folio_mark_lazyfree(struct folio *folio)
|
|
{
|
|
if (!folio_test_anon(folio) || !folio_test_swapbacked(folio) ||
|
|
folio_test_swapcache(folio) || folio_test_unevictable(folio))
|
|
return;
|
|
|
|
folio_batch_add_and_move(folio, lru_lazyfree, true);
|
|
}
|
|
|
|
void lru_add_drain(void)
|
|
{
|
|
local_lock(&cpu_fbatches.lock);
|
|
lru_add_drain_cpu(smp_processor_id());
|
|
local_unlock(&cpu_fbatches.lock);
|
|
mlock_drain_local();
|
|
}
|
|
|
|
/*
|
|
* It's called from per-cpu workqueue context in SMP case so
|
|
* lru_add_drain_cpu and invalidate_bh_lrus_cpu should run on
|
|
* the same cpu. It shouldn't be a problem in !SMP case since
|
|
* the core is only one and the locks will disable preemption.
|
|
*/
|
|
static void lru_add_and_bh_lrus_drain(void)
|
|
{
|
|
local_lock(&cpu_fbatches.lock);
|
|
lru_add_drain_cpu(smp_processor_id());
|
|
local_unlock(&cpu_fbatches.lock);
|
|
invalidate_bh_lrus_cpu();
|
|
mlock_drain_local();
|
|
}
|
|
|
|
void lru_add_drain_cpu_zone(struct zone *zone)
|
|
{
|
|
local_lock(&cpu_fbatches.lock);
|
|
lru_add_drain_cpu(smp_processor_id());
|
|
drain_local_pages(zone);
|
|
local_unlock(&cpu_fbatches.lock);
|
|
mlock_drain_local();
|
|
}
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
static DEFINE_PER_CPU(struct work_struct, lru_add_drain_work);
|
|
|
|
static void lru_add_drain_per_cpu(struct work_struct *dummy)
|
|
{
|
|
lru_add_and_bh_lrus_drain();
|
|
}
|
|
|
|
static bool cpu_needs_drain(unsigned int cpu)
|
|
{
|
|
struct cpu_fbatches *fbatches = &per_cpu(cpu_fbatches, cpu);
|
|
|
|
/* Check these in order of likelihood that they're not zero */
|
|
return folio_batch_count(&fbatches->lru_add) ||
|
|
folio_batch_count(&fbatches->lru_move_tail) ||
|
|
folio_batch_count(&fbatches->lru_deactivate_file) ||
|
|
folio_batch_count(&fbatches->lru_deactivate) ||
|
|
folio_batch_count(&fbatches->lru_lazyfree) ||
|
|
folio_batch_count(&fbatches->lru_activate) ||
|
|
need_mlock_drain(cpu) ||
|
|
has_bh_in_lru(cpu, NULL);
|
|
}
|
|
|
|
/*
|
|
* Doesn't need any cpu hotplug locking because we do rely on per-cpu
|
|
* kworkers being shut down before our page_alloc_cpu_dead callback is
|
|
* executed on the offlined cpu.
|
|
* Calling this function with cpu hotplug locks held can actually lead
|
|
* to obscure indirect dependencies via WQ context.
|
|
*/
|
|
static inline void __lru_add_drain_all(bool force_all_cpus)
|
|
{
|
|
/*
|
|
* lru_drain_gen - Global pages generation number
|
|
*
|
|
* (A) Definition: global lru_drain_gen = x implies that all generations
|
|
* 0 < n <= x are already *scheduled* for draining.
|
|
*
|
|
* This is an optimization for the highly-contended use case where a
|
|
* user space workload keeps constantly generating a flow of pages for
|
|
* each CPU.
|
|
*/
|
|
static unsigned int lru_drain_gen;
|
|
static struct cpumask has_work;
|
|
static DEFINE_MUTEX(lock);
|
|
unsigned cpu, this_gen;
|
|
|
|
/*
|
|
* Make sure nobody triggers this path before mm_percpu_wq is fully
|
|
* initialized.
|
|
*/
|
|
if (WARN_ON(!mm_percpu_wq))
|
|
return;
|
|
|
|
/*
|
|
* Guarantee folio_batch counter stores visible by this CPU
|
|
* are visible to other CPUs before loading the current drain
|
|
* generation.
|
|
*/
|
|
smp_mb();
|
|
|
|
/*
|
|
* (B) Locally cache global LRU draining generation number
|
|
*
|
|
* The read barrier ensures that the counter is loaded before the mutex
|
|
* is taken. It pairs with smp_mb() inside the mutex critical section
|
|
* at (D).
|
|
*/
|
|
this_gen = smp_load_acquire(&lru_drain_gen);
|
|
|
|
mutex_lock(&lock);
|
|
|
|
/*
|
|
* (C) Exit the draining operation if a newer generation, from another
|
|
* lru_add_drain_all(), was already scheduled for draining. Check (A).
|
|
*/
|
|
if (unlikely(this_gen != lru_drain_gen && !force_all_cpus))
|
|
goto done;
|
|
|
|
/*
|
|
* (D) Increment global generation number
|
|
*
|
|
* Pairs with smp_load_acquire() at (B), outside of the critical
|
|
* section. Use a full memory barrier to guarantee that the
|
|
* new global drain generation number is stored before loading
|
|
* folio_batch counters.
|
|
*
|
|
* This pairing must be done here, before the for_each_online_cpu loop
|
|
* below which drains the page vectors.
|
|
*
|
|
* Let x, y, and z represent some system CPU numbers, where x < y < z.
|
|
* Assume CPU #z is in the middle of the for_each_online_cpu loop
|
|
* below and has already reached CPU #y's per-cpu data. CPU #x comes
|
|
* along, adds some pages to its per-cpu vectors, then calls
|
|
* lru_add_drain_all().
|
|
*
|
|
* If the paired barrier is done at any later step, e.g. after the
|
|
* loop, CPU #x will just exit at (C) and miss flushing out all of its
|
|
* added pages.
|
|
*/
|
|
WRITE_ONCE(lru_drain_gen, lru_drain_gen + 1);
|
|
smp_mb();
|
|
|
|
cpumask_clear(&has_work);
|
|
for_each_online_cpu(cpu) {
|
|
struct work_struct *work = &per_cpu(lru_add_drain_work, cpu);
|
|
|
|
if (cpu_needs_drain(cpu)) {
|
|
INIT_WORK(work, lru_add_drain_per_cpu);
|
|
queue_work_on(cpu, mm_percpu_wq, work);
|
|
__cpumask_set_cpu(cpu, &has_work);
|
|
}
|
|
}
|
|
|
|
for_each_cpu(cpu, &has_work)
|
|
flush_work(&per_cpu(lru_add_drain_work, cpu));
|
|
|
|
done:
|
|
mutex_unlock(&lock);
|
|
}
|
|
|
|
void lru_add_drain_all(void)
|
|
{
|
|
__lru_add_drain_all(false);
|
|
}
|
|
#else
|
|
void lru_add_drain_all(void)
|
|
{
|
|
lru_add_drain();
|
|
}
|
|
#endif /* CONFIG_SMP */
|
|
|
|
atomic_t lru_disable_count = ATOMIC_INIT(0);
|
|
|
|
/*
|
|
* lru_cache_disable() needs to be called before we start compiling
|
|
* a list of folios to be migrated using folio_isolate_lru().
|
|
* It drains folios on LRU cache and then disable on all cpus until
|
|
* lru_cache_enable is called.
|
|
*
|
|
* Must be paired with a call to lru_cache_enable().
|
|
*/
|
|
void lru_cache_disable(void)
|
|
{
|
|
atomic_inc(&lru_disable_count);
|
|
/*
|
|
* Readers of lru_disable_count are protected by either disabling
|
|
* preemption or rcu_read_lock:
|
|
*
|
|
* preempt_disable, local_irq_disable [bh_lru_lock()]
|
|
* rcu_read_lock [rt_spin_lock CONFIG_PREEMPT_RT]
|
|
* preempt_disable [local_lock !CONFIG_PREEMPT_RT]
|
|
*
|
|
* Since v5.1 kernel, synchronize_rcu() is guaranteed to wait on
|
|
* preempt_disable() regions of code. So any CPU which sees
|
|
* lru_disable_count = 0 will have exited the critical
|
|
* section when synchronize_rcu() returns.
|
|
*/
|
|
synchronize_rcu_expedited();
|
|
#ifdef CONFIG_SMP
|
|
__lru_add_drain_all(true);
|
|
#else
|
|
lru_add_and_bh_lrus_drain();
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* folios_put_refs - Reduce the reference count on a batch of folios.
|
|
* @folios: The folios.
|
|
* @refs: The number of refs to subtract from each folio.
|
|
*
|
|
* Like folio_put(), but for a batch of folios. This is more efficient
|
|
* than writing the loop yourself as it will optimise the locks which need
|
|
* to be taken if the folios are freed. The folios batch is returned
|
|
* empty and ready to be reused for another batch; there is no need
|
|
* to reinitialise it. If @refs is NULL, we subtract one from each
|
|
* folio refcount.
|
|
*
|
|
* Context: May be called in process or interrupt context, but not in NMI
|
|
* context. May be called while holding a spinlock.
|
|
*/
|
|
void folios_put_refs(struct folio_batch *folios, unsigned int *refs)
|
|
{
|
|
int i, j;
|
|
struct lruvec *lruvec = NULL;
|
|
unsigned long flags = 0;
|
|
|
|
for (i = 0, j = 0; i < folios->nr; i++) {
|
|
struct folio *folio = folios->folios[i];
|
|
unsigned int nr_refs = refs ? refs[i] : 1;
|
|
|
|
if (is_huge_zero_folio(folio))
|
|
continue;
|
|
|
|
if (folio_is_zone_device(folio)) {
|
|
if (lruvec) {
|
|
unlock_page_lruvec_irqrestore(lruvec, flags);
|
|
lruvec = NULL;
|
|
}
|
|
if (folio_ref_sub_and_test(folio, nr_refs))
|
|
free_zone_device_folio(folio);
|
|
continue;
|
|
}
|
|
|
|
if (!folio_ref_sub_and_test(folio, nr_refs))
|
|
continue;
|
|
|
|
/* hugetlb has its own memcg */
|
|
if (folio_test_hugetlb(folio)) {
|
|
if (lruvec) {
|
|
unlock_page_lruvec_irqrestore(lruvec, flags);
|
|
lruvec = NULL;
|
|
}
|
|
free_huge_folio(folio);
|
|
continue;
|
|
}
|
|
folio_unqueue_deferred_split(folio);
|
|
__page_cache_release(folio, &lruvec, &flags);
|
|
|
|
if (j != i)
|
|
folios->folios[j] = folio;
|
|
j++;
|
|
}
|
|
if (lruvec)
|
|
unlock_page_lruvec_irqrestore(lruvec, flags);
|
|
if (!j) {
|
|
folio_batch_reinit(folios);
|
|
return;
|
|
}
|
|
|
|
folios->nr = j;
|
|
mem_cgroup_uncharge_folios(folios);
|
|
free_unref_folios(folios);
|
|
}
|
|
EXPORT_SYMBOL(folios_put_refs);
|
|
|
|
/**
|
|
* release_pages - batched put_page()
|
|
* @arg: array of pages to release
|
|
* @nr: number of pages
|
|
*
|
|
* Decrement the reference count on all the pages in @arg. If it
|
|
* fell to zero, remove the page from the LRU and free it.
|
|
*
|
|
* Note that the argument can be an array of pages, encoded pages,
|
|
* or folio pointers. We ignore any encoded bits, and turn any of
|
|
* them into just a folio that gets free'd.
|
|
*/
|
|
void release_pages(release_pages_arg arg, int nr)
|
|
{
|
|
struct folio_batch fbatch;
|
|
int refs[PAGEVEC_SIZE];
|
|
struct encoded_page **encoded = arg.encoded_pages;
|
|
int i;
|
|
|
|
folio_batch_init(&fbatch);
|
|
for (i = 0; i < nr; i++) {
|
|
/* Turn any of the argument types into a folio */
|
|
struct folio *folio = page_folio(encoded_page_ptr(encoded[i]));
|
|
|
|
/* Is our next entry actually "nr_pages" -> "nr_refs" ? */
|
|
refs[fbatch.nr] = 1;
|
|
if (unlikely(encoded_page_flags(encoded[i]) &
|
|
ENCODED_PAGE_BIT_NR_PAGES_NEXT))
|
|
refs[fbatch.nr] = encoded_nr_pages(encoded[++i]);
|
|
|
|
if (folio_batch_add(&fbatch, folio) > 0)
|
|
continue;
|
|
folios_put_refs(&fbatch, refs);
|
|
}
|
|
|
|
if (fbatch.nr)
|
|
folios_put_refs(&fbatch, refs);
|
|
}
|
|
EXPORT_SYMBOL(release_pages);
|
|
|
|
/*
|
|
* The folios which we're about to release may be in the deferred lru-addition
|
|
* queues. That would prevent them from really being freed right now. That's
|
|
* OK from a correctness point of view but is inefficient - those folios may be
|
|
* cache-warm and we want to give them back to the page allocator ASAP.
|
|
*
|
|
* So __folio_batch_release() will drain those queues here.
|
|
* folio_batch_move_lru() calls folios_put() directly to avoid
|
|
* mutual recursion.
|
|
*/
|
|
void __folio_batch_release(struct folio_batch *fbatch)
|
|
{
|
|
if (!fbatch->percpu_pvec_drained) {
|
|
lru_add_drain();
|
|
fbatch->percpu_pvec_drained = true;
|
|
}
|
|
folios_put(fbatch);
|
|
}
|
|
EXPORT_SYMBOL(__folio_batch_release);
|
|
|
|
/**
|
|
* folio_batch_remove_exceptionals() - Prune non-folios from a batch.
|
|
* @fbatch: The batch to prune
|
|
*
|
|
* find_get_entries() fills a batch with both folios and shadow/swap/DAX
|
|
* entries. This function prunes all the non-folio entries from @fbatch
|
|
* without leaving holes, so that it can be passed on to folio-only batch
|
|
* operations.
|
|
*/
|
|
void folio_batch_remove_exceptionals(struct folio_batch *fbatch)
|
|
{
|
|
unsigned int i, j;
|
|
|
|
for (i = 0, j = 0; i < folio_batch_count(fbatch); i++) {
|
|
struct folio *folio = fbatch->folios[i];
|
|
if (!xa_is_value(folio))
|
|
fbatch->folios[j++] = folio;
|
|
}
|
|
fbatch->nr = j;
|
|
}
|
|
|
|
static const struct ctl_table swap_sysctl_table[] = {
|
|
{
|
|
.procname = "page-cluster",
|
|
.data = &page_cluster,
|
|
.maxlen = sizeof(int),
|
|
.mode = 0644,
|
|
.proc_handler = proc_dointvec_minmax,
|
|
.extra1 = SYSCTL_ZERO,
|
|
.extra2 = (void *)&page_cluster_max,
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Perform any setup for the swap system
|
|
*/
|
|
void __init swap_setup(void)
|
|
{
|
|
unsigned long megs = totalram_pages() >> (20 - PAGE_SHIFT);
|
|
|
|
/* Use a smaller cluster for small-memory machines */
|
|
if (megs < 16)
|
|
page_cluster = 2;
|
|
else
|
|
page_cluster = 3;
|
|
/*
|
|
* Right now other parts of the system means that we
|
|
* _really_ don't want to cluster much more
|
|
*/
|
|
|
|
register_sysctl_init("vm", swap_sysctl_table);
|
|
}
|