mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-09-05 08:44:14 +02:00
Pull MM updates from Andrew Morton:
- "mm: drop "sub" prefix from various places" (Dev Jain)
page->folio conversion and a naming cleanup
- "mm/kasan: remove redundant initialization for kasan_flag_write_only"
(Igor Putko)
KASAN cleanup work
- "mm/filemap: reduce unnecessary xarray lookups" (Chi Zhiling)
Small speedup in the pagecaache read code
- "mm/percpu: Fix possible NOFS/NOIO reclaim recursion" (Kaitao Cheng)
Improve the vmalloc code - mainly the avoidance of GFP_KERNEL
allocations when the caller asked for GFP_NOFS or GFP_NOIO
- "mm/kmemleak: avoid soft lockup when scanning task stacks" (Breno
Leitao)
Avoid a soft lockup watchdog trigger from the kmemleak scanning code
in extreme situations
- "mm/page_owner: misc cleanups" (Ye Liu)
Cleanups to the page_owner code. For some reason lots of people have
been working on the page_owner code this cycle.
- "mm: convert to walk_page_range_vma() to eliminate find_vma()"
(Kefeng Wang)
Simplify and accelerate the page walking library function
- "mm/migrate: preparatory cleanups for batch copy and offload"
(Shivank Garg)
Cleanups in the migration code
- "mm/page_owner: add per-fd filter infrastructure for print_mode and
NUMA filtering" (Zhen Ni)
Per-fd filtering to page_owner in order to reduce the sometimes vast
amount of output it can produce
- "mm: Refactor bootmem gigantic hugepage allocation" (Muchun Song)
Fixes and preparatory cleanups around bootmem HugeTLB handling,
sparse initialization ordering, and related vmemmap setup
- "mm/zsmalloc: reduce lock contention in zs_free()" (Wenchao Hao)
Reduce lock contention in zs_free(), which dominates the unmap path
under memory pressure on Android (LMK kills) and on x86 servers
running zswap-heavy workloads.
Up to 1.83x improvement in microbenchmarking.
- "move alloc_tag.c file under mm/" (Suren Baghdasaryan)
- "samples/damon: handle damon_{start,stop}() failures" (SJ Park)
Fix improper handling of damon_start(), damon_stop(), and
damon_call() failures across DAMON sample modules to prevent
potential memory leaks, operation disruptions and use-after-free
bugs
- "mm/damon/sysfs: kobject_del() directories that users can
create/remove" (SJ Park)
Fix delayed sysfs directory removal under DEBUG_KOBJECT_RELEASE
causeing creation failures due to duplicate directory names by adding
missing kobject_del() calls before creating new directories
- "mm: cleanup clear_not_present_full_ptes()" (David Hildenbrand)
Clean up the core pte handling code
- "selftests/damon: misc fixes for test bugs" (Kunwu Chan)
Fix several bugs in the DAMON selftests
- "selftests/damon: fix memcg_path staging handling" (Cheng Nie)
Fix a bug in _damon_sysfs.py for damos_filter memcg_path setup, and
add a test case for it in sysfs.py.
- "selftests/damon: test kdamond refresh_ms" (Ruslan Valiyev)
Selftest coverage for DAMON's refresh_ms sysfs feature by updating
the test control module and verifying that scheme stats update
automatically without manual intervention
- "mm/damon: five misc fixups" (Akinobu Mita)
Miscellaneous DAMON fixups.
- "mm/damon/core: detect internal variation above max_nr_regions/2"
(Jiayuan Chen)
Fix DAMON's region splitting behavior when region counts exceed half
the maximum budget by dynamically scaling down the split fraction as
the limit approaches, preventing large regions from staying un-split,
and add corresponding KUnit test coverage
- "mm: preparatory patches for PMD level swap entries" (Usama Arif)
Refactor and clean up PMD softleaf helpers, call sites, and
architecture flags to lay the groundwork for a follow-up series that
introduces PMD page table swap entries
- "mm/damon: update, optimize, and clean up doc, tests, and code" (SJ
Park)
Update DAMON design and ABI documentation, expands unit and selftest
coverage, optimize damon_commit_target_regions(), and clean up
recently added sysfs interface code for better readability
- "mm/vmpressure: reduce CPU, memory and code overhead on cgroup v2"
(Usama Arif)
Optimize vmpressure() by skipping unnecessary work on cgroup v2 for
userspace event notifications and refactor v1-only eventfd handling
into mm/memcontrol-v1.c to reduce memory overhead and code complexity
- "selftests/mm: refactor pkey helpers and fix mmap error handling"
(Hongfu Li)
Refactor pkeys shared tracing and assertion helpers into a common
file, unify protection key selftests to use consistent diagnostic
logging and assertions, and enforce standardized MAP_FAILED return
checks for mmap() calls across the tests
- "mm/damon: optimize out nr_accesses_bp" (SJ Park)
Replace the error-prone, continuously updated nr_accesses_bp field in
damon_region with an on-demand moving sum function, reducing
structure memory overhead and avoiding state corruption bugs
- "Open HugeTLB allocation routine for more generic use" (Ackerley Tng)
Decouple HugeTLB folio allocation from VMA dependencies by
introducing hugetlb_alloc_folio(), enabling subsystems like
guest_memfd to allocate HugeTLB folios without standard VMA
reservations or pseudo-VMAs
- "mm/damon: provide pseudo moving sum probe_hits" (SJ Park)
Integrate DAMON's probe_hits attribute counter into the pseudo moving
sum infrastructure, enabling real-time, online monitoring without
waiting for full aggregation intervals
- "mm: Some cleanups for page allocator APIs" (Brendan Jackman)
Simplify and refactor the page allocator entry points and flags by
unifying allocation paths, adding internal alloc_flags arguments, and
eliminating redundant __ prefixed alloc_pages variants.
- "Fix incorrect access of hugetlb pte entries" (Dev Jain)
Enforce the consistent use of huge_ptep_get() instead of ptep_get()
for HugeTLB entries and fixes an unaligned address issue in arm64's
huge_ptep_get() implementation
- "mm/damon: validate all parameters in the core" (SJ Park)
Consolidate parameter validation into the DAMON core specifically
within damon_start() and damon_commit_ctx() to centralize error
checking, eliminate caller-side redundant checks and to improve
maintenance efficiency
- "tools/mm/page_owner_sort: fix filtering and cleanup issues" (Yichong
Chen)
Rename is_need() to filter_record() for clearer return semantics, fix
per-record allocation memory leaks and bound output copies in
search_pattern() to address an existing buffer issue
- "memcg: bail out reclaim when memcg is dying" (Jiayuan Chen)
Mitigate a system-wide stall which occurs when a cgroup is removed
while one of its memory control files is doing synchronous reclaim
- "mm/memory-failure: add panic option for unrecoverable pages" (Breno
Leitao)
Introduce an opt-in vm.panic_on_unrecoverable_memory_failure sysctl
that immediately panics the kernel on unrecoverable memory errors in
kernel-owned pages to preserve error context and prevent delayed,
silent data corruption
- "mm/damon: refactor damon_{start,stop,commit}() for simple error
handling" (SJ Park)
Refactor the DAMON core API functions to guarantee that all contexts
are fully stopped when damon_start(), damon_stop(), or damon_commit()
fail, eliminating the need for complex and error-prone caller-side
cleanup code
- "Keep tail page private zero at free and folio split" (Zi Yan)
Add checks to ensure tail_page->private is zero when freeing compound
or high-order pages and when promoting tail pages during large folio
splits. By validating these fields at free and split time, it allows
the removal of redundant private field clearing inside
prep_compound_tail()
- "mm: drop redundant lru_add_drain in anon folio reuse paths" (Barry
Song)
Eliminate redundant lru_add_drain() calls in
wp_can_reuse_anon_folio() and do_swap_page() to reduce LRU lock
contention and system overhead
By validating folio refcounts against the LRU cache before draining
and removing unnecessary drains in the swap path, it achieves up to a
30.5% reduction in drain calls during heavy swap workloads
- "mm: clean up folio LRU and swap declarations" (Jianyue Wu)
Reorganize folio LRU and swap code by relocating page-cluster state
to mm/swap_state.c, renaming mm/swap.c to mm/folio.c, and moving
MM-internal reclaim declarations into mm/internal.h.
- "userfaultfd: working set tracking for VM guest memory" (Kiryl
Shutsemau)
Add userfaultfd support for tracking the working set of VM guest
memory, so a VMM can identify hot pages and reclaim cold ones to
tiered or remote storage
- "mm: remove CONFIG_HAVE_BOOTMEM_INFO_NODE (Part 2)" (David
Hildenbrand)
Remove the remaining pieces of CONFIG_HAVE_BOOTMEM_INFO_NODE,
performing some smaller cleanups around freeing of reserved vmemmap
pages on the way.
- "mm/damon: update probe hits for runtime parameter commits" (SJ Park)
Ensure that DAMON's probe_hits attribute counter is properly updated
when monitoring intervals are changed at runtime, matching the
behavior of nr_accesses. To achieve this, it refactors and renames
existing helper functions for shared use, applies the updates to
probe_hits, and handles edge cases in damon_probe_hits_mvsum() to
maintain measurement accuracy.
- "KSM: performance optimizations for rmap_walk_ksm" (xu xin)
Resolve a severe KSM reverse-mapping performance bottleneck where
thousands of split VMAs sharing a single anon_vma cause extended lock
contention.
By adding an interval-filtering check during the rmap walk, it
reduces worst-case anon_vma lock hold times from over 500ms down to
under 2ms, preventing application freezes and latency spikes under
memory pressure.
- "mm: split a couple of headers from internal.h" (Mike Rapoport)
Split declarations related to mm_init, memblock, vmalloc and sparse
into new headers
- "KSM: use linear_page_index in collect_procs_ksm()" (xu xin)
Apply the interval tree optimization from rmap_walk_ksm() to
collect_procs_ksm() to avoid iterating over non-matching VMAs during
KSM memory error handling.
It hoists loop-invariant address initialization and restricts the
anon_vma_interval_tree_foreach walk to a targeted page offset range,
reducing redundant checks and improving lookup efficiency.
- "selftests/mm: avoid false failures in hugetlb and KSM tests" (Sayali
Patil)
Fix issues in the hugetlb and KSM MM selftest categories that can
report failures when the prerequisites for the tests are not
satisfied
- "mm/damon: introduce data attributes only monitoring" (SJ Park)
Introduce attribute-weighted region management in DAMON, allowing
users to prioritize specific data attributes (such as page sizes or
cgroups) over or instead of access monitoring.
By assigning weights to attribute probes, DAMON can completely
disable access tracking and adjust monitoring regions based on
weighted probe-hit counters to optimize monitoring quality for
attribute-focused workloads.
- "mm/hmm: Add mmap lock-drop support for userfaultfd-backed mappings"
(Stanislav Kinsburskii)
Extend hmm_range_fault() to support userfaultfd-backed regions by
allowing the mmap lock to be dropped during fault handling via a new
hmm_range_fault_locked() helper.
By accepting a locked pointer and signaling retry status when lock
release occurs, it enables page fault resolution in userfaultfd
regions while preserving backward compatibility for existing callers.
- "mm: make VMA page offset handling more consistent" (Lorenzo Stoakes)
Clean up and standardize how vma->vm_pgoff is accessed and
manipulated across file-backed and anonymous mappings in the kernel
It introduces dedicated helper functions such as vma_start_pgoff(),
vma_end_pgoff(), vma_set_pgoff() and linear_page_delta() while
renaming rmap interval tree helpers to better reflect their
functionality.
These changes establish a cleaner foundation for future work that
will unify virtual page offset indexing for all anonymous and CoW'd
folios.
- "mm: handle device-private PMDs in walk callbacks" (Usama Arif)
Address kernel panics and state corruption caused by MM walk
callbacks reaching non-present device-private PMD swap entries
created during HMM migrations
It ensures that functions which acquire pmd_trans_huge_lock()
properly recognize device-private PMDs instead of assuming a present
THP or a standard migration entry.
- "mm/rmap: Refactor try_to_unmap_one" (Dev Jain)
Refactor try_to_unmap_one by modularizing Hugetlb,
anonymous-lazyfree, and anonymous-swapbacked logic into dedicated
functions, laying the structural groundwork for batched anonymous
large folio unmapping.
- "Docs/ABI/damon: sysfs ABI document fixes and additions" (Song Hu)
Fix typos and fills in missing entries in the DAMON sysfs ABI
document
- "dax/kmem: atomic whole-device hotplug via sysfs" (Gregory Price)
Introduce an atomic sysfs state attribute and supporting DAX/MM
infrastructure to prevent userland races when offlining and removing
entire memory regions
By adding an unplugged state alongside standard online modes, it
enables whole-device atomic hotplug control while preserving backward
compatibility.
- "mm: convert more vm_flags_t users to vma_flags_t" (Lorenzo Stoakes)
Continue transitioning the kernel from the deprecated vm_flags_t type
to vma_flags_t across core memory management infrastructure.
It replaces legacy type usage in core functions such as do_mmap(),
unmapped area allocation, mm->def_vma_flags, and VMA operations like
mlock, mprotect, and mremap.
- "Two small patches to clean up mm/mm_slot.h" (xu xin)
Refactor mm_slot.h by introducing mm_slot_remove() to unify duplicate
slot deletion sequences in khugepaged and KSM. It also adds code
documentation explaining why mm_slot_lookup and mm_slot_insert must
remain as preprocessor macros rather than static inline functions.
- "mm/damon/core: hide core-private struct fields" (SJ Park)
Clean up DAMON core structures by consistently marking internal-only
fields with private: comment tags to prevent improper direct access
from outer layers.
It enforces encapsulation across core structures including
damon_region, damon_target, and damon_ctx and updates DAMON_SYSFS to
interact through approved access APIs instead of exposing raw struct
members.
- "mm/damon: unurgent fixes for infinite loop, NULL de-ref and races"
(SJ Park)
Address potential infinite loops, NULL dereferences, and race
conditions identified in DAMON
It fixes an infinite loop triggered by extreme user configurations, a
NULL pointer dereference within unit tests and minor monitoring
accuracy degradation caused by subtle runtime races.
- "mm/page_alloc: fixes for free_pages_nolock() on RT/UP" (Brendan
Jackman)
Fix an NMI safety flaw in __free_frozen_pages() where freeing pages
on non-SMP or PREEMPT_RT kernels can bypass can_spin_trylock() checks
via non-PCP or isolated migration paths.
It also resolves potential kernel crashes and privilege escalation
risks triggered when BPF tracing runs in NMI context alongside memory
hotplug or large allocation frees.
- "mm/page_alloc: couple of followups for recent cleanups" (Brendan
Jackman)
Clean up and update page allocator nomenclature, documentation, and
debug assertions.
It aligns internal FPI_ flags with the public "nolock" naming
convention, removes outdated internal implementation details from
high-level page allocator comments, and eliminates obsolete
VM_BUG_ON() assertions in allocation paths.
- "mm/mseal: further cleanups" (Lorenzo Stoakes)
Refactor and simplify the mseal implementation by clarifying API
boundaries and removing unnecessary code complexity.
It replaces generic do_mseal() usage outside the syscall with a
dedicated mseal_mmap_page_zero() helper for MMAP_PAGE_ZERO,
eliminates mm_struct parameters to enforce that sealing applies only
to current->mm, and streamlines overall logic and comments with no
functional changes intended.
- "mm/vmscan: fix swappiness=max and clean up per-node proactive
reclaim" (Ridong Chen)
Resolve reclaim behavior bugs and clean up function parameters across
memory reclaim paths
It fixes swappiness=max in both standard reclaim and MGLRU so
unswappable anonymous memory no longer falls back to evicting page
cache, ensures reclaim_store() returns accurate error codes instead
of collapsing all failures into -EAGAIN, and removes the obsolete
gfp_mask parameter from __node_reclaim().
- "mm: mincore: misc cleanups" (Kefeng Wang)
Clean up and simplifies the mincore code. Most importantly, it
removes the historical special behavior that always reports VM_PFNMAP
pages as non-resident.
- "mm/huge_memory: drop dead split helper variants" (Kiryl Shutsemau)
Two trivial cleanups in the folio split API
- "mm/damon: fix uninitialized DAMOS field and kunit exec expectation
bugs" (SJ Park)
Resolve minor operational and testing bugs in DAMON identified by
Sashiko. It initializes the damos->last_applied field to prevent
occasional efficiency degradation and fixes invalid memory accesses
in DAMON KUnit tests during test failure handling.
- "cleanup for stable_page_flags()" (Jinjiang Tu)
Clean up and refactor stable_page_flags() used by /proc/kpageflags
without altering functionality.
It uses BIT_ULL() to prevent shift-overflow warnings on 64-bit flag
bits, converts folio-specific flag checks to standard folio_test_*()
helpers, and removes redundant CONFIG_PAGE_IDLE_FLAG handling.
- "Batch unmap of uffd-wp file folios" (Dev Jain)
Extend batched folio unmapping support to file folios within
userfaultfd write-protect (uffd-wp) VMAs by adding batching
capabilities to pte_install_uffd_wp_if_needed().
This removes special-case restrictions on uffd-wp VMAs in
try_to_unmap_one(), significantly simplifying the function's control
flow and complexity.
- "mm/early_ioremap: clarify and clean up early_ioremap_reset()"
(Sang-Heon Jeon)
Clarify and clean up the architecture-specific usage of
__late_set_fixmap() and __late_clear_fixmap() after
early_ioremap_reset()
It adds explicit documentation regarding when early_ioremap_reset()
must be called and removes redundant macro definitions and reset
calls in the RISC-V and ARM64 architectures.
- "mm: fix reclaim storms in defrag_mode" (Johannes Weiner)
Address severe performance regressions, swap storms, and spurious
OOMs caused by vm.defrag_mode=1 under high memory pressure in Meta
production
It updates the page allocator slowpath so non-movable allocation
requests actively trigger direct reclaim and direct compaction at
pageblock_order scale, allowing them to claim whole pageblocks rather
than spinning unproductively.
- "zram: lockmap tweaks" (Sebastian Siewior)
Optimize and fix lockdep tracking for zram devices by consolidating
per-entry lockmaps and isolate lock classes across multiple instances
This reduces memory overhead by replacing per-entry lockdep_map
instances with a single map per struct zram, and assigns a dynamic
lock_class_key to each instance to prevent false deadlock reports
when different zram devices are backed by distinct filesystems.
* tag 'mm-stable-2026-08-18-18-39' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm: (501 commits)
selftests/mm: thuge-gen: fix test_shmget() for PAGE_SIZE check
selftests/mm: unpoison pages in memory-failure teardown
mm/shmem: downgrade final i_blocks check in shmem_evict_inode() to pr_warn()
mm/khugepaged: replace mutex_lock/mutex_unlock usage with guard macro
mm/zsmalloc: fix release order of locks in zs_page_migrate()
Documentation: zram: remove sections numbering
ksm: stop iterating VMAs when ksm_test_exit returns true
mm: fold userfaultfd_rwp() to false without CONFIG_ARCH_HAS_PTE_PROTNONE
mm/migrate: report RCU-tasks quiescent states in migrate_pages_batch()
zram: use a custom key for each zram object
zram: move lockmap to be per-zram instead per table
selftests/mm: fix gup_longterm EINVAL error message
mm: page_alloc: fix non-movable reclaim storm in defrag_mode
mm: page_alloc: move capture_control to the page allocator
mm: compaction: support non-movable compaction for pageblock requests
mm: page_alloc: __GFP_FS lockdep annotation for direct compaction
hugetlb: evaluate subpool free state while locked
mm/damon: remove trailing semicolons after function definitions
mm/damon/ops-common: prevent migration fallback to non-target nodes
mm/damon: update outdated comment about DAMOS filter handling
...
646 lines
19 KiB
C
646 lines
19 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _LINUX_MMAP_LOCK_H
|
|
#define _LINUX_MMAP_LOCK_H
|
|
|
|
/* Avoid a dependency loop by declaring here. */
|
|
extern int rcuwait_wake_up(struct rcuwait *w);
|
|
|
|
#include <linux/lockdep.h>
|
|
#include <linux/mm_types.h>
|
|
#include <linux/mmdebug.h>
|
|
#include <linux/rwsem.h>
|
|
#include <linux/tracepoint-defs.h>
|
|
#include <linux/types.h>
|
|
#include <linux/cleanup.h>
|
|
#include <linux/sched/mm.h>
|
|
|
|
#define MMAP_LOCK_INITIALIZER(name) \
|
|
.mmap_lock = __RWSEM_INITIALIZER((name).mmap_lock),
|
|
|
|
DECLARE_TRACEPOINT(mmap_lock_start_locking);
|
|
DECLARE_TRACEPOINT(mmap_lock_acquire_returned);
|
|
DECLARE_TRACEPOINT(mmap_lock_released);
|
|
|
|
#ifdef CONFIG_TRACING
|
|
|
|
void __mmap_lock_do_trace_start_locking(struct mm_struct *mm, bool write);
|
|
void __mmap_lock_do_trace_acquire_returned(struct mm_struct *mm, bool write,
|
|
bool success);
|
|
void __mmap_lock_do_trace_released(struct mm_struct *mm, bool write);
|
|
|
|
static inline void __mmap_lock_trace_start_locking(struct mm_struct *mm,
|
|
bool write)
|
|
{
|
|
if (tracepoint_enabled(mmap_lock_start_locking))
|
|
__mmap_lock_do_trace_start_locking(mm, write);
|
|
}
|
|
|
|
static inline void __mmap_lock_trace_acquire_returned(struct mm_struct *mm,
|
|
bool write, bool success)
|
|
{
|
|
if (tracepoint_enabled(mmap_lock_acquire_returned))
|
|
__mmap_lock_do_trace_acquire_returned(mm, write, success);
|
|
}
|
|
|
|
static inline void __mmap_lock_trace_released(struct mm_struct *mm, bool write)
|
|
{
|
|
if (tracepoint_enabled(mmap_lock_released))
|
|
__mmap_lock_do_trace_released(mm, write);
|
|
}
|
|
|
|
#else /* !CONFIG_TRACING */
|
|
|
|
static inline void __mmap_lock_trace_start_locking(struct mm_struct *mm,
|
|
bool write)
|
|
{
|
|
}
|
|
|
|
static inline void __mmap_lock_trace_acquire_returned(struct mm_struct *mm,
|
|
bool write, bool success)
|
|
{
|
|
}
|
|
|
|
static inline void __mmap_lock_trace_released(struct mm_struct *mm, bool write)
|
|
{
|
|
}
|
|
|
|
#endif /* CONFIG_TRACING */
|
|
|
|
static inline void mmap_assert_locked(const struct mm_struct *mm)
|
|
{
|
|
rwsem_assert_held(&mm->mmap_lock);
|
|
}
|
|
|
|
static inline void mmap_assert_write_locked(const struct mm_struct *mm)
|
|
{
|
|
rwsem_assert_held_write(&mm->mmap_lock);
|
|
}
|
|
|
|
#ifdef CONFIG_PER_VMA_LOCK
|
|
|
|
#ifdef CONFIG_LOCKDEP
|
|
#define __vma_lockdep_map(vma) (&vma->vmlock_dep_map)
|
|
#else
|
|
#define __vma_lockdep_map(vma) NULL
|
|
#endif
|
|
|
|
/*
|
|
* VMA locks do not behave like most ordinary locks found in the kernel, so we
|
|
* cannot quite have full lockdep tracking in the way we would ideally prefer.
|
|
*
|
|
* Read locks act as shared locks which exclude an exclusive lock being
|
|
* taken. We therefore mark these accordingly on read lock acquire/release.
|
|
*
|
|
* Write locks are acquired exclusively per-VMA, but released in a shared
|
|
* fashion, that is upon vma_end_write_all(), we update the mmap's seqcount such
|
|
* that write lock is released.
|
|
*
|
|
* We therefore cannot track write locks per-VMA, nor do we try. Mitigating this
|
|
* is the fact that, of course, we do lockdep-track the mmap lock rwsem which
|
|
* must be held when taking a VMA write lock.
|
|
*
|
|
* We do, however, want to indicate that during either acquisition of a VMA
|
|
* write lock or detachment of a VMA that we require the lock held be exclusive,
|
|
* so we utilise lockdep to do so.
|
|
*/
|
|
#define __vma_lockdep_acquire_read(vma) \
|
|
lock_acquire_shared(__vma_lockdep_map(vma), 0, 1, NULL, _RET_IP_)
|
|
#define __vma_lockdep_release_read(vma) \
|
|
lock_release(__vma_lockdep_map(vma), _RET_IP_)
|
|
#define __vma_lockdep_acquire_exclusive(vma) \
|
|
lock_acquire_exclusive(__vma_lockdep_map(vma), 0, 0, NULL, _RET_IP_)
|
|
#define __vma_lockdep_release_exclusive(vma) \
|
|
lock_release(__vma_lockdep_map(vma), _RET_IP_)
|
|
/* Only meaningful if CONFIG_LOCK_STAT is defined. */
|
|
#define __vma_lockdep_stat_mark_acquired(vma) \
|
|
lock_acquired(__vma_lockdep_map(vma), _RET_IP_)
|
|
|
|
static inline void mm_lock_seqcount_init(struct mm_struct *mm)
|
|
{
|
|
seqcount_init(&mm->mm_lock_seq);
|
|
}
|
|
|
|
static inline void mm_lock_seqcount_begin(struct mm_struct *mm)
|
|
{
|
|
do_raw_write_seqcount_begin(&mm->mm_lock_seq);
|
|
}
|
|
|
|
static inline void mm_lock_seqcount_end(struct mm_struct *mm)
|
|
{
|
|
ASSERT_EXCLUSIVE_WRITER(mm->mm_lock_seq);
|
|
do_raw_write_seqcount_end(&mm->mm_lock_seq);
|
|
}
|
|
|
|
static inline bool mmap_lock_speculate_try_begin(struct mm_struct *mm, unsigned int *seq)
|
|
{
|
|
/*
|
|
* Since mmap_lock is a sleeping lock, and waiting for it to become
|
|
* unlocked is more or less equivalent with taking it ourselves, don't
|
|
* bother with the speculative path if mmap_lock is already write-locked
|
|
* and take the slow path, which takes the lock.
|
|
*/
|
|
return raw_seqcount_try_begin(&mm->mm_lock_seq, *seq);
|
|
}
|
|
|
|
static inline bool mmap_lock_speculate_retry(struct mm_struct *mm, unsigned int seq)
|
|
{
|
|
return read_seqcount_retry(&mm->mm_lock_seq, seq);
|
|
}
|
|
|
|
static inline void vma_lock_init(struct vm_area_struct *vma, bool reset_refcnt)
|
|
{
|
|
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
|
static struct lock_class_key lockdep_key;
|
|
|
|
lockdep_init_map(__vma_lockdep_map(vma), "vm_lock", &lockdep_key, 0);
|
|
#endif
|
|
if (reset_refcnt)
|
|
refcount_set(&vma->vm_refcnt, 0);
|
|
vma->vm_lock_seq = UINT_MAX;
|
|
}
|
|
|
|
/*
|
|
* This function determines whether the input VMA reference count describes a
|
|
* VMA which has excluded all VMA read locks.
|
|
*
|
|
* In the case of a detached VMA, we may incorrectly indicate that readers are
|
|
* excluded when one remains, because in that scenario we target a refcount of
|
|
* VM_REFCNT_EXCLUDE_READERS_FLAG, rather than the attached target of
|
|
* VM_REFCNT_EXCLUDE_READERS_FLAG + 1.
|
|
*
|
|
* However, the race window for that is very small so it is unlikely.
|
|
*
|
|
* Returns: true if readers are excluded, false otherwise.
|
|
*/
|
|
static inline bool __vma_are_readers_excluded(int refcnt)
|
|
{
|
|
/*
|
|
* See the comment describing the vm_area_struct->vm_refcnt field for
|
|
* details of possible refcnt values.
|
|
*/
|
|
return (refcnt & VM_REFCNT_EXCLUDE_READERS_FLAG) &&
|
|
refcnt <= VM_REFCNT_EXCLUDE_READERS_FLAG + 1;
|
|
}
|
|
|
|
/*
|
|
* Actually decrement the VMA reference count.
|
|
*
|
|
* The function returns the reference count as it was immediately after the
|
|
* decrement took place. If it returns zero, the VMA is now detached.
|
|
*/
|
|
static inline __must_check unsigned int
|
|
__vma_refcount_put_return(struct vm_area_struct *vma)
|
|
{
|
|
int oldcnt;
|
|
|
|
if (__refcount_dec_and_test(&vma->vm_refcnt, &oldcnt))
|
|
return 0;
|
|
|
|
return oldcnt - 1;
|
|
}
|
|
|
|
/**
|
|
* vma_refcount_put() - Drop reference count in VMA vm_refcnt field due to a
|
|
* read-lock being dropped.
|
|
* @vma: The VMA whose reference count we wish to decrement.
|
|
*
|
|
* If we were the last reader, wake up threads waiting to obtain an exclusive
|
|
* lock.
|
|
*/
|
|
static inline void vma_refcount_put(struct vm_area_struct *vma)
|
|
{
|
|
/* Use a copy of vm_mm in case vma is freed after we drop vm_refcnt. */
|
|
struct mm_struct *mm = vma->vm_mm;
|
|
int newcnt;
|
|
|
|
__vma_lockdep_release_read(vma);
|
|
newcnt = __vma_refcount_put_return(vma);
|
|
|
|
/*
|
|
* __vma_start_exclude_readers() may be sleeping waiting for readers to
|
|
* drop their reference count, so wake it up if we were the last reader
|
|
* blocking it from being acquired.
|
|
*
|
|
* We may be raced by other readers temporarily incrementing the
|
|
* reference count, though the race window is very small, this might
|
|
* cause spurious wakeups.
|
|
*/
|
|
if (newcnt && __vma_are_readers_excluded(newcnt))
|
|
rcuwait_wake_up(&mm->vma_writer_wait);
|
|
}
|
|
|
|
/*
|
|
* Use only while holding mmap read lock which guarantees that locking will not
|
|
* fail (nobody can concurrently write-lock the vma). vma_start_read() should
|
|
* not be used in such cases because it might fail due to mm_lock_seq overflow.
|
|
* This functionality is used to obtain vma read lock and drop the mmap read lock.
|
|
*/
|
|
static inline bool vma_start_read_locked_nested(struct vm_area_struct *vma, int subclass)
|
|
{
|
|
int oldcnt;
|
|
|
|
mmap_assert_locked(vma->vm_mm);
|
|
if (unlikely(!__refcount_inc_not_zero_limited_acquire(&vma->vm_refcnt, &oldcnt,
|
|
VM_REFCNT_LIMIT)))
|
|
return false;
|
|
|
|
__vma_lockdep_acquire_read(vma);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Use only while holding mmap read lock which guarantees that locking will not
|
|
* fail (nobody can concurrently write-lock the vma). vma_start_read() should
|
|
* not be used in such cases because it might fail due to mm_lock_seq overflow.
|
|
* This functionality is used to obtain vma read lock and drop the mmap read lock.
|
|
*/
|
|
static inline bool vma_start_read_locked(struct vm_area_struct *vma)
|
|
{
|
|
return vma_start_read_locked_nested(vma, 0);
|
|
}
|
|
|
|
static inline void vma_end_read(struct vm_area_struct *vma)
|
|
{
|
|
vma_refcount_put(vma);
|
|
}
|
|
|
|
static inline unsigned int __vma_raw_mm_seqnum(struct vm_area_struct *vma)
|
|
{
|
|
const struct mm_struct *mm = vma->vm_mm;
|
|
|
|
/* We must hold an exclusive write lock for this access to be valid. */
|
|
mmap_assert_write_locked(vma->vm_mm);
|
|
return mm->mm_lock_seq.sequence;
|
|
}
|
|
|
|
/*
|
|
* Determine whether a VMA is write-locked. Must be invoked ONLY if the mmap
|
|
* write lock is held.
|
|
*
|
|
* Returns true if write-locked, otherwise false.
|
|
*/
|
|
static inline bool __is_vma_write_locked(struct vm_area_struct *vma)
|
|
{
|
|
/*
|
|
* current task is holding mmap_write_lock, both vma->vm_lock_seq and
|
|
* mm->mm_lock_seq can't be concurrently modified.
|
|
*/
|
|
return vma->vm_lock_seq == __vma_raw_mm_seqnum(vma);
|
|
}
|
|
|
|
int __vma_start_write(struct vm_area_struct *vma, int state);
|
|
|
|
/*
|
|
* Begin writing to a VMA.
|
|
* Exclude concurrent readers under the per-VMA lock until the currently
|
|
* write-locked mmap_lock is dropped or downgraded.
|
|
*/
|
|
static inline void vma_start_write(struct vm_area_struct *vma)
|
|
{
|
|
if (__is_vma_write_locked(vma))
|
|
return;
|
|
|
|
__vma_start_write(vma, TASK_UNINTERRUPTIBLE);
|
|
}
|
|
|
|
/**
|
|
* vma_start_write_killable - Begin writing to a VMA.
|
|
* @vma: The VMA we are going to modify.
|
|
*
|
|
* Exclude concurrent readers under the per-VMA lock until the currently
|
|
* write-locked mmap_lock is dropped or downgraded.
|
|
*
|
|
* Context: May sleep while waiting for readers to drop the vma read lock.
|
|
* Caller must already hold the mmap_lock for write.
|
|
*
|
|
* Return: 0 for a successful acquisition. -EINTR if a fatal signal was
|
|
* received.
|
|
*/
|
|
static inline __must_check
|
|
int vma_start_write_killable(struct vm_area_struct *vma)
|
|
{
|
|
if (__is_vma_write_locked(vma))
|
|
return 0;
|
|
|
|
return __vma_start_write(vma, TASK_KILLABLE);
|
|
}
|
|
|
|
/**
|
|
* vma_assert_write_locked() - assert that @vma holds a VMA write lock.
|
|
* @vma: The VMA to assert.
|
|
*/
|
|
static inline void vma_assert_write_locked(struct vm_area_struct *vma)
|
|
{
|
|
VM_WARN_ON_ONCE_VMA(!__is_vma_write_locked(vma), vma);
|
|
}
|
|
|
|
/**
|
|
* vma_assert_locked() - assert that @vma holds either a VMA read or a VMA write
|
|
* lock and is not detached.
|
|
* @vma: The VMA to assert.
|
|
*/
|
|
static inline void vma_assert_locked(struct vm_area_struct *vma)
|
|
{
|
|
unsigned int refcnt;
|
|
|
|
if (IS_ENABLED(CONFIG_LOCKDEP)) {
|
|
if (!lock_is_held(__vma_lockdep_map(vma)))
|
|
vma_assert_write_locked(vma);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* See the comment describing the vm_area_struct->vm_refcnt field for
|
|
* details of possible refcnt values.
|
|
*/
|
|
refcnt = refcount_read(&vma->vm_refcnt);
|
|
|
|
/*
|
|
* In this case we're either read-locked, write-locked with temporary
|
|
* readers, or in the midst of excluding readers, all of which means
|
|
* we're locked.
|
|
*/
|
|
if (refcnt > 1)
|
|
return;
|
|
|
|
/* It is a bug for the VMA to be detached here. */
|
|
VM_WARN_ON_ONCE_VMA(!refcnt, vma);
|
|
|
|
/*
|
|
* OK, the VMA has a reference count of 1 which means it is either
|
|
* unlocked and attached or write-locked, so assert that it is
|
|
* write-locked.
|
|
*/
|
|
vma_assert_write_locked(vma);
|
|
}
|
|
|
|
/**
|
|
* vma_assert_stabilised() - assert that this VMA cannot be changed from
|
|
* underneath us either by having a VMA or mmap lock held.
|
|
* @vma: The VMA whose stability we wish to assess.
|
|
*
|
|
* If lockdep is enabled we can precisely ensure stability via either an mmap
|
|
* lock owned by us or a specific VMA lock.
|
|
*
|
|
* With lockdep disabled we may sometimes race with other threads acquiring the
|
|
* mmap read lock simultaneous with our VMA read lock.
|
|
*/
|
|
static inline void vma_assert_stabilised(struct vm_area_struct *vma)
|
|
{
|
|
/*
|
|
* If another thread owns an mmap lock, it may go away at any time, and
|
|
* thus is no guarantee of stability.
|
|
*
|
|
* If lockdep is enabled we can accurately determine if an mmap lock is
|
|
* held and owned by us. Otherwise we must approximate.
|
|
*
|
|
* It doesn't necessarily mean we are not stabilised however, as we may
|
|
* hold a VMA read lock (not a write lock as this would require an owned
|
|
* mmap lock).
|
|
*
|
|
* If (assuming lockdep is not enabled) we were to assert a VMA read
|
|
* lock first we may also run into issues, as other threads can hold VMA
|
|
* read locks simlutaneous to us.
|
|
*
|
|
* Therefore if lockdep is not enabled we risk a false negative (i.e. no
|
|
* assert fired). If accurate checking is required, enable lockdep.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_LOCKDEP)) {
|
|
if (lockdep_is_held(&vma->vm_mm->mmap_lock))
|
|
return;
|
|
} else {
|
|
if (rwsem_is_locked(&vma->vm_mm->mmap_lock))
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* We're not stabilised by the mmap lock, so assert that we're
|
|
* stabilised by a VMA lock.
|
|
*/
|
|
vma_assert_locked(vma);
|
|
}
|
|
|
|
static inline bool vma_is_attached(struct vm_area_struct *vma)
|
|
{
|
|
return refcount_read(&vma->vm_refcnt);
|
|
}
|
|
|
|
/*
|
|
* WARNING: to avoid racing with vma_mark_attached()/vma_mark_detached(), these
|
|
* assertions should be made either under mmap_write_lock or when the object
|
|
* has been isolated under mmap_write_lock, ensuring no competing writers.
|
|
*/
|
|
static inline void vma_assert_attached(struct vm_area_struct *vma)
|
|
{
|
|
WARN_ON_ONCE(!vma_is_attached(vma));
|
|
}
|
|
|
|
static inline void vma_assert_detached(struct vm_area_struct *vma)
|
|
{
|
|
WARN_ON_ONCE(vma_is_attached(vma));
|
|
}
|
|
|
|
static inline void vma_mark_attached(struct vm_area_struct *vma)
|
|
{
|
|
vma_assert_write_locked(vma);
|
|
vma_assert_detached(vma);
|
|
refcount_set_release(&vma->vm_refcnt, 1);
|
|
}
|
|
|
|
void __vma_exclude_readers_for_detach(struct vm_area_struct *vma);
|
|
|
|
static inline void vma_mark_detached(struct vm_area_struct *vma)
|
|
{
|
|
vma_assert_write_locked(vma);
|
|
vma_assert_attached(vma);
|
|
|
|
/*
|
|
* The VMA still being attached (refcnt > 0) - is unlikely, because the
|
|
* vma has been already write-locked and readers can increment vm_refcnt
|
|
* only temporarily before they check vm_lock_seq, realize the vma is
|
|
* locked and drop back the vm_refcnt. That is a narrow window for
|
|
* observing a raised vm_refcnt.
|
|
*
|
|
* See the comment describing the vm_area_struct->vm_refcnt field for
|
|
* details of possible refcnt values.
|
|
*/
|
|
if (likely(!__vma_refcount_put_return(vma)))
|
|
return;
|
|
|
|
__vma_exclude_readers_for_detach(vma);
|
|
}
|
|
|
|
struct vm_area_struct *lock_vma_under_rcu(struct mm_struct *mm,
|
|
unsigned long address);
|
|
|
|
/*
|
|
* Locks next vma pointed by the iterator. Confirms the locked vma has not
|
|
* been modified and will retry under mmap_lock protection if modification
|
|
* was detected. Should be called from read RCU section.
|
|
* Returns either a valid locked VMA, NULL if no more VMAs or -EINTR if the
|
|
* process was interrupted.
|
|
*/
|
|
struct vm_area_struct *lock_next_vma(struct mm_struct *mm,
|
|
struct vma_iterator *iter,
|
|
unsigned long address);
|
|
|
|
#else /* CONFIG_PER_VMA_LOCK */
|
|
|
|
static inline void mm_lock_seqcount_init(struct mm_struct *mm) {}
|
|
static inline void mm_lock_seqcount_begin(struct mm_struct *mm) {}
|
|
static inline void mm_lock_seqcount_end(struct mm_struct *mm) {}
|
|
|
|
static inline bool mmap_lock_speculate_try_begin(struct mm_struct *mm, unsigned int *seq)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static inline bool mmap_lock_speculate_retry(struct mm_struct *mm, unsigned int seq)
|
|
{
|
|
return true;
|
|
}
|
|
static inline void vma_lock_init(struct vm_area_struct *vma, bool reset_refcnt) {}
|
|
static inline void vma_end_read(struct vm_area_struct *vma) {}
|
|
static inline void vma_start_write(struct vm_area_struct *vma) {}
|
|
static inline __must_check
|
|
int vma_start_write_killable(struct vm_area_struct *vma) { return 0; }
|
|
static inline void vma_assert_write_locked(struct vm_area_struct *vma)
|
|
{ mmap_assert_write_locked(vma->vm_mm); }
|
|
static inline bool vma_is_attached(struct vm_area_struct *vma)
|
|
{ return true; }
|
|
static inline void vma_assert_attached(struct vm_area_struct *vma) {}
|
|
static inline void vma_assert_detached(struct vm_area_struct *vma) {}
|
|
static inline void vma_mark_attached(struct vm_area_struct *vma) {}
|
|
static inline void vma_mark_detached(struct vm_area_struct *vma) {}
|
|
|
|
static inline struct vm_area_struct *lock_vma_under_rcu(struct mm_struct *mm,
|
|
unsigned long address)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static inline void vma_assert_locked(struct vm_area_struct *vma)
|
|
{
|
|
mmap_assert_locked(vma->vm_mm);
|
|
}
|
|
|
|
static inline void vma_assert_stabilised(struct vm_area_struct *vma)
|
|
{
|
|
/* If no VMA locks, then either mmap lock suffices to stabilise. */
|
|
mmap_assert_locked(vma->vm_mm);
|
|
}
|
|
|
|
#endif /* CONFIG_PER_VMA_LOCK */
|
|
|
|
static inline void vma_assert_can_modify(struct vm_area_struct *vma)
|
|
{
|
|
if (vma_is_attached(vma))
|
|
vma_assert_write_locked(vma);
|
|
}
|
|
|
|
static inline void mmap_write_lock(struct mm_struct *mm)
|
|
{
|
|
__mmap_lock_trace_start_locking(mm, true);
|
|
down_write(&mm->mmap_lock);
|
|
mm_lock_seqcount_begin(mm);
|
|
__mmap_lock_trace_acquire_returned(mm, true, true);
|
|
}
|
|
|
|
static inline void mmap_write_lock_nested(struct mm_struct *mm, int subclass)
|
|
{
|
|
__mmap_lock_trace_start_locking(mm, true);
|
|
down_write_nested(&mm->mmap_lock, subclass);
|
|
mm_lock_seqcount_begin(mm);
|
|
__mmap_lock_trace_acquire_returned(mm, true, true);
|
|
}
|
|
|
|
static inline int __must_check mmap_write_lock_killable(struct mm_struct *mm)
|
|
{
|
|
int ret;
|
|
|
|
__mmap_lock_trace_start_locking(mm, true);
|
|
ret = down_write_killable(&mm->mmap_lock);
|
|
if (!ret)
|
|
mm_lock_seqcount_begin(mm);
|
|
__mmap_lock_trace_acquire_returned(mm, true, ret == 0);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Drop all currently-held per-VMA locks.
|
|
* This is called from the mmap_lock implementation directly before releasing
|
|
* a write-locked mmap_lock (or downgrading it to read-locked).
|
|
* This should normally NOT be called manually from other places.
|
|
* If you want to call this manually anyway, keep in mind that this will release
|
|
* *all* VMA write locks, including ones from further up the stack.
|
|
*/
|
|
static inline void vma_end_write_all(struct mm_struct *mm)
|
|
{
|
|
mmap_assert_write_locked(mm);
|
|
mm_lock_seqcount_end(mm);
|
|
}
|
|
|
|
static inline void mmap_write_unlock(struct mm_struct *mm)
|
|
{
|
|
__mmap_lock_trace_released(mm, true);
|
|
vma_end_write_all(mm);
|
|
up_write(&mm->mmap_lock);
|
|
}
|
|
|
|
static inline void mmap_write_downgrade(struct mm_struct *mm)
|
|
{
|
|
__mmap_lock_trace_acquire_returned(mm, false, true);
|
|
vma_end_write_all(mm);
|
|
downgrade_write(&mm->mmap_lock);
|
|
}
|
|
|
|
static inline void mmap_read_lock(struct mm_struct *mm)
|
|
{
|
|
__mmap_lock_trace_start_locking(mm, false);
|
|
down_read(&mm->mmap_lock);
|
|
__mmap_lock_trace_acquire_returned(mm, false, true);
|
|
}
|
|
|
|
static inline int __must_check mmap_read_lock_killable(struct mm_struct *mm)
|
|
{
|
|
int ret;
|
|
|
|
__mmap_lock_trace_start_locking(mm, false);
|
|
ret = down_read_killable(&mm->mmap_lock);
|
|
__mmap_lock_trace_acquire_returned(mm, false, ret == 0);
|
|
return ret;
|
|
}
|
|
|
|
static inline bool __must_check mmap_read_trylock(struct mm_struct *mm)
|
|
{
|
|
bool ret;
|
|
|
|
__mmap_lock_trace_start_locking(mm, false);
|
|
ret = down_read_trylock(&mm->mmap_lock) != 0;
|
|
__mmap_lock_trace_acquire_returned(mm, false, ret);
|
|
return ret;
|
|
}
|
|
|
|
static inline void mmap_read_unlock(struct mm_struct *mm)
|
|
{
|
|
__mmap_lock_trace_released(mm, false);
|
|
up_read(&mm->mmap_lock);
|
|
}
|
|
|
|
DEFINE_GUARD(mmap_read_lock, struct mm_struct *,
|
|
mmap_read_lock(_T), mmap_read_unlock(_T))
|
|
DEFINE_GUARD_COND(mmap_read_lock, _try, mmap_read_trylock(_T))
|
|
|
|
static inline void mmap_read_unlock_non_owner(struct mm_struct *mm)
|
|
{
|
|
__mmap_lock_trace_released(mm, false);
|
|
up_read_non_owner(&mm->mmap_lock);
|
|
}
|
|
|
|
static inline int mmap_lock_is_contended(struct mm_struct *mm)
|
|
{
|
|
return rwsem_is_contended(&mm->mmap_lock);
|
|
}
|
|
|
|
#endif /* _LINUX_MMAP_LOCK_H */
|