Merge tag 'mm-stable-2026-08-26-15-22' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm

Pull more MM updates from Andrew Morton:

 - "mm/rmap: index MAP_PRIVATE file-backed folios by anonymous pgoff"
   (Lorenzo Stoakes)

   Index MAP_PRIVATE file-backed folios by their anonymous page offset
   to resolve confusion around reverse mapping for zeroed and CoW'd
   file-backed memory.

   Use this new VMA anonymous page offset tracking to eliminate index
   conflicts and lay the foundation for scalable CoW performance
   improvements.

 - "promote mapped executable folios after first usage for MGLRU"
   (Baolin Wang)

   Make MGLRU's protection of mapped executable file folios more
   reliable. Follow the classical LRU's logic, promoting mapped
   executable file folios after their first usage to give executable
   code a better chance to stay in memory and improve workload
   performance.

 - "mm: vmscan: fix node reclaim ignoring swappiness parameter" (Ridong
   Chen)

   Fix per-node proactive reclaim interface's ignoring the swappiness
   parameter when CONFIG_MEMCG is disabled by consolidating
   sc_swappiness() into a single function that checks
   proactive_swappiness regardless of kernel configuration.

 - "mm/vmscan: reduce lru_lock contention via vmstat-derived
   scan-balance cost" (Usama Arif)

   Reduce lru_lock contention in the reclaim path by deriving
   scan-balance costs from vmstat counters rather than lock-acquired
   producer updates.

   Read and decay these cost signals on the reclaim side under a
   dedicated per-lruvec lock, reducing total LRU lock wait time by over
   60% without impacting scan throughput.

 - "zram: fix zram issues reported by sashiko" (Sergey Senozhatsky)

   Fix two low-risk zram bugs which Sashiko spotted in drive-by review.

 - "Honor XA_FLAGS_ACCOUNT in xas_split_alloc() and charge to folio's
   memcg" (Zi Yan)

   Fix xas_split_alloc() by enabling target folio memcg charging during
   splits and adding the missing __GFP_ACCOUNT flag for proper XArray
   node memory accounting.

 - "selftests/mm: use pattern matching in .gitignore" (Pratyush Mallick)

   Replace hardcoded binary names in selftests/mm/.gitignore with a
   generic pattern-matching rule to automatically ignore generated test
   files and avoid manual updates when adding new tests.

 - "mm/page_ext: remove pgdat_page_ext_init()" (Sang-Heon Jeon)

   Make the incompatibility between FLATMEM and NUMA explicit in
   mm/Kconfig and remove the unused pgdat_page_ext_init() function.

 - "zram: fix zstd error paths and add parameter validation" (Haoqin
   Huang)

   Clean up zram compression backends by removing redundant error
   cleanup, adding parameter and dictionary validation, auto-prefixing
   algorithm error logs, and resetting parameters prior to
   reinitialization.

 - "zram: fix stale scan bounds after reinitialization" (Longlong Xia)

   Prevent out-of-bounds slot accesses during concurrent zram resets by
   moving table scan bound calculations under dev_lock in
   writeback_store() and read_block_state().

 - "add anon mTHP collapse test cases" (Baolin Wang)

   Extend selftests helper functions to support arbitrary page orders
   and add new test cases and options for mTHP collapse in khugepaged.

 - "selftests/mm: Handle unsupported and transient test conditions"
   (Muhammad Usama Anjum)

   Update MM selftests to report a SKIP status instead of a failure when
   required kernel or filesystem features are unsupported, while adding
   retry logic for transient page migration errors.

 - "mm/zswap: Fixes and improves the zswap shrink" (Hao Jia)

   Fix the missing zswap global shrinker when CONFIG_MEMCG is disabled
   and extend shrink_memcg() to support batch writeback for improved
   writeback efficiency.

 - "alloc_tag: introduce IOCTL-based filtering for MAP" (Suren
   Baghdasaryan)

   Introduce an IOCTL-based binary interface for memory allocation
   profiling that enables kernel-side filtering before per-CPU counter
   aggregation.

   This eliminates the text-parsing overhead of /proc/allocinfo and
   provides up to a 20x speedup by transferring only filtered allocation
   data to userspace.

 - "better block swap batching and a different take on swap_ops v5"
   (Christoph Hellwig)

   Refactor block swap I/O to use swap_iocb for batching instead of
   single-bio requests and rebase the swap_ops interface, achieving
   faster swap throughput during kernel builds.

 - "mm: kmemleak: reduce transient false positives by confirming leaks"
   (Catalin Marinas)

   Reduce false-positive kmemleak reports by combining two kmemleak
   enhancements that add a second confirmation scan and a configurable
   minimum unreferenced scan count module parameter.

 - "mm: kmemleak: default min_unref_scans to 2 for verbose kernels"
   (Breno Leitao)

   Auto-scanning kernels can generate false-positive memory leak reports
   on single scans, so this patch defaults min_unref_scans to 2 when
   CONFIG_DEBUG_KMEMLEAK_VERBOSE is enabled to require a second
   confirming scan.

 - "swap_ops updates" (Christoph Hellwig)

   Batching I/O for synchronous swap devices causes performance
   regressions and filesystem-based swap suffers from double-indirection
   overhead. This series resolves both issues by reintroducing per-folio
   writes for synchronous swap and allowing filesystems to directly
   export their own swap_ops.

 - "mm/khugepaged: several cleanups" (Nico Pache)

   khugepaged accumulated redundant state-checking patterns and outdated
   comments following mTHP integration. Introduce dedicated helpers for
   PTE validation and event counting while refreshing the internal
   documentation.

 - "maple_tree: lock checking and clean ups" (Liam Howlett)

   Syzbot reports incorrectly blame memory management exit paths for
   locking bugs, maple tree erase operations risk allocation failures
   without gfp flags and internal documentation lacks clarity.

   Improve lock error detection, update docs, fix race and allocation
   edge cases and optimize erase allocations using a fallback to
   GFP_KERNEL | GFP_NOFAIL.

* tag 'mm-stable-2026-08-26-15-22' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm: (172 commits)
  selftests/proc: make proc-maps-race work with READ_IMPLIES_EXEC
  memcg: move LRU size accounting on reparenting instead of copying it
  mm/vmscan: fix comment logic in balance_pgdat
  maple_tree: add helper mas_make_walkable()
  maple_tree: avoid extra gap calculation
  maple_tree: fix argument name in header
  maple_tree: change two GFP flags in tests
  maple_tree: document erase and allocations better
  maple_tree: avoid mas_erase() and mtree_erase() failures
  maple_tree: document that erase may use GFP_KERNEL for allocations
  maple_tree: catch race in mas_alloc_cyclic()
  maple_tree: add bulk parent set helper
  maple_tree: micro optimisation of mas_wr_store_type()
  maple_tree: optimise mas_wr_node_store() when not in rcu mode
  maple_tree: use prefetched value in mas_wr_store_type()
  maple_tree: clarify comments on mas_nomem()
  maple_tree: drop MAPLE_ALLOC_SLOTS
  maple_tree: drop dead code from mas_extend_spanning_null()
  maple_tree: documentation fix
  maple_tree: add write lock checking with lockdep sequence numbers
  ...
This commit is contained in:
Linus Torvalds
2026-08-27 09:17:06 -07:00
168 changed files with 4572 additions and 1781 deletions

View File

@@ -109,14 +109,41 @@ path to the `dict` along with other parameters::
#pass path to pre-trained zstd dictionary
echo "algo=zstd dict=/etc/dictionary" > /sys/block/zram0/algorithm_params
#same, but using algorithm priority
echo "priority=1 dict=/etc/dictionary" > \
/sys/block/zram0/algorithm_params
#pass path to pre-trained zstd dictionary and compression level
echo "algo=zstd level=8 dict=/etc/dictionary" > \
/sys/block/zram0/algorithm_params
#same, but using algorithm priority
echo "algo=zstd priority=1" > /sys/block/zram0/recomp_algorithm
echo "priority=1 dict=/etc/dictionary" > \
/sys/block/zram0/algorithm_params
Each write to `algorithm_params` replaces the entire set of parameters of
the corresponding algorithm, parameters that are not listed in the write
are reset to their default values. Configure all of the parameters of an
algorithm in one write::
#WRONG: the second write resets level back to its default value
echo "algo=zstd level=8" > /sys/block/zram0/algorithm_params
echo "algo=zstd dict=/etc/dictionary" > /sys/block/zram0/algorithm_params
#RIGHT
echo "algo=zstd level=8 dict=/etc/dictionary" > \
/sys/block/zram0/algorithm_params
Select the compression algorithm before configuring its parameters. The
parameters of one algorithm are not necessarily valid for another one, so
changing the algorithm of a particular priority resets that priority's
parameters::
#WRONG: comp_algorithm write resets the previously configured level
echo "level=8" > /sys/block/zram0/algorithm_params
echo zstd > /sys/block/zram0/comp_algorithm
#RIGHT
echo zstd > /sys/block/zram0/comp_algorithm
echo "algo=zstd level=8" > /sys/block/zram0/algorithm_params
Parameters are algorithm specific: not all algorithms support pre-trained
dictionaries, not all algorithms support `level`. Furthermore, for certain
algorithms `level` controls the compression level (the higher the value the
@@ -124,6 +151,11 @@ better the compression ratio, it even can take negatives values for some
algorithms), for other algorithms `level` is acceleration level (the higher
the value the lower the compression ratio).
Parameters are handed over to the compression algorithm when the device is
initialised, hence invalid parameters (or parameters that the selected
algorithm does not support) are reported by the `disksize` write, and not
by the `algorithm_params` write that has configured them.
Set Disksize
============

View File

@@ -2077,8 +2077,14 @@ Kernel parameters
hugetlb_cma= [HW,CMA,EARLY] The size of a CMA area used for allocation
of gigantic hugepages. Or using node format, the size
of a CMA area per node can be specified.
Format: nn[KMGTPE] or (node format)
<node>:nn[KMGTPE][,<node>:nn[KMGTPE]]
The size can be an absolute value (e.g., 2G) or a
percentage of the total memory or node memory (e.g., 20%).
Percentage-derived sizes are rounded down to a multiple of
the architecture's gigantic hugepage size and may become
zero.
Format: nn[KMGTPE] or nn% or (node format)
<node>:nn[KMGTPE][,<node>:nn[KMGTPE]] or
<node>:nn%[,<node>:nn%]
The size must be a multiple of the gigantic page size.
When using node format, this applies to each per-node size.
@@ -4854,6 +4860,12 @@ Kernel parameters
Adjust the minimal page reporting order. The page
reporting is disabled when it exceeds MAX_PAGE_ORDER.
page_reporting.page_reporting_delay_ms=
[KNL] Free page reporting delay in milliseconds
Format: <unsigned integer>
Adjust the delay in milliseconds between free page
reporting intervals. Default is 2000 (2 seconds).
panic= [KNL] Kernel behaviour on panic: delay <timeout>
timeout > 0: seconds before rebooting
timeout = 0: wait forever

View File

@@ -224,7 +224,7 @@ khugepaged will be automatically started when any THP size is enabled
(either of the per-size anon control or the top-level control are set
to "always" or "madvise"), and it'll be automatically shutdown when
all THP sizes are disabled (when both the per-size anon control and the
top-level control are "never")
top-level control are "never").
process THP controls
--------------------
@@ -301,7 +301,9 @@ being replaced by a PMD mapping, or (2) physical pages replaced by one
hugepage of various sizes (PMD-sized or mTHP). Each may happen independently,
or together, depending on the type of memory and the failures that occur.
As such, this value should be interpreted roughly as a sign of progress,
and counters in /proc/vmstat consulted for more accurate accounting)::
and counters in /proc/vmstat consulted for more accurate accounting.
Per-order mTHP collapse statistics are also available under
/sys/kernel/mm/transparent_hugepage/hugepages-<size>kB/stats/)::
/sys/kernel/mm/transparent_hugepage/khugepaged/pages_collapsed

View File

@@ -17,7 +17,8 @@ supports iterating over a range of entries and going to the previous or next
entry in a cache-efficient manner. The tree can also be put into an RCU-safe
mode of operation which allows reading and writing concurrently. Writers must
synchronize on a lock, which can be the default spinlock, or the user can set
the lock to an external lock of a different type.
the lock to an external lock of a different type. Note that external locks may
interfere with allocations in a low memory situation.
The Maple Tree maintains a small memory footprint and was designed to use
modern processor cache efficiently. The majority of the users will be able to
@@ -42,6 +43,15 @@ successful store operation within a given
code segment when allocating cannot be done. Allocations of nodes are
relatively small at around 256 bytes.
Since the maple tree uses internal nodes that are allocated and has rules on
data density, erasing an entry may cause allocations to occur. That is,
erasing an entry may consume memory. Users must take care to ensure that they
do not violate the larger system constraints on when and how memory is
allocated. Most situations are fine to allocate, but the pre-allocation
support is provided as a mechanism to avoid trickier situations. There is also
the possibility of using special entries and clean up the tree later, in
extreme circumstances.
.. _maple-tree-normal-api:
Normal API
@@ -63,7 +73,10 @@ success or an error code otherwise. mtree_store_range() works in the same way
but takes a range. mtree_load() is used to retrieve the entry stored at a
given index. You can use mtree_erase() to erase an entire range by only
knowing one value within that range, or mtree_store() call with an entry of
NULL may be used to partially erase a range or many ranges at once.
NULL may be used to partially erase a range or many ranges at once. Note that
mtree_erase() may use GFP_KERNEL | __GFP_NOFAIL for allocations and cannot
fail. mtree_erase() can sleep, so it must not be called from an atomic
context.
If you want to only store a new entry to a range (or index) if that range is
currently ``NULL``, you can use mtree_insert_range() or mtree_insert() which
@@ -163,7 +176,10 @@ You can use mas_erase() to erase an entire range by setting index and
last of the maple state to the desired range to erase. This will erase
the first range that is found in that range, set the maple state index
and last as the range that was erased and return the entry that existed
at that location.
at that location. Note that mas_erase() may allocate with the GFP_KERNEL
__GFP_NOFAIL and cannot fail, but may sleep. If this is not okay, consider
using mas_store_gfp() and pass it a ``NULL``,
after setting up the correct range by walking to the entry.
You can walk each entry within a range by using mas_for_each(). If you want
to walk each element of the tree then ``0`` and ``ULONG_MAX`` may be used as
@@ -211,7 +227,7 @@ Advanced Locking
The maple tree uses a spinlock by default, but external locks can be used for
tree updates as well. To use an external lock, the tree must be initialized
with the ``MT_FLAGS_LOCK_EXTERN flag``, this is usually done with the
with the ``MT_FLAGS_LOCK_EXTERN`` flag, this is usually done with the
MTREE_INIT_EXT() #define, which takes an external lock as an argument.
Functions and structures

View File

@@ -198,6 +198,16 @@ systems, because of pointers temporarily stored in CPU registers or
stacks. Kmemleak defines MSECS_MIN_AGE (defaulting to 1000) representing
the minimum age of an object to be reported as a memory leak.
The ``min_unref_scans`` module parameter requires an object to be seen
unreferenced in that many consecutive scans before it is reported. It
defaults to 2 when CONFIG_DEBUG_KMEMLEAK_VERBOSE is enabled, where the
periodic scan thread confirms a leak on its own, and to 1 otherwise. A
value of 1 preserves the historical behaviour; higher values filter the
transient false positives described above, at the cost of delaying genuine
reports by up to that many scans. It can be set at boot with
``kmemleak.min_unref_scans=<n>`` or at run-time via
``/sys/module/kmemleak/parameters/min_unref_scans``.
Limitations and Drawbacks
-------------------------

View File

@@ -1237,6 +1237,10 @@ astute users may notice some differences in behavior:
- DAX (Direct Access) is not supported on encrypted files.
- Encrypted files cannot be used directly as swap files. To swap to
an encrypted file, set up a loopback device on top of it.
Alternatively, encrypted swap can use a dm-crypt device.
- The maximum length of an encrypted symlink is 2 bytes shorter than
the maximum length of an unencrypted symlink. For example, on an
EXT4 filesystem with a 4K block size, unencrypted symlinks can be up

View File

@@ -266,7 +266,6 @@ prototypes::
int (*error_remove_folio)(struct address_space *, struct folio *);
int (*swap_activate)(struct swap_info_struct *sis, struct file *f, sector_t *span)
int (*swap_deactivate)(struct file *);
int (*swap_rw)(struct kiocb *iocb, struct iov_iter *iter);
locking rules:
All except dirty_folio and free_folio may block
@@ -291,7 +290,6 @@ is_partially_uptodate: yes
error_remove_folio: yes
swap_activate: no
swap_deactivate: no
swap_rw: yes, unlocks
====================== ======================== ========= ===============
->write_begin(), ->write_end() and ->read_folio() may be called from
@@ -355,14 +353,12 @@ should perform any validation and preparation necessary to ensure that
writes can be performed with minimal memory allocation. It should call
add_swap_extent(), or the helper iomap_swapfile_activate(), and return
the number of extents added. If IO should be submitted through
->swap_rw(), it should set SWP_FS_OPS, otherwise IO will be submitted
directly to the block device ``sis->bdev``.
the file system it should call swap_fs_activate, otherwise IO will be
submitted directly to the block device ``sis->bdev``.
->swap_deactivate() will be called in the sys_swapoff()
path after ->swap_activate() returned success.
->swap_rw will be called for swap IO if SWP_FS_OPS was set by ->swap_activate().
file_lock_operations
====================

View File

@@ -776,7 +776,6 @@ cache in your filesystem. The following members are defined:
int (*error_remove_folio)(struct mapping *mapping, struct folio *);
int (*swap_activate)(struct swap_info_struct *sis, struct file *f, sector_t *span)
int (*swap_deactivate)(struct file *);
int (*swap_rw)(struct kiocb *iocb, struct iov_iter *iter);
};
``read_folio``
@@ -977,16 +976,13 @@ cache in your filesystem. The following members are defined:
can be performed with minimal memory allocation. It should call
add_swap_extent(), or the helper iomap_swapfile_activate(), and
return the number of extents added. If IO should be submitted
through ->swap_rw(), it should set SWP_FS_OPS, otherwise IO will
be submitted directly to the block device ``sis->bdev``.
through the file system it should call swap_fs_activate, otherwise IO
will be submitted directly to the block device ``sis->bdev``.
``swap_deactivate``
Called during swapoff on files where swap_activate was
successful.
``swap_rw``
Called to read or write swap pages when SWP_FS_OPS is set.
The File Object
===============

View File

@@ -43,9 +43,25 @@ sysctl:
warnings produced by allocations made while profiling is disabled and freed
when it's enabled.
/proc/sys/vm/mem_profiling_compressed
1: Page alloc tag compression is enabled.
0: Page alloc tag compression is disabled.
This reflects a static boot-time configuration of how page allocation tags are
stored (in page flags when compression is enabled and in page_ext when disabled).
Toggling ``mem_profiling`` at runtime does not change the state of
``mem_profiling_compressed``.
Runtime info:
/proc/allocinfo
Profiling data can be retrieved either by reading `/proc/allocinfo` directly as
text or programmatically via `ioctl()` calls defined in `<uapi/linux/alloc_tag.h>`.
The ioctl interface supports structured binary data extraction as well as filtering
by module name, function, file, line number, accuracy, or allocation size limits.
Example output::
root@moria-kvm:~# sort -g /proc/allocinfo|tail|numfmt --to=iec

View File

@@ -24,13 +24,13 @@ tree.
If a KSM page is shared between less than ``max_page_sharing`` VMAs,
the node of the stable tree that represents such KSM page points to a
list of struct ksm_rmap_item and the ``page->mapping`` of the
list of struct ksm_rmap_item and the ``folio->mapping`` of the
KSM page points to the stable tree node.
When the sharing passes this threshold, KSM adds a second dimension to
the stable tree. The tree node becomes a "chain" that links one or
more "dups". Each "dup" keeps reverse mapping information for a KSM
page with ``page->mapping`` pointing to that "dup".
page with ``folio->mapping`` pointing to that "dup".
Every "chain" and all "dups" linked into a "chain" enforce the
invariant that they represent the same write protected memory content,

View File

@@ -110,13 +110,13 @@ Steps:
6. The refcount of the page is examined and we back out if references remain.
Otherwise, we know that we are the only one referencing this page.
7. The radix tree is checked and if it does not contain the pointer to this
page then we back out because someone else modified the radix tree.
7. The page cache tree is checked and if it does not contain the pointer to this
page then we back out because someone else modified the page cache tree.
8. The new page is prepped with some settings from the old page so that
accesses to the new page will discover a page with the correct settings.
9. The radix tree is changed to point to the new page.
9. The page cache tree is changed to point to the new page.
10. The reference count of the old page is dropped because the address space
reference is gone. A reference to the new page is established because

View File

@@ -31,10 +31,10 @@ KSM维护着稳定树中的KSM页的逆映射信息。
当KSM页面的共享数小于 ``max_page_sharing`` 的虚拟内存区域(VMAs)时,则代表了
KSM页的稳定树其中的节点指向了一个ksm_rmap_item结构体类型的列表。同时这个KSM页
``page->mapping`` 指向了该稳定树节点。
``folio->mapping`` 指向了该稳定树节点。
如果共享数超过了阈值KSM将给稳定树添加第二个维度。稳定树就变成链接一个或多
个稳定树"副本"的"链"。每个副本都保留KSM页的逆映射信息其中 ``page->mapping``
个稳定树"副本"的"链"。每个副本都保留KSM页的逆映射信息其中 ``folio->mapping``
指向该"副本"。
每个链以及链接到该链中的所有"副本"强制不变的是,它们代表了相同的写保护内存

View File

@@ -346,6 +346,8 @@ Code Seq# Include File Comments
<mailto:luzmaximilian@gmail.com>
0xA5 20-2F linux/surface_aggregator/dtx.h Microsoft Surface DTX driver
<mailto:luzmaximilian@gmail.com>
0xA6 00-0F uapi/linux/alloc_tag.h Memory allocation profiling
<mailto:surenb@google.com>
0xAA 00-3F linux/uapi/linux/userfaultfd.h
0xAB 00-1F linux/nbd.h
0xAC 00-1F linux/raw.h

View File

@@ -17022,6 +17022,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/rppt/memblock.git fixes
F: Documentation/core-api/boot-time-mm.rst
F: include/linux/kho/abi/memblock.h
F: include/linux/memblock.h
F: mm/arch_numa.c
F: mm/memblock.c
F: mm/memtest.c
F: mm/mm_init.c
@@ -17081,7 +17082,9 @@ S: Maintained
F: Documentation/mm/allocation-profiling.rst
F: include/linux/alloc_tag.h
F: include/linux/pgalloc_tag.h
F: include/uapi/linux/alloc_tag.h
F: mm/alloc_tag.c
F: tools/testing/selftests/alloc_tag/
MEMORY MANAGEMENT - BALLOON
M: Andrew Morton <akpm@linux-foundation.org>
@@ -17254,6 +17257,7 @@ F: Documentation/ABI/testing/sysfs-kernel-mm-memory-tiers
F: Documentation/ABI/testing/sysfs-kernel-mm-numa
F: Documentation/admin-guide/mm/
F: Documentation/mm/
F: drivers/char/mem.c
F: include/linux/cma.h
F: include/linux/dmapool.h
F: include/linux/ioremap.h
@@ -17381,6 +17385,7 @@ S: Maintained
F: Documentation/ABI/testing/sysfs-kernel-mm-swap
F: Documentation/mm/swap-table.rst
F: include/linux/swap.h
F: include/linux/swap_ops.h
F: include/linux/swapfile.h
F: include/linux/swapops.h
F: mm/page_io.c
@@ -17457,6 +17462,7 @@ L: linux-mm@kvack.org
S: Maintained
W: http://www.linux-mm.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
F: drivers/char/mem.c
F: include/trace/events/mmap.h
F: fs/proc/task_mmu.c
F: fs/proc/task_nommu.c

View File

@@ -203,7 +203,7 @@ static int find_zeropage_pte_entry(pte_t *pte, unsigned long addr,
* currently only works in COW mappings, which is also where
* mm_forbids_zeropage() is checked.
*/
if (!is_cow_mapping(walk->vma->vm_flags))
if (!vma_is_cow_mapping(walk->vma))
return -EFAULT;
*found_addr = addr;

View File

@@ -230,13 +230,6 @@ config GENERIC_ARCH_TOPOLOGY
appropriate scaling, sysfs interface for reading capacity values at
runtime.
config GENERIC_ARCH_NUMA
bool
select NUMA_MEMBLKS
help
Enable support for generic NUMA implementation. Currently, RISC-V
and ARM64 use it.
config FW_DEVLINK_SYNC_STATE_TIMEOUT
bool "sync_state() behavior defaults to timeout instead of strict"
help

View File

@@ -25,7 +25,6 @@ obj-$(CONFIG_PINCTRL) += pinctrl.o
obj-$(CONFIG_DEV_COREDUMP) += devcoredump.o
obj-$(CONFIG_GENERIC_MSI_IRQ) += platform-msi.o
obj-$(CONFIG_GENERIC_ARCH_TOPOLOGY) += arch_topology.o
obj-$(CONFIG_GENERIC_ARCH_NUMA) += arch_numa.o
obj-$(CONFIG_ACPI) += physical_location.o
obj-y += test/

View File

@@ -1,5 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#define pr_fmt(fmt) "842: " fmt
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/sw842.h>
@@ -13,6 +15,14 @@ static void release_params_842(struct zcomp_params *params)
static int setup_params_842(struct zcomp_params *params)
{
if (params->dict_sz) {
pr_err("dictionary is not supported\n");
return -EOPNOTSUPP;
}
if (params->level != ZCOMP_PARAM_NOT_SET) {
pr_err("compression level is not supported\n");
return -EOPNOTSUPP;
}
return 0;
}

View File

@@ -1,5 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#define pr_fmt(fmt) "deflate: " fmt
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
@@ -22,10 +24,29 @@ static void deflate_release_params(struct zcomp_params *params)
static int deflate_setup_params(struct zcomp_params *params)
{
if (params->level == ZCOMP_PARAM_NOT_SET)
if (params->dict_sz) {
pr_err("dictionary is not supported\n");
return -EOPNOTSUPP;
}
if (params->level == ZCOMP_PARAM_NOT_SET) {
params->level = Z_DEFAULT_COMPRESSION;
if (params->deflate.winbits == ZCOMP_PARAM_NOT_SET)
} else if (params->level < Z_DEFAULT_COMPRESSION ||
params->level > Z_BEST_COMPRESSION) {
pr_err("invalid compression level %d\n", params->level);
return -EINVAL;
}
if (params->deflate.winbits == ZCOMP_PARAM_NOT_SET) {
params->deflate.winbits = DEFLATE_DEF_WINBITS;
} else {
s32 wb = params->deflate.winbits;
if ((wb < -15 || wb > -9) && (wb < 9 || wb > 15)) {
pr_err("invalid winbits %d\n", wb);
return -EINVAL;
}
}
return 0;
}

View File

@@ -1,3 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#define pr_fmt(fmt) "lz4: " fmt
#include <linux/kernel.h>
#include <linux/lz4.h>
#include <linux/slab.h>
@@ -28,8 +32,12 @@ static int lz4_setup_params(struct zcomp_params *params)
LZ4_stream_t *dict_stream;
int ret;
if (params->level == ZCOMP_PARAM_NOT_SET)
if (params->level == ZCOMP_PARAM_NOT_SET) {
params->level = LZ4_ACCELERATION_DEFAULT;
} else if (params->level < LZ4_ACCELERATION_DEFAULT) {
pr_err("invalid compression level %d\n", params->level);
return -EINVAL;
}
if (!params->dict || !params->dict_sz)
return 0;

View File

@@ -1,3 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#define pr_fmt(fmt) "lz4hc: " fmt
#include <linux/kernel.h>
#include <linux/lz4.h>
#include <linux/slab.h>
@@ -18,8 +22,18 @@ static void lz4hc_release_params(struct zcomp_params *params)
static int lz4hc_setup_params(struct zcomp_params *params)
{
if (params->level == ZCOMP_PARAM_NOT_SET)
if (params->level == ZCOMP_PARAM_NOT_SET) {
params->level = LZ4HC_DEFAULT_CLEVEL;
} else if (params->level < 1 || params->level > LZ4HC_MAX_CLEVEL) {
/*
* Use < 1 rather than < LZ4HC_MIN_CLEVEL here because
* LZ4HC_compress_generic() only clamps levels below 1
* (levels 1 and 2 are valid). LZ4HC_MIN_CLEVEL (3) is
* advisory and not enforced by the library.
*/
pr_err("invalid compression level %d\n", params->level);
return -EINVAL;
}
return 0;
}

View File

@@ -1,5 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#define pr_fmt(fmt) "lzo: " fmt
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/lzo.h>
@@ -12,6 +14,14 @@ static void lzo_release_params(struct zcomp_params *params)
static int lzo_setup_params(struct zcomp_params *params)
{
if (params->dict_sz) {
pr_err("dictionary is not supported\n");
return -EOPNOTSUPP;
}
if (params->level != ZCOMP_PARAM_NOT_SET) {
pr_err("compression level is not supported\n");
return -EOPNOTSUPP;
}
return 0;
}

View File

@@ -1,5 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#define pr_fmt(fmt) "lzo-rle: " fmt
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/lzo.h>
@@ -12,6 +14,14 @@ static void lzorle_release_params(struct zcomp_params *params)
static int lzorle_setup_params(struct zcomp_params *params)
{
if (params->dict_sz) {
pr_err("dictionary is not supported\n");
return -EOPNOTSUPP;
}
if (params->level != ZCOMP_PARAM_NOT_SET) {
pr_err("compression level is not supported\n");
return -EOPNOTSUPP;
}
return 0;
}

View File

@@ -1,5 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#define pr_fmt(fmt) "zstd: " fmt
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
@@ -58,8 +60,13 @@ static int zstd_setup_params(struct zcomp_params *params)
return -ENOMEM;
params->drv_data = zp;
if (params->level == ZCOMP_PARAM_NOT_SET)
if (params->level == ZCOMP_PARAM_NOT_SET) {
params->level = zstd_default_clevel();
} else if (params->level < zstd_min_clevel() ||
params->level > zstd_max_clevel()) {
pr_err("invalid compression level %d\n", params->level);
goto error;
}
zp->cprm = zstd_get_params(params->level, PAGE_SIZE);
@@ -85,7 +92,6 @@ static int zstd_setup_params(struct zcomp_params *params)
return 0;
error:
zstd_release_params(params);
return -EINVAL;
}
@@ -161,7 +167,6 @@ static int zstd_create(struct zcomp_params *params, struct zcomp_ctx *ctx)
return 0;
error:
zstd_release_params(params);
zstd_destroy(ctx);
return -EINVAL;
}

View File

@@ -56,7 +56,7 @@ static size_t huge_class_size;
static const struct block_device_operations zram_devops;
static void slot_free(struct zram *zram, u32 index);
static void slot_free(struct zram *zram, unsigned long index);
/*
* entry locking rules:
@@ -70,11 +70,11 @@ static void slot_free(struct zram *zram, u32 index);
* 4) Use TRY lock variant when in atomic context
* - must check return value and handle locking failers
*/
static __must_check bool slot_trylock(struct zram *zram, u32 index)
static __must_check bool slot_trylock(struct zram *zram, unsigned long index)
{
unsigned long *lock = &zram->table[index].__lock;
if (!test_and_set_bit_lock(ZRAM_ENTRY_LOCK, lock)) {
if (!test_and_set_bit_lock(ZRAM_ENTRY_LOCK_BIT, lock)) {
mutex_acquire(&zram->table_lock_map, 0, 1, _RET_IP_);
lock_acquired(&zram->table_lock_map, _RET_IP_);
return true;
@@ -83,21 +83,21 @@ static __must_check bool slot_trylock(struct zram *zram, u32 index)
return false;
}
static void slot_lock(struct zram *zram, u32 index)
static void slot_lock(struct zram *zram, unsigned long index)
{
unsigned long *lock = &zram->table[index].__lock;
mutex_acquire(&zram->table_lock_map, 0, 0, _RET_IP_);
wait_on_bit_lock(lock, ZRAM_ENTRY_LOCK, TASK_UNINTERRUPTIBLE);
wait_on_bit_lock(lock, ZRAM_ENTRY_LOCK_BIT, TASK_UNINTERRUPTIBLE);
lock_acquired(&zram->table_lock_map, _RET_IP_);
}
static void slot_unlock(struct zram *zram, u32 index)
static void slot_unlock(struct zram *zram, unsigned long index)
{
unsigned long *lock = &zram->table[index].__lock;
mutex_release(&zram->table_lock_map, _RET_IP_);
clear_and_wake_up_bit(ZRAM_ENTRY_LOCK, lock);
clear_and_wake_up_bit(ZRAM_ENTRY_LOCK_BIT, lock);
}
static inline bool init_done(struct zram *zram)
@@ -110,55 +110,56 @@ static inline struct zram *dev_to_zram(struct device *dev)
return (struct zram *)dev_to_disk(dev)->private_data;
}
static unsigned long get_slot_handle(struct zram *zram, u32 index)
static unsigned long get_slot_handle(struct zram *zram, unsigned long index)
{
return zram->table[index].handle;
}
static void set_slot_handle(struct zram *zram, u32 index, unsigned long handle)
static void set_slot_handle(struct zram *zram, unsigned long index,
unsigned long handle)
{
zram->table[index].handle = handle;
}
static bool test_slot_flag(struct zram *zram, u32 index,
static bool test_slot_flag(struct zram *zram, unsigned long index,
enum zram_pageflags flag)
{
return zram->table[index].attr.flags & BIT(flag);
}
static void set_slot_flag(struct zram *zram, u32 index,
static void set_slot_flag(struct zram *zram, unsigned long index,
enum zram_pageflags flag)
{
zram->table[index].attr.flags |= BIT(flag);
}
static void clear_slot_flag(struct zram *zram, u32 index,
static void clear_slot_flag(struct zram *zram, unsigned long index,
enum zram_pageflags flag)
{
zram->table[index].attr.flags &= ~BIT(flag);
}
static size_t get_slot_size(struct zram *zram, u32 index)
static size_t get_slot_size(struct zram *zram, unsigned long index)
{
return zram->table[index].attr.flags & (BIT(ZRAM_FLAG_SHIFT) - 1);
}
static void set_slot_size(struct zram *zram, u32 index, size_t size)
static void set_slot_size(struct zram *zram, unsigned long index, size_t size)
{
unsigned long flags = zram->table[index].attr.flags >> ZRAM_FLAG_SHIFT;
zram->table[index].attr.flags = (flags << ZRAM_FLAG_SHIFT) | size;
}
static inline bool slot_allocated(struct zram *zram, u32 index)
static inline bool slot_allocated(struct zram *zram, unsigned long index)
{
return get_slot_size(zram, index) ||
test_slot_flag(zram, index, ZRAM_SAME) ||
test_slot_flag(zram, index, ZRAM_WB);
}
static inline void set_slot_comp_priority(struct zram *zram, u32 index,
u32 prio)
static inline void set_slot_comp_priority(struct zram *zram,
unsigned long index, u32 prio)
{
prio &= ZRAM_COMP_PRIORITY_MASK;
/*
@@ -170,14 +171,14 @@ static inline void set_slot_comp_priority(struct zram *zram, u32 index,
zram->table[index].attr.flags |= (prio << ZRAM_COMP_PRIORITY_BIT1);
}
static inline u32 get_slot_comp_priority(struct zram *zram, u32 index)
static inline u32 get_slot_comp_priority(struct zram *zram, unsigned long index)
{
u32 prio = zram->table[index].attr.flags >> ZRAM_COMP_PRIORITY_BIT1;
return prio & ZRAM_COMP_PRIORITY_MASK;
}
static void mark_slot_accessed(struct zram *zram, u32 index)
static void mark_slot_accessed(struct zram *zram, unsigned long index)
{
clear_slot_flag(zram, index, ZRAM_IDLE);
clear_slot_flag(zram, index, ZRAM_PP_SLOT);
@@ -284,7 +285,7 @@ static void release_pp_ctl(struct zram *zram, struct zram_pp_ctl *ctl)
}
static bool place_pp_slot(struct zram *zram, struct zram_pp_ctl *ctl,
u32 index)
unsigned long index)
{
struct zram_pp_slot *pps;
u32 bid;
@@ -418,7 +419,7 @@ static void mark_idle(struct zram *zram, ktime_t cutoff)
{
int is_idle = 1;
unsigned long nr_pages = zram->disksize >> PAGE_SHIFT;
int index;
unsigned long index;
for (index = 0; index < nr_pages; index++) {
/*
@@ -485,8 +486,9 @@ static ssize_t idle_store(struct device *dev, struct device_attribute *attr,
#define INVALID_BDEV_BLOCK (~0UL)
static int read_from_zspool_raw(struct zram *zram, struct page *page,
u32 index);
static int read_from_zspool(struct zram *zram, struct page *page, u32 index);
unsigned long index);
static int read_from_zspool(struct zram *zram, struct page *page,
unsigned long index);
struct zram_wb_ctl {
/* idle list is accessed only by the writeback task, no concurency */
@@ -522,7 +524,7 @@ struct zram_rb_req {
/* error status (sync read) */
int error;
};
u32 index;
unsigned long index;
};
#define FOUR_K(x) ((x) * (1 << (PAGE_SHIFT - 12)))
@@ -910,7 +912,7 @@ static void zram_account_writeback_submit(struct zram *zram)
static int zram_writeback_complete(struct zram *zram, struct zram_wb_req *req)
{
u32 index = req->pps->index;
unsigned long index = req->pps->index;
int err;
err = blk_status_to_errno(req->bio.bi_status);
@@ -1032,7 +1034,7 @@ static int zram_writeback_slots(struct zram *zram,
struct zram_wb_req *req = NULL;
struct zram_pp_slot *pps;
int ret = 0, err = 0;
u32 index = 0;
unsigned long index = 0;
while ((pps = select_pp_slot(ctl))) {
if (zram->wb_limit_enable && !zram->bd_wb_limit) {
@@ -1198,7 +1200,7 @@ static void scan_slots_for_writeback(struct zram *zram, u32 mode,
unsigned long lo, unsigned long hi,
struct zram_pp_ctl *ctl)
{
u32 index = lo;
unsigned long index = lo;
while (index < hi) {
bool ok = true;
@@ -1235,8 +1237,8 @@ static ssize_t writeback_store(struct device *dev,
const char *buf, size_t len)
{
struct zram *zram = dev_to_zram(dev);
u64 nr_pages = zram->disksize >> PAGE_SHIFT;
unsigned long lo = 0, hi = nr_pages;
unsigned long nr_pages;
unsigned long lo = 0, hi;
struct zram_pp_ctl *pp_ctl = NULL;
struct zram_wb_ctl *wb_ctl = NULL;
char *args, *param, *val;
@@ -1250,6 +1252,9 @@ static ssize_t writeback_store(struct device *dev,
if (!zram->backing_dev)
return -ENODEV;
nr_pages = zram->disksize >> PAGE_SHIFT;
hi = nr_pages;
pp_ctl = init_pp_ctl();
if (!pp_ctl)
return -ENOMEM;
@@ -1333,7 +1338,8 @@ static ssize_t writeback_store(struct device *dev,
return ret;
}
static int decompress_bdev_page(struct zram *zram, struct page *page, u32 index)
static int decompress_bdev_page(struct zram *zram, struct page *page,
unsigned long index)
{
struct zcomp_strm *zstrm;
unsigned int size;
@@ -1375,7 +1381,7 @@ static void zram_deferred_decompress(struct work_struct *w)
struct zram_rb_req *req = container_of(w, struct zram_rb_req, work);
struct page *page = bio_first_page_all(req->bio);
struct zram *zram = req->zram;
u32 index = req->index;
unsigned long index = req->index;
int ret;
ret = decompress_bdev_page(zram, page, index);
@@ -1426,7 +1432,7 @@ static void zram_async_read_endio(struct bio *bio)
}
static int read_from_bdev_async(struct zram *zram, struct page *page,
u32 index, unsigned long blk_idx,
unsigned long index, unsigned long blk_idx,
struct bio *parent)
{
struct zram_rb_req *req;
@@ -1476,8 +1482,8 @@ static void zram_sync_read(struct work_struct *w)
* chained IO with parent IO in same context, it's a deadlock. To avoid that,
* use a worker thread context.
*/
static int read_from_bdev_sync(struct zram *zram, struct page *page, u32 index,
unsigned long blk_idx)
static int read_from_bdev_sync(struct zram *zram, struct page *page,
unsigned long index, unsigned long blk_idx)
{
struct zram_rb_req req;
@@ -1496,8 +1502,9 @@ static int read_from_bdev_sync(struct zram *zram, struct page *page, u32 index,
return decompress_bdev_page(zram, page, index);
}
static int read_from_bdev(struct zram *zram, struct page *page, u32 index,
unsigned long blk_idx, struct bio *parent)
static int read_from_bdev(struct zram *zram, struct page *page,
unsigned long index, unsigned long blk_idx,
struct bio *parent)
{
atomic64_inc(&zram->stats.bd_reads);
if (!parent) {
@@ -1509,8 +1516,9 @@ static int read_from_bdev(struct zram *zram, struct page *page, u32 index,
}
#else
static inline void reset_bdev(struct zram *zram) {};
static int read_from_bdev(struct zram *zram, struct page *page, u32 index,
unsigned long blk_idx, struct bio *parent)
static int read_from_bdev(struct zram *zram, struct page *page,
unsigned long index, unsigned long blk_idx,
struct bio *parent)
{
return -EIO;
}
@@ -1538,9 +1546,10 @@ static ssize_t read_block_state(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
char *kbuf;
ssize_t index, written = 0;
unsigned long index;
ssize_t written = 0;
struct zram *zram = file->private_data;
unsigned long nr_pages = zram->disksize >> PAGE_SHIFT;
unsigned long nr_pages;
kbuf = kvmalloc(count, GFP_KERNEL);
if (!kbuf)
@@ -1552,6 +1561,8 @@ static ssize_t read_block_state(struct file *file, char __user *buf,
return -EINVAL;
}
nr_pages = zram->disksize >> PAGE_SHIFT;
for (index = *ppos; index < nr_pages; index++) {
int copied;
@@ -1560,7 +1571,7 @@ static ssize_t read_block_state(struct file *file, char __user *buf,
goto next;
copied = snprintf(kbuf + written, count,
"%12zd %12u.%06d %c%c%c%c%c%c\n",
"%12lu %12u.%06d %c%c%c%c%c%c\n",
index, zram->table[index].attr.ac_time, 0,
test_slot_flag(zram, index, ZRAM_SAME) ? 's' : '.',
test_slot_flag(zram, index, ZRAM_WB) ? 'w' : '.',
@@ -1652,6 +1663,17 @@ static void comp_algorithm_set(struct zram *zram, u32 prio, const char *alg)
zram->comp_algs[prio] = alg;
}
static void comp_params_reset(struct zram *zram, u32 prio)
{
struct zcomp_params *params = &zram->params[prio];
vfree(params->dict);
params->level = ZCOMP_PARAM_NOT_SET;
params->deflate.winbits = ZCOMP_PARAM_NOT_SET;
params->dict_sz = 0;
params->dict = NULL;
}
static int __comp_algorithm_store(struct zram *zram, u32 prio, const char *buf)
{
const char *alg;
@@ -1672,20 +1694,10 @@ static int __comp_algorithm_store(struct zram *zram, u32 prio, const char *buf)
}
comp_algorithm_set(zram, prio, alg);
comp_params_reset(zram, prio);
return 0;
}
static void comp_params_reset(struct zram *zram, u32 prio)
{
struct zcomp_params *params = &zram->params[prio];
vfree(params->dict);
params->level = ZCOMP_PARAM_NOT_SET;
params->deflate.winbits = ZCOMP_PARAM_NOT_SET;
params->dict_sz = 0;
params->dict = NULL;
}
static int comp_params_store(struct zram *zram, u32 prio, s32 level,
const char *dict_path,
struct deflate_params *deflate_params)
@@ -1700,8 +1712,16 @@ static int comp_params_store(struct zram *zram, u32 prio, s32 level,
INT_MAX,
NULL,
READING_POLICY);
if (sz < 0)
if (sz < 0) {
pr_err("failed to load dictionary %s (err=%zd)\n",
dict_path, sz);
return sz;
}
if (sz == 0) {
pr_err("failed to load dictionary %s (empty file)\n",
dict_path);
return -EINVAL;
}
}
zram->params[prio].dict_sz = sz;
@@ -1958,8 +1978,8 @@ static ssize_t debug_stat_show(struct device *dev,
static void zram_meta_free(struct zram *zram, u64 disksize)
{
size_t num_pages = disksize >> PAGE_SHIFT;
size_t index;
unsigned long num_pages = disksize >> PAGE_SHIFT;
unsigned long index;
if (!zram->table)
return;
@@ -1976,7 +1996,7 @@ static void zram_meta_free(struct zram *zram, u64 disksize)
static bool zram_meta_alloc(struct zram *zram, u64 disksize)
{
size_t num_pages;
unsigned long num_pages;
num_pages = disksize >> PAGE_SHIFT;
zram->table = vzalloc(array_size(num_pages, sizeof(*zram->table)));
@@ -1999,7 +2019,7 @@ static bool zram_meta_alloc(struct zram *zram, u64 disksize)
return true;
}
static void slot_free(struct zram *zram, u32 index)
static void slot_free(struct zram *zram, unsigned long index)
{
unsigned long handle;
@@ -2053,7 +2073,7 @@ static void slot_free(struct zram *zram, u32 index)
}
static int read_same_filled_page(struct zram *zram, struct page *page,
u32 index)
unsigned long index)
{
void *mem;
@@ -2064,7 +2084,7 @@ static int read_same_filled_page(struct zram *zram, struct page *page,
}
static int read_incompressible_page(struct zram *zram, struct page *page,
u32 index)
unsigned long index)
{
unsigned long handle;
void *src, *dst;
@@ -2079,7 +2099,8 @@ static int read_incompressible_page(struct zram *zram, struct page *page,
return 0;
}
static int read_compressed_page(struct zram *zram, struct page *page, u32 index)
static int read_compressed_page(struct zram *zram, struct page *page,
unsigned long index)
{
struct zcomp_strm *zstrm;
unsigned long handle;
@@ -2104,7 +2125,8 @@ static int read_compressed_page(struct zram *zram, struct page *page, u32 index)
}
#if defined CONFIG_ZRAM_WRITEBACK
static int read_from_zspool_raw(struct zram *zram, struct page *page, u32 index)
static int read_from_zspool_raw(struct zram *zram, struct page *page,
unsigned long index)
{
struct zcomp_strm *zstrm;
unsigned long handle;
@@ -2136,7 +2158,8 @@ static int read_from_zspool_raw(struct zram *zram, struct page *page, u32 index)
* Reads (decompresses if needed) a page from zspool (zsmalloc).
* Corresponding ZRAM slot should be locked.
*/
static int read_from_zspool(struct zram *zram, struct page *page, u32 index)
static int read_from_zspool(struct zram *zram, struct page *page,
unsigned long index)
{
if (test_slot_flag(zram, index, ZRAM_SAME) ||
!get_slot_handle(zram, index))
@@ -2148,8 +2171,8 @@ static int read_from_zspool(struct zram *zram, struct page *page, u32 index)
return read_incompressible_page(zram, page, index);
}
static int zram_read_page(struct zram *zram, struct page *page, u32 index,
struct bio *parent)
static int zram_read_page(struct zram *zram, struct page *page,
unsigned long index, struct bio *parent)
{
int ret;
@@ -2171,7 +2194,7 @@ static int zram_read_page(struct zram *zram, struct page *page, u32 index,
/* Should NEVER happen. Return bio error if it does. */
if (WARN_ON(ret < 0))
pr_err("Decompression failed! err=%d, page=%u\n", ret, index);
pr_err("Decompression failed! err=%d, page=%lu\n", ret, index);
return ret;
}
@@ -2181,7 +2204,7 @@ static int zram_read_page(struct zram *zram, struct page *page, u32 index,
* always expects a full page for the output.
*/
static int zram_bvec_read_partial(struct zram *zram, struct bio_vec *bvec,
u32 index, int offset)
unsigned long index, int offset)
{
struct page *page = alloc_page(GFP_NOIO);
int ret;
@@ -2196,7 +2219,7 @@ static int zram_bvec_read_partial(struct zram *zram, struct bio_vec *bvec,
}
static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec,
u32 index, int offset, struct bio *bio)
unsigned long index, int offset, struct bio *bio)
{
if (is_partial_io(bvec))
return zram_bvec_read_partial(zram, bvec, index, offset);
@@ -2204,7 +2227,7 @@ static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec,
}
static int write_same_filled_page(struct zram *zram, unsigned long fill,
u32 index)
unsigned long index)
{
slot_lock(zram, index);
slot_free(zram, index);
@@ -2219,7 +2242,7 @@ static int write_same_filled_page(struct zram *zram, unsigned long fill,
}
static int write_incompressible_page(struct zram *zram, struct page *page,
u32 index)
unsigned long index)
{
unsigned long handle;
void *src;
@@ -2259,7 +2282,8 @@ static int write_incompressible_page(struct zram *zram, struct page *page,
return 0;
}
static int zram_write_page(struct zram *zram, struct page *page, u32 index)
static int zram_write_page(struct zram *zram, struct page *page,
unsigned long index)
{
int ret = 0;
unsigned long handle;
@@ -2326,7 +2350,7 @@ static int zram_write_page(struct zram *zram, struct page *page, u32 index)
* This is a partial IO. Read the full page before writing the changes.
*/
static int zram_bvec_write_partial(struct zram *zram, struct bio_vec *bvec,
u32 index, int offset)
unsigned long index, int offset)
{
struct page *page = alloc_page(GFP_NOIO);
int ret;
@@ -2344,7 +2368,7 @@ static int zram_bvec_write_partial(struct zram *zram, struct bio_vec *bvec,
}
static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec,
u32 index, int offset)
unsigned long index, int offset)
{
if (is_partial_io(bvec))
return zram_bvec_write_partial(zram, bvec, index, offset);
@@ -2412,8 +2436,9 @@ static void scan_slots_for_recompress(struct zram *zram, u32 mode, u32 prio,
*
* Corresponding ZRAM slot should be locked.
*/
static int recompress_slot(struct zram *zram, u32 index, struct page *page,
u64 *num_recomp_pages, u32 threshold, u32 prio)
static int recompress_slot(struct zram *zram, unsigned long index,
struct page *page, u64 *num_recomp_pages,
u32 threshold, u32 prio)
{
struct zcomp_strm *zstrm = NULL;
unsigned long handle_old;
@@ -2665,7 +2690,7 @@ static ssize_t recompress_store(struct device *dev,
static void zram_bio_discard(struct zram *zram, struct bio *bio)
{
size_t n = bio->bi_iter.bi_size;
u32 index = bio->bi_iter.bi_sector >> SECTORS_PER_PAGE_SHIFT;
unsigned long index = bio->bi_iter.bi_sector >> SECTORS_PER_PAGE_SHIFT;
u32 offset = (bio->bi_iter.bi_sector & (SECTORS_PER_PAGE - 1)) <<
SECTOR_SHIFT;
@@ -2706,7 +2731,7 @@ static void zram_bio_read(struct zram *zram, struct bio *bio)
struct bvec_iter iter = bio->bi_iter;
do {
u32 index = iter.bi_sector >> SECTORS_PER_PAGE_SHIFT;
unsigned long index = iter.bi_sector >> SECTORS_PER_PAGE_SHIFT;
u32 offset = (iter.bi_sector & (SECTORS_PER_PAGE - 1)) <<
SECTOR_SHIFT;
struct bio_vec bv = bio_iter_iovec(bio, iter);
@@ -2737,7 +2762,7 @@ static void zram_bio_write(struct zram *zram, struct bio *bio)
struct bvec_iter iter = bio->bi_iter;
do {
u32 index = iter.bi_sector >> SECTORS_PER_PAGE_SHIFT;
unsigned long index = iter.bi_sector >> SECTORS_PER_PAGE_SHIFT;
u32 offset = (iter.bi_sector & (SECTORS_PER_PAGE - 1)) <<
SECTOR_SHIFT;
struct bio_vec bv = bio_iter_iovec(bio, iter);
@@ -2828,6 +2853,7 @@ static void zram_destroy_comps(struct zram *zram)
zram->comp_algs[prio] = NULL;
zram_comp_params_reset(zram);
comp_algorithm_set(zram, ZRAM_PRIMARY_COMP, default_compressor);
}
static void zram_reset_device(struct zram *zram)
@@ -2845,13 +2871,12 @@ static void zram_reset_device(struct zram *zram)
zram_destroy_comps(zram);
memset(&zram->stats, 0, sizeof(zram->stats));
reset_bdev(zram);
comp_algorithm_set(zram, ZRAM_PRIMARY_COMP, default_compressor);
}
static ssize_t disksize_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t len)
{
unsigned long num_pages;
u64 disksize;
struct zcomp *comp;
struct zram *zram = dev_to_zram(dev);
@@ -2869,6 +2894,11 @@ static ssize_t disksize_store(struct device *dev, struct device_attribute *attr,
}
disksize = PAGE_ALIGN(disksize);
num_pages = disksize >> PAGE_SHIFT;
/* Slots are addressed by an unsigned long index */
if (!num_pages || ((u64)num_pages << PAGE_SHIFT) != disksize)
return -EINVAL;
if (!zram_meta_alloc(zram, disksize))
return -ENOMEM;

View File

@@ -15,6 +15,7 @@
#ifndef _ZRAM_DRV_H_
#define _ZRAM_DRV_H_
#include <asm/byteorder.h>
#include <linux/rwsem.h>
#include <linux/zsmalloc.h>
@@ -57,6 +58,19 @@ enum zram_pageflags {
__NR_ZRAM_PAGEFLAGS,
};
/*
* The slot lock is a bit-wait lock on the whole __lock word, while
* flags and ac_time alias that word as two u32s. The lock bit must
* land in the slot that ZRAM_ENTRY_LOCK reserves in attr.flags; on
* 64-bit big-endian the flags word maps to the upper half of __lock,
* so the bit position has to be shifted up.
*/
#if defined(CONFIG_64BIT) && defined(__BIG_ENDIAN)
#define ZRAM_ENTRY_LOCK_BIT (ZRAM_ENTRY_LOCK + 32)
#else
#define ZRAM_ENTRY_LOCK_BIT ZRAM_ENTRY_LOCK
#endif
/*
* Allocated for each disk page. We use bit-lock (ZRAM_ENTRY_LOCK bit
* of flags) to save memory. There can be plenty of entries and standard

View File

@@ -377,9 +377,9 @@ static int amdgpu_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_str
/* Workaround for Thunk bug creating PROT_NONE,MAP_PRIVATE mappings
* for debugger access to invisible VRAM. Should have used MAP_SHARED
* instead. Clearing VM_MAYWRITE prevents the mapping from ever
* becoming writable and makes is_cow_mapping(vm_flags) false.
* becoming writable and makes vma_is_cow_mapping(vma) false.
*/
if (is_cow_mapping(vma->vm_flags) &&
if (vma_is_cow_mapping(vma) &&
!(vma->vm_flags & VM_ACCESS_FLAGS))
vm_flags_clear(vma, VM_MAYWRITE);

View File

@@ -771,7 +771,7 @@ int drm_gem_shmem_mmap(struct drm_gem_shmem_object *shmem, struct vm_area_struct
return ret;
}
if (is_cow_mapping(vma->vm_flags))
if (vma_is_cow_mapping(vma))
return -EINVAL;
dma_resv_lock(shmem->base.resv, NULL);

View File

@@ -325,7 +325,7 @@ void __shmem_writeback(size_t size, struct address_space *mapping)
if (folio_mapped(folio))
folio_redirty_for_writepage(&wbc, folio);
else
error = shmem_writeout(folio, NULL, NULL);
error = shmem_write_folio(folio);
}
}

View File

@@ -761,7 +761,7 @@ static int panthor_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *v
return ret;
}
if (is_cow_mapping(vma->vm_flags))
if (vma_is_cow_mapping(vma))
return -EINVAL;
if (!refcount_inc_not_zero(&bo->cmap.mmap_count)) {

View File

@@ -160,7 +160,7 @@ ttm_backup_backup_folio(struct file *backup, struct folio *folio,
if (writeback && !folio_mapped(to_folio) &&
folio_clear_dirty_for_io(to_folio)) {
folio_set_reclaim(to_folio);
ret = shmem_writeout(to_folio, NULL, NULL);
ret = shmem_write_folio(to_folio);
if (!folio_test_writeback(to_folio))
folio_clear_reclaim(to_folio);
if (ret == AOP_WRITEPAGE_ACTIVATE)

View File

@@ -489,7 +489,7 @@ static const struct vm_operations_struct ttm_bo_vm_ops = {
int ttm_bo_mmap_obj(struct vm_area_struct *vma, struct ttm_buffer_object *bo)
{
/* Enforce no COW since would have really strange behavior with it. */
if (is_cow_mapping(vma->vm_flags))
if (vma_is_cow_mapping(vma))
return -EINVAL;
drm_gem_object_get(&bo->base);

View File

@@ -331,7 +331,7 @@ static int xe_pci_barrier_mmap(struct file *filp,
if (vma->vm_end - vma->vm_start > SZ_4K)
return -EINVAL;
if (is_cow_mapping(vma->vm_flags))
if (vma_is_cow_mapping(vma))
return -EINVAL;
if (vma->vm_flags & (VM_READ | VM_EXEC))

View File

@@ -145,26 +145,6 @@ static void nfs_direct_file_adjust_size_locked(struct inode *inode,
}
}
/**
* nfs_swap_rw - NFS address space operation for swap I/O
* @iocb: target I/O control block
* @iter: I/O buffer
*
* Perform IO to the swap-file. This is much like direct IO.
*/
int nfs_swap_rw(struct kiocb *iocb, struct iov_iter *iter)
{
ssize_t ret;
if (iov_iter_rw(iter) == READ)
ret = nfs_file_direct_read(iocb, iter, true);
else
ret = nfs_file_direct_write(iocb, iter, true);
if (ret < 0)
return ret;
return 0;
}
static void nfs_direct_release_pages(struct page **pages, unsigned int npages)
{
unsigned int i;

View File

@@ -29,9 +29,8 @@
#include <linux/pagemap.h>
#include <linux/gfp.h>
#include <linux/rmap.h>
#include <linux/swap.h>
#include <linux/compaction.h>
#include <linux/swap_ops.h>
#include <linux/uaccess.h>
#include <linux/filelock.h>
@@ -575,6 +574,38 @@ static int nfs_launder_folio(struct folio *folio)
return ret;
}
#ifdef CONFIG_SWAP
static void nfs_swap_submit_write(struct swap_io_ctx *ctx)
{
struct swap_iocb *sio = ctx->sio;
struct iov_iter iter;
int ret;
swap_fs_prepare_rw(ctx, WRITE, &iter);
ret = nfs_file_direct_write(&sio->iocb, &iter, true);
if (ret != -EIOCBQUEUED)
sio->iocb.ki_complete(&sio->iocb, ret);
}
static void nfs_swap_submit_read(struct swap_io_ctx *ctx)
{
struct swap_iocb *sio = ctx->sio;
struct iov_iter iter;
int ret;
swap_fs_prepare_rw(ctx, READ, &iter);
ret = nfs_file_direct_read(&sio->iocb, &iter, true);
if (ret != -EIOCBQUEUED)
sio->iocb.ki_complete(&sio->iocb, ret);
}
static const struct swap_ops nfs_swap_ops = {
.flags = SWAP_OPS_F_REQUIRE_NOFS,
.submit_write = nfs_swap_submit_write,
.submit_read = nfs_swap_submit_read,
.can_merge = swap_fs_can_merge,
};
static int nfs_swap_activate(struct swap_info_struct *sis, struct file *file,
sector_t *span)
{
@@ -597,7 +628,7 @@ static int nfs_swap_activate(struct swap_info_struct *sis, struct file *file,
ret = rpc_clnt_swap_activate(clnt);
if (ret)
return ret;
ret = add_swap_extent(sis, 0, sis->max, 0);
ret = swap_fs_activate(sis, &nfs_swap_ops);
if (ret < 0) {
rpc_clnt_swap_deactivate(clnt);
return ret;
@@ -607,8 +638,6 @@ static int nfs_swap_activate(struct swap_info_struct *sis, struct file *file,
if (cl->rpc_ops->enable_swap)
cl->rpc_ops->enable_swap(inode);
sis->flags |= SWP_FS_OPS;
return ret;
}
@@ -622,6 +651,10 @@ static void nfs_swap_deactivate(struct file *file)
if (cl->rpc_ops->disable_swap)
cl->rpc_ops->disable_swap(file_inode(file));
}
#else
#define nfs_swap_activate NULL
#define nfs_swap_deactivate NULL
#endif /* CONFIG_SWAP */
const struct address_space_operations nfs_file_aops = {
.read_folio = nfs_read_folio,
@@ -638,7 +671,6 @@ const struct address_space_operations nfs_file_aops = {
.error_remove_folio = generic_error_remove_folio,
.swap_activate = nfs_swap_activate,
.swap_deactivate = nfs_swap_deactivate,
.swap_rw = nfs_swap_rw,
};
/*

View File

@@ -1693,7 +1693,7 @@ static inline bool pte_is_pinned(struct vm_area_struct *vma, unsigned long addr,
if (!pte_write(pte))
return false;
if (!is_cow_mapping(vma->vm_flags))
if (!vma_is_cow_mapping(vma))
return false;
if (likely(!mm_flags_test(MMF_HAS_PINNED, vma->vm_mm)))
return false;

View File

@@ -69,6 +69,9 @@ int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize)
gfp_t gfp = mapping_gfp_mask(inode->i_mapping);
/* make various checks */
if (!newsize)
return 0;
order = get_order(newsize);
if (unlikely(order > MAX_PAGE_ORDER))
return -EFBIG;

View File

@@ -20,7 +20,7 @@
#include <linux/delay.h>
#include <linux/mount.h>
#include <linux/slab.h>
#include <linux/swap.h>
#include <linux/swap_ops.h>
#include <linux/mm.h>
#include <asm/div64.h>
#include "cifsfs.h"
@@ -3406,6 +3406,38 @@ void cifs_oplock_break(struct work_struct *work)
cifs_done_oplock_break(cinode);
}
#ifdef CONFIG_SWAP
static void cifs_swap_submit_write(struct swap_io_ctx *ctx)
{
struct swap_iocb *sio = ctx->sio;
struct iov_iter iter;
int ret;
swap_fs_prepare_rw(ctx, WRITE, &iter);
ret = netfs_unbuffered_write_iter_locked(&sio->iocb, &iter, NULL);
if (ret != -EIOCBQUEUED)
sio->iocb.ki_complete(&sio->iocb, ret);
}
static void cifs_swap_submit_read(struct swap_io_ctx *ctx)
{
struct swap_iocb *sio = ctx->sio;
struct iov_iter iter;
int ret;
swap_fs_prepare_rw(ctx, READ, &iter);
ret = netfs_unbuffered_read_iter_locked(&sio->iocb, &iter);
if (ret != -EIOCBQUEUED)
sio->iocb.ki_complete(&sio->iocb, ret);
}
static const struct swap_ops cifs_swap_ops = {
.flags = SWAP_OPS_F_REQUIRE_NOFS,
.submit_write = cifs_swap_submit_write,
.submit_read = cifs_swap_submit_read,
.can_merge = swap_fs_can_merge,
};
static int cifs_swap_activate(struct swap_info_struct *sis,
struct file *swap_file, sector_t *span)
{
@@ -3416,7 +3448,7 @@ static int cifs_swap_activate(struct swap_info_struct *sis,
cifs_dbg(FYI, "swap activate\n");
if (!swap_file->f_mapping->a_ops->swap_rw)
if (swap_file->f_mapping->a_ops != &cifs_addr_ops)
/* Cannot support swap */
return -EINVAL;
@@ -3447,9 +3479,7 @@ static int cifs_swap_activate(struct swap_info_struct *sis,
* but we could add call to grab a byte range lock to prevent others
* from reading or writing the file
*/
sis->flags |= SWP_FS_OPS;
return add_swap_extent(sis, 0, sis->max, 0);
return swap_fs_activate(sis, &cifs_swap_ops);
}
static void cifs_swap_deactivate(struct file *file)
@@ -3465,26 +3495,10 @@ static void cifs_swap_deactivate(struct file *file)
/* do we need to unpin (or unlock) the file */
}
/**
* cifs_swap_rw - SMB3 address space operation for swap I/O
* @iocb: target I/O control block
* @iter: I/O buffer
*
* Perform IO to the swap-file. This is much like direct IO.
*/
static int cifs_swap_rw(struct kiocb *iocb, struct iov_iter *iter)
{
ssize_t ret;
if (iov_iter_rw(iter) == READ)
ret = netfs_unbuffered_read_iter_locked(iocb, iter);
else
ret = netfs_unbuffered_write_iter_locked(iocb, iter, NULL);
if (ret < 0)
return ret;
return 0;
}
#else
#define cifs_swap_activate NULL
#define cifs_swap_deactivate NULL
#endif /* CONFIG_SWAP */
const struct address_space_operations cifs_addr_ops = {
.read_folio = netfs_read_folio,
@@ -3501,7 +3515,6 @@ const struct address_space_operations cifs_addr_ops = {
*/
.swap_activate = cifs_swap_activate,
.swap_deactivate = cifs_swap_deactivate,
.swap_rw = cifs_swap_rw,
};
/*

View File

@@ -77,6 +77,8 @@ struct codetag_iterator {
void codetag_lock_module_list(struct codetag_type *cttype);
bool codetag_trylock_module_list(struct codetag_type *cttype);
void codetag_unlock_module_list(struct codetag_type *cttype);
unsigned long codetag_get_content_id(struct codetag_type *cttype);
unsigned int codetag_get_count(struct codetag_type *cttype);
struct codetag_iterator codetag_get_ct_iter(struct codetag_type *cttype);
struct codetag *codetag_next_ct(struct codetag_iterator *iter);

View File

@@ -89,7 +89,7 @@ static inline int execmem_restore_rox(void *ptr, size_t size) { return 0; }
* @end: address space end (inclusive)
* @fallback_start: start of the secondary address space range for fallback
* allocations on architectures that require it
* @fallback_end: start of the secondary address space (inclusive)
* @fallback_end: end of the secondary address space (inclusive)
* @pgprot: permissions for memory in this address space
* @alignment: alignment required for text allocations
* @flags: options for memory allocations for this range

View File

@@ -438,7 +438,6 @@ struct address_space_operations {
int (*swap_activate)(struct swap_info_struct *sis, struct file *file,
sector_t *span);
void (*swap_deactivate)(struct file *file);
int (*swap_rw)(struct kiocb *iocb, struct iov_iter *iter);
};
extern const struct address_space_operations empty_aops;

View File

@@ -273,6 +273,9 @@ extern struct pin_cookie lock_pin_lock(struct lockdep_map *lock);
extern void lock_repin_lock(struct lockdep_map *lock, struct pin_cookie);
extern void lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie);
extern u32 lock_sequence(struct lockdep_map *lock);
#define lockdep_sequence(lock) lock_sequence(&(lock)->dep_map)
#define lockdep_depth(tsk) (debug_locks ? (tsk)->lockdep_depth : 0)
#define lockdep_assert(cond) \

View File

@@ -253,7 +253,8 @@ struct held_lock {
unsigned int hardirqs_off:1;
unsigned int sync:1;
unsigned int references:11; /* 32 bits */
unsigned int pin_count;
unsigned int pin_count:24;
unsigned int seq_count:8;
};
#else /* !CONFIG_LOCKDEP */

View File

@@ -11,7 +11,6 @@
#include <linux/kernel.h>
#include <linux/rcupdate.h>
#include <linux/spinlock.h>
/* #define CONFIG_MAPLE_RCU_DISABLED */
/*
* Allocated nodes are mutable until they have been inserted into the tree,
@@ -29,13 +28,11 @@
#define MAPLE_NODE_SLOTS 31 /* 256 bytes including ->parent */
#define MAPLE_RANGE64_SLOTS 16 /* 256 bytes */
#define MAPLE_ARANGE64_SLOTS 10 /* 240 bytes */
#define MAPLE_ALLOC_SLOTS (MAPLE_NODE_SLOTS - 1)
#else
/* 32bit sizes */
#define MAPLE_NODE_SLOTS 63 /* 256 bytes including ->parent */
#define MAPLE_RANGE64_SLOTS 32 /* 256 bytes */
#define MAPLE_ARANGE64_SLOTS 21 /* 240 bytes */
#define MAPLE_ALLOC_SLOTS (MAPLE_NODE_SLOTS - 2)
#endif /* defined(CONFIG_64BIT) || defined(BUILD_VDSO32_64) */
#define MAPLE_NODE_MASK 255UL
@@ -485,6 +482,12 @@ struct ma_state {
unsigned char mas_flags;
unsigned char end; /* The end of the node */
enum store_type store_type; /* The type of store needed for this operation */
#ifdef CONFIG_LOCKDEP
u32 ld_seq;
#ifdef CONFIG_RCU_STRICT_GRACE_PERIOD
unsigned long rcu_gp;
#endif /* CONFIG_RCU_STRICT_GRACE_PERIOD */
#endif /* CONFIG_LOCKDEP */
};
struct ma_wr_state {
@@ -567,12 +570,14 @@ int mas_alloc_cyclic(struct ma_state *mas, unsigned long *startp,
unsigned long *next, gfp_t gfp);
bool mas_nomem(struct ma_state *mas, gfp_t gfp);
bool mas_nomem_nofail(struct ma_state *mas, unsigned long index,
unsigned long last);
void mas_pause(struct ma_state *mas);
void maple_tree_init(void);
void mas_destroy(struct ma_state *mas);
void *mas_prev(struct ma_state *mas, unsigned long min);
void *mas_prev_range(struct ma_state *mas, unsigned long max);
void *mas_prev_range(struct ma_state *mas, unsigned long min);
void *mas_next(struct ma_state *mas, unsigned long max);
void *mas_next_range(struct ma_state *mas, unsigned long max);
@@ -864,9 +869,6 @@ static inline void mt_init(struct maple_tree *mt)
static inline bool mt_in_rcu(struct maple_tree *mt)
{
#ifdef CONFIG_MAPLE_RCU_DISABLED
return false;
#endif
return mt->ma_flags & MT_FLAGS_USE_RCU;
}

View File

@@ -239,8 +239,6 @@ struct mem_cgroup {
*/
bool oom_group;
int swappiness;
/* memory.events and memory.events.local */
struct cgroup_file events_file;
struct cgroup_file events_local_file;
@@ -270,10 +268,15 @@ struct mem_cgroup {
#endif
int kmemcg_id;
struct memcg_vmstats_percpu __percpu *vmstats_percpu;
#ifdef CONFIG_CGROUP_WRITEBACK
struct list_head cgwb_list;
#endif
/* Keep the hot per-CPU stats pointer away from memory event counters. */
struct memcg_vmstats_percpu __percpu *vmstats_percpu
____cacheline_aligned_in_smp;
#ifdef CONFIG_CGROUP_WRITEBACK
struct wb_domain cgwb_domain;
struct memcg_cgwb_frn cgwb_frn[MEMCG_CGWB_FRN_CNT];
#endif
@@ -318,6 +321,8 @@ struct mem_cgroup {
/* List of events which userspace want to receive */
struct list_head event_list;
spinlock_t event_list_lock;
int swappiness;
#endif /* CONFIG_MEMCG_V1 */
struct mem_cgroup_per_node *nodeinfo[];
@@ -947,6 +952,8 @@ unsigned long memcg_page_state_output(struct mem_cgroup *memcg, int item);
bool memcg_stat_item_valid(int idx);
bool memcg_vm_event_item_valid(enum vm_event_item idx);
unsigned long lruvec_page_state(struct lruvec *lruvec, enum node_stat_item idx);
unsigned long lruvec_page_state_monotonic(struct lruvec *lruvec,
enum node_stat_item idx);
unsigned long lruvec_page_state_local(struct lruvec *lruvec,
enum node_stat_item idx);
@@ -1399,6 +1406,12 @@ static inline unsigned long lruvec_page_state(struct lruvec *lruvec,
return node_page_state(lruvec_pgdat(lruvec), idx);
}
static inline unsigned long lruvec_page_state_monotonic(struct lruvec *lruvec,
enum node_stat_item idx)
{
return node_page_state_monotonic(lruvec_pgdat(lruvec), idx);
}
static inline unsigned long lruvec_page_state_local(struct lruvec *lruvec,
enum node_stat_item idx)
{

View File

@@ -1556,7 +1556,7 @@ static inline void vma_desc_set_anonymous(struct vm_area_desc *desc)
desc->vm_ops = NULL;
}
static inline bool vma_is_anonymous(struct vm_area_struct *vma)
static inline bool vma_is_anonymous(const struct vm_area_struct *vma)
{
return !vma->vm_ops;
}
@@ -2271,19 +2271,78 @@ void unpin_user_pages(struct page **pages, unsigned long npages);
void unpin_user_folio(struct folio *folio, unsigned long npages);
void unpin_folios(struct folio **folios, unsigned long nfolios);
static inline bool is_cow_mapping(vm_flags_t flags)
/**
* vma_flags_is_cow_mapping() - Do these VMA flags imply a CoW mapping?
* @flags: The VMA flags to check.
*
* Mappings which could be CoW'd (subject to Copy-On-Write faults) are
* described as CoW mappings.
*
* All mappings backed by anonymous folios (all anonymous mappings and most
* MAP_PRIVATE-file backed ranges) are CoW mappings.
*
* All other mappings (including all MAP_SHARED mappings) are non-CoW.
*
* The criteria are !VMA_SHARED_BIT, VMA_MAYWRITE_BIT.
*
* VMA_MAYWRITE_BIT is checked instead of VMA_WRITE_BIT to account for both
* future mprotect() calls which can render a read-only mapping writable, and
* GUP with FOLL_FORCE (e.g. ptrace) which can CoW a read-only mapping.
*
* - No anonymous mapping can ever clear VMA_MAYWRITE_BIT.
*
* - Writes to anonymous mappings do not immediately result in CoW faults but
* may do so after the process is forked or if a read is followed by a
* write.
*
* - Writes to MAP_PRIVATE file-backed mappings result in CoW faults and may
* do so again after fork.
*
* - MAP_SHARED mappings of a file opened read-only are transformed into
* VMA_MAYSHARE_BIT, !VMA_SHARED_BIT, !VMA_MAYWRITE_BIT mappings, so remain
* non-CoW.
*
* - Drivers may clear VMA_MAYWRITE_BIT but do so at mmap() time and cannot
* mark themselves anonymous. Having cleared this flag it is not valid for
* them to leave the VMA_WRITE_BIT flag set.
*
* As a consequence, the anonymous reverse mapping only tracks CoW mappings.
*
* Returns: true if the flags indicate a CoW mapping, otherwise false.
*/
static inline bool vma_flags_is_cow_mapping(const vma_flags_t *flags)
{
return (flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
}
static inline bool vma_desc_is_cow_mapping(struct vm_area_desc *desc)
{
const vma_flags_t *flags = &desc->vma_flags;
return vma_flags_test(flags, VMA_MAYWRITE_BIT) &&
!vma_flags_test(flags, VMA_SHARED_BIT);
}
/**
* vma_is_cow_mapping() - Is this VMA a CoW mapping?
* @vma: The VMA to check.
*
* See vma_flags_is_cow_mapping() for details.
*
* Returns: true if the VMA is a CoW mapping, otherwise false.
*/
static inline bool vma_is_cow_mapping(const struct vm_area_struct *vma)
{
return vma_flags_is_cow_mapping(&vma->flags);
}
/**
* vma_desc_is_cow_mapping() - Is this VMA descriptor a CoW mapping?
* @desc: The VMA descriptor to check.
*
* See vma_flags_is_cow_mapping() for details.
*
* Returns: true if the VMA descriptor describes a CoW mapping, otherwise
* false.
*/
static inline bool vma_desc_is_cow_mapping(struct vm_area_desc *desc)
{
return vma_flags_is_cow_mapping(&desc->vma_flags);
}
#ifndef CONFIG_MMU
static inline bool is_nommu_shared_mapping(vm_flags_t flags)
{
@@ -4393,6 +4452,65 @@ static inline pgoff_t vma_last_pgoff(const struct vm_area_struct *vma)
return vma_end_pgoff(vma) - 1;
}
/**
* vma_start_anon_pgoff() - Get the anonymous page offset of the start of @vma
* @vma: The VMA whose anonymous page offset is required.
*
* If unfaulted, then this is vma->vm_start >> PAGE_SHIFT, if faulted then the
* anonymous page offset at the time of first fault.
*
* If the VMA is anonymous, this returns the same value as vma_start_pgoff().
*
* This value is used for tracking MAP_PRIVATE file-backed mappings by their
* anonymous page offset.
*
* Returns: The anonymous page offset of the start of @vma.
*/
static inline pgoff_t vma_start_anon_pgoff(const struct vm_area_struct *vma)
{
pgoff_t pgoff = 0;
#ifdef CONFIG_64BIT
pgoff += vma->__vm_anon_pgoff_hi;
pgoff <<= 32;
#endif
pgoff += vma->__vm_anon_pgoff_lo;
return pgoff;
}
/**
* vma_end_anon_pgoff() - Get the anonymous page offset of the exclusive end of
* @vma.
* @vma: The VMA whose end anonymous page offset is required.
*
* This returns the anonymous exclusive end page offset of @vma, which is useful
* for expressing page offset ranges.
*
* See the description of vma_start_anon_pgoff() for a description of VMA
* anonymous page offsets.
*
* Returns: The exclusive end anonymous page offset of @vma.
*/
static inline pgoff_t vma_end_anon_pgoff(const struct vm_area_struct *vma)
{
return vma_start_anon_pgoff(vma) + vma_pages(vma);
}
/**
* vma_last_anon_pgoff() - Get the anonymous page offset of the last page in
* @vma.
* @vma: The VMA whose last anonymous page offset is required.
*
* See the description of vma_start_anon_pgoff() for a description of VMA
* anonymous page offsets.
*
* Returns: The last anonymous page offset of @vma.
*/
static inline pgoff_t vma_last_anon_pgoff(const struct vm_area_struct *vma)
{
return vma_end_anon_pgoff(vma) - 1;
}
static inline unsigned long vma_desc_size(const struct vm_area_desc *desc)
{
return desc->end - desc->start;

View File

@@ -967,6 +967,11 @@ struct vm_area_struct {
*/
unsigned int vm_lock_seq;
#endif
/*
* Low 32-bits of anonymous page offset.
* See vma_start_anon_pgoff() comment for details.
*/
unsigned int __vm_anon_pgoff_lo;
/*
* A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
* list, after a COW of one of the file pages. A MAP_SHARED vma
@@ -1041,6 +1046,13 @@ struct vm_area_struct {
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map vmlock_dep_map;
#endif
#endif
#ifdef CONFIG_64BIT
/*
* High 32-bits of anonymous page offset.
* See vma_start_anon_pgoff() comment for details.
*/
unsigned int __vm_anon_pgoff_hi;
#endif
/*
* For areas with an address space and backing store,
@@ -1706,20 +1718,20 @@ enum vm_fault_reason {
VM_FAULT_SIGSEGV | VM_FAULT_HWPOISON | \
VM_FAULT_HWPOISON_LARGE | VM_FAULT_FALLBACK)
#define VM_FAULT_RESULT_TRACE \
{ VM_FAULT_OOM, "OOM" }, \
{ VM_FAULT_SIGBUS, "SIGBUS" }, \
{ VM_FAULT_MAJOR, "MAJOR" }, \
{ VM_FAULT_HWPOISON, "HWPOISON" }, \
{ VM_FAULT_HWPOISON_LARGE, "HWPOISON_LARGE" }, \
{ VM_FAULT_SIGSEGV, "SIGSEGV" }, \
{ VM_FAULT_NOPAGE, "NOPAGE" }, \
{ VM_FAULT_LOCKED, "LOCKED" }, \
{ VM_FAULT_RETRY, "RETRY" }, \
{ VM_FAULT_FALLBACK, "FALLBACK" }, \
{ VM_FAULT_DONE_COW, "DONE_COW" }, \
{ VM_FAULT_NEEDDSYNC, "NEEDDSYNC" }, \
{ VM_FAULT_COMPLETED, "COMPLETED" }
#define VM_FAULT_RESULT_TRACE \
{ (__force u32)VM_FAULT_OOM, "OOM" }, \
{ (__force u32)VM_FAULT_SIGBUS, "SIGBUS" }, \
{ (__force u32)VM_FAULT_MAJOR, "MAJOR" }, \
{ (__force u32)VM_FAULT_HWPOISON, "HWPOISON" }, \
{ (__force u32)VM_FAULT_HWPOISON_LARGE, "HWPOISON_LARGE" }, \
{ (__force u32)VM_FAULT_SIGSEGV, "SIGSEGV" }, \
{ (__force u32)VM_FAULT_NOPAGE, "NOPAGE" }, \
{ (__force u32)VM_FAULT_LOCKED, "LOCKED" }, \
{ (__force u32)VM_FAULT_RETRY, "RETRY" }, \
{ (__force u32)VM_FAULT_FALLBACK, "FALLBACK" }, \
{ (__force u32)VM_FAULT_DONE_COW, "DONE_COW" }, \
{ (__force u32)VM_FAULT_NEEDDSYNC, "NEEDDSYNC" }, \
{ (__force u32)VM_FAULT_COMPLETED, "COMPLETED" }
struct vm_special_mapping {
const char *name; /* The name, e.g. "[vdso]". */

View File

@@ -323,6 +323,8 @@ enum node_stat_item {
PGSCAN_PROACTIVE,
PGSCAN_ANON,
PGSCAN_FILE,
PGROTATE_ANON,
PGROTATE_FILE,
PGREFILL,
#ifdef CONFIG_HUGETLB_PAGE
NR_HUGETLB,
@@ -755,6 +757,12 @@ void lru_gen_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent,
#endif /* CONFIG_LRU_GEN */
struct lru_cost {
unsigned long count;
unsigned long last_rotated;
unsigned long last_io;
};
struct lruvec {
struct list_head lists[NR_LRU_LISTS];
/* per lruvec lru_lock for memcg */
@@ -763,9 +771,12 @@ struct lruvec {
* These track the cost of reclaiming one LRU - file or anon -
* over the other. As the observed cost of reclaiming one LRU
* increases, the reclaim scan balance tips toward the other.
* Updated and decayed at prepare_scan_control() time; cost_lock
* serialises that update.
*/
unsigned long anon_cost;
unsigned long file_cost;
struct lru_cost cost[ANON_AND_FILE];
/* Protects cost[]. */
spinlock_t cost_lock;
/* Non-resident age, driven by LRU movement */
atomic_long_t nonresident_age;
/* Refaults at the time of last reclaim cycle */
@@ -2058,7 +2069,6 @@ static inline struct mem_section *__nr_to_section(unsigned long nr)
#endif
return &mem_section[root][nr & SECTION_ROOT_MASK];
}
extern size_t mem_section_usage_size(void);
/*
* We use the lower bits of the mem_map pointer to store a little bit of

View File

@@ -552,7 +552,6 @@ static inline const struct cred *nfs_file_cred(struct file *file)
/*
* linux/fs/nfs/direct.c
*/
int nfs_swap_rw(struct kiocb *iocb, struct iov_iter *iter);
ssize_t nfs_file_direct_read(struct kiocb *iocb,
struct iov_iter *iter, bool swap);
ssize_t nfs_file_direct_write(struct kiocb *iocb,

View File

@@ -55,7 +55,6 @@ struct page_ext {
extern bool early_page_ext;
extern unsigned long page_ext_size;
extern void pgdat_page_ext_init(struct pglist_data *pgdat);
static inline bool early_page_ext_enabled(void)
{
@@ -202,10 +201,6 @@ static inline bool early_page_ext_enabled(void)
return false;
}
static inline void pgdat_page_ext_init(struct pglist_data *pgdat)
{
}
static inline void page_ext_init(void)
{
}

View File

@@ -1094,10 +1094,44 @@ static inline pgoff_t linear_page_delta(const struct vm_area_struct *vma,
static inline pgoff_t linear_page_index(const struct vm_area_struct *vma,
const unsigned long address)
{
pgoff_t pgoff;
return linear_page_delta(vma, address) + vma_start_pgoff(vma);
}
static inline pgoff_t __linear_anon_page_index(const struct vm_area_struct *vma,
const unsigned long address)
{
return linear_page_delta(vma, address) + vma_start_anon_pgoff(vma);
}
/**
* linear_anon_page_index() - Determine the absolute anonymous page offset of
* @address within @vma.
* @vma: An anonymous or MAP_PRIVATE file-backed VMA in which @address resides.
* @address: The address whose absolute page offset is required.
*
* This returns the anonymous page offset of @address, which is the page offset
* the address possessed at the time the VMA was first faulted.
*
* For anonymous mappings, this returns the same value as linear_page_index().
*
* For MAP_PRIVATE file-backed mappings, this returns the anonymous page offset
* of @address, which is the page offset the address possessed at the time the
* VMA was first faulted.
*
* It is not valid to call this function for shared file-backed mappings.
*
* Returns: The absolute anonymous page offset of @address within @vma.
*/
static inline pgoff_t linear_anon_page_index(const struct vm_area_struct *vma,
const unsigned long address)
{
const pgoff_t pgoff = __linear_anon_page_index(vma, address);
VM_WARN_ON_ONCE(!vma_is_cow_mapping(vma));
/* Account for MAP_PRIVATE-/dev/zero which is only semi-anonymous. */
if (vma_is_anonymous(vma) && !vma->vm_file)
VM_WARN_ON_ONCE(pgoff != linear_page_index(vma, address));
pgoff = linear_page_delta(vma, address);
pgoff += vma_start_pgoff(vma);
return pgoff;
}

View File

@@ -65,13 +65,8 @@
*
* Archs which need weak percpu definitions should set
* CONFIG_ARCH_MODULE_NEEDS_WEAK_PER_CPU when necessary.
*
* To ensure that the generic code observes the above two
* restrictions, if CONFIG_DEBUG_FORCE_WEAK_PER_CPU is set weak
* definition is used for all cases.
*/
#if (defined(CONFIG_ARCH_MODULE_NEEDS_WEAK_PER_CPU) && defined(MODULE)) || \
defined(CONFIG_DEBUG_FORCE_WEAK_PER_CPU)
#if defined(CONFIG_ARCH_MODULE_NEEDS_WEAK_PER_CPU) && defined(MODULE)
/*
* __pcpu_scope_* dummy variable is used to enforce scope. It
* receives the static modifier when it's used in front of

View File

@@ -843,7 +843,7 @@ static inline int folio_try_share_anon_rmap_pmd(struct folio *folio,
* Called from mm/vmscan.c to handle paging out
*/
int folio_referenced(struct folio *, int is_locked,
struct mem_cgroup *memcg, vm_flags_t *vm_flags);
struct mem_cgroup *memcg, vma_flags_t *vma_flags);
void try_to_migrate(struct folio *folio, enum ttu_flags flags);
void try_to_unmap(struct folio *, enum ttu_flags flags);
@@ -864,13 +864,14 @@ struct page *make_device_exclusive(struct mm_struct *mm, unsigned long addr,
struct page_vma_mapped_walk {
unsigned long pfn;
unsigned long nr_pages;
pgoff_t pgoff;
pgoff_t pgoff; /* Only meaningful if nr_pages > 1 and not a KSM walk */
struct vm_area_struct *vma;
unsigned long address;
pmd_t *pmd;
pte_t *pte;
spinlock_t *ptl;
unsigned int flags;
bool pgoff_is_anon : 1;
};
#define DEFINE_FOLIO_VMA_WALK(name, _folio, _vma, _address, _flags) \
@@ -881,6 +882,7 @@ struct page_vma_mapped_walk {
.vma = _vma, \
.address = _address, \
.flags = _flags, \
.pgoff_is_anon = folio_test_anon(_folio), \
}
static inline void page_vma_mapped_walk_done(struct page_vma_mapped_walk *pvmw)
@@ -975,10 +977,9 @@ struct anon_vma *folio_lock_anon_vma_read(const struct folio *folio,
#define anon_vma_prepare(vma) (0)
static inline int folio_referenced(struct folio *folio, int is_locked,
struct mem_cgroup *memcg,
vm_flags_t *vm_flags)
struct mem_cgroup *memcg, vma_flags_t *vma_flags)
{
*vm_flags = 0;
vma_flags_clear_all(vma_flags);
return 0;
}

View File

@@ -1298,6 +1298,7 @@ struct task_struct {
u64 curr_chain_key;
int lockdep_depth;
unsigned int lockdep_recursion;
unsigned int lockdep_seq;
struct held_lock held_locks[MAX_LOCK_DEPTH];
#endif

View File

@@ -12,8 +12,6 @@
#include <linux/userfaultfd_k.h>
#include <linux/bits.h>
struct swap_iocb;
/* inode in-kernel data */
#ifdef CONFIG_TMPFS_QUOTA
@@ -123,8 +121,7 @@ static inline bool shmem_mapping(const struct address_space *mapping)
void shmem_unlock_mapping(struct address_space *mapping);
struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
pgoff_t index, gfp_t gfp_mask);
int shmem_writeout(struct folio *folio, struct swap_iocb **plug,
struct list_head *folio_list);
int shmem_write_folio(struct folio *folio);
void shmem_truncate_range(struct inode *inode, loff_t start, uoff_t end);
int shmem_unuse(unsigned int type);

View File

@@ -202,7 +202,6 @@ enum {
SWP_SOLIDSTATE = (1 << 4), /* blkdev seeks are cheap */
SWP_BLKDEV = (1 << 6), /* its a block device */
SWP_ACTIVATED = (1 << 7), /* set after swap_activate success */
SWP_FS_OPS = (1 << 8), /* swapfile operations go through fs */
SWP_AREA_DISCARD = (1 << 9), /* single-time swap area discards */
SWP_PAGE_DISCARD = (1 << 10), /* freed swap page-cluster discards */
SWP_STABLE_WRITES = (1 << 11), /* no overwrite PG_writeback pages */
@@ -276,6 +275,7 @@ struct swap_info_struct {
struct work_struct reclaim_work; /* reclaim worker */
struct list_head discard_clusters; /* discard clusters list */
struct plist_node avail_list; /* entry in swap_avail_head */
const struct swap_ops *ops;
};
static inline swp_entry_t page_swap_entry(struct page *page)
@@ -298,6 +298,14 @@ void folio_add_lru(struct folio *folio);
void folio_mark_accessed(struct folio *folio);
void lru_add_drain_all(void);
enum lru_cache_drained {
LRU_CACHE_NOT_DRAINED,
LRU_CACHE_DRAINED,
LRU_CACHE_DRAINED_ALL,
};
void lru_cache_drain_for_folio(const struct folio *folio,
unsigned int extra_refs, enum lru_cache_drained *drained);
/* linux/mm/folio-compat.c */
void mark_page_accessed(struct page *page);
@@ -309,7 +317,6 @@ static inline bool lru_cache_disabled(void)
}
extern unsigned long shrink_all_memory(unsigned long nr_pages);
extern int vm_swappiness;
long remove_mapping(struct address_space *mapping, struct folio *folio);
#if defined(CONFIG_SYSFS) && defined(CONFIG_NUMA)
@@ -334,7 +341,6 @@ extern void __meminit kswapd_run(int nid);
extern void __meminit kswapd_stop(int nid);
#ifdef CONFIG_SWAP
int add_swap_extent(struct swap_info_struct *sis, unsigned long start_page,
unsigned long nr_pages, sector_t start_block);
int generic_swapfile_activate(struct swap_info_struct *, struct file *,
@@ -468,25 +474,7 @@ static inline int add_swap_extent(struct swap_info_struct *sis,
}
#endif /* CONFIG_SWAP */
#ifdef CONFIG_MEMCG
static inline int mem_cgroup_swappiness(struct mem_cgroup *memcg)
{
/* Cgroup2 doesn't have per-cgroup swappiness */
if (cgroup_subsys_on_dfl(memory_cgrp_subsys))
return READ_ONCE(vm_swappiness);
/* root ? */
if (mem_cgroup_disabled() || mem_cgroup_is_root(memcg))
return READ_ONCE(vm_swappiness);
return READ_ONCE(memcg->swappiness);
}
void lru_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int nid);
#else
static inline int mem_cgroup_swappiness(struct mem_cgroup *memcg)
{
return READ_ONCE(vm_swappiness);
}
#endif
#if defined(CONFIG_SWAP) && defined(CONFIG_MEMCG) && defined(CONFIG_BLK_CGROUP)

44
include/linux/swap_ops.h Normal file
View File

@@ -0,0 +1,44 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _MM_SWAP_OPS_H
#define _MM_SWAP_OPS_H
#include <linux/swap.h> /* for SWAP_CLUSTER_MAX */
struct swap_iocb {
union {
struct kiocb iocb;
struct bio bio;
};
struct bio_vec bvecs[SWAP_CLUSTER_MAX];
int nr_bvecs;
int len;
};
struct swap_io_ctx {
struct swap_iocb *sio;
struct swap_info_struct *sis;
};
/*
* SWAP_OPS_F_REQUIRE_NOFS:
* When set, all reclaim operations must operated as GFS_NOFS and not
* just GFP_NOIO, as GFP_NOIO allocations could recourse into the
* file system backing this swap file.
*/
#define SWAP_OPS_F_REQUIRE_NOFS (1U << 0)
struct swap_ops {
unsigned int flags;
bool (*can_merge)(struct folio *folio, struct folio *prev_folio,
size_t prev_folio_size, int rw);
void (*submit_write)(struct swap_io_ctx *ctx);
void (*submit_read)(struct swap_io_ctx *ctx);
};
void swap_fs_prepare_rw(struct swap_io_ctx *ctx, int rw, struct iov_iter *iter);
bool swap_fs_can_merge(struct folio *folio, struct folio *prev_folio,
size_t prev_folio_size, int rw);
int swap_fs_activate(struct swap_info_struct *sis, const struct swap_ops *ops);
#endif /* _MM_SWAP_OPS_H */

View File

@@ -5,6 +5,7 @@
#include <linux/radix-tree.h>
#include <linux/bug.h>
#include <linux/mm_types.h>
#include <linux/swap.h>
#ifdef CONFIG_MMU
@@ -325,8 +326,8 @@ struct page_vma_mapped_walk;
extern int set_pmd_migration_entry(struct page_vma_mapped_walk *pvmw,
struct page *page);
extern void remove_migration_pmd(struct page_vma_mapped_walk *pvmw,
struct page *new);
void remove_migration_pmd(struct page_vma_mapped_walk *pvmw,
struct folio *folio);
extern void pmd_migration_entry_wait(struct mm_struct *mm, pmd_t *pmd);
@@ -346,7 +347,7 @@ static inline int set_pmd_migration_entry(struct page_vma_mapped_walk *pvmw,
}
static inline void remove_migration_pmd(struct page_vma_mapped_walk *pvmw,
struct page *new)
struct folio *folio)
{
BUILD_BUG();
}

View File

@@ -175,6 +175,10 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT,
KSTACK_REST,
#endif
#endif /* CONFIG_DEBUG_STACK_USAGE */
#ifdef CONFIG_SWAP
NRSWPIN,
NRSWPOUT,
#endif /* CONFIG_SWAP */
NR_VM_EVENT_ITEMS
};

View File

@@ -62,7 +62,7 @@ struct vm_struct {
#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC
unsigned int page_order;
#endif
unsigned int nr_pages;
unsigned long nr_pages;
phys_addr_t phys_addr;
const void *caller;
unsigned long requested_size;

View File

@@ -20,7 +20,6 @@ struct reclaim_stat {
unsigned nr_congested;
unsigned nr_writeback;
unsigned nr_immediate;
unsigned nr_pageout;
unsigned nr_activate[ANON_AND_FILE];
unsigned nr_ref_keep;
unsigned nr_unmap_fail;
@@ -194,6 +193,19 @@ unsigned long global_node_page_state_pages(enum node_stat_item item)
return x;
}
/*
* Non-clamping variant of global_node_page_state() intended for callers that
* snapshot a monotonically-incremented counter and subtract two samples.
* Returns the raw wrapping value so that unsigned modular subtraction stays
* correct across a signed-long overflow (a real hazard on 32-bit) that the
* clamp in global_node_page_state() would otherwise turn into a huge spurious
* delta. Do NOT use for non-monotonic page-count reads.
*/
static inline unsigned long global_node_page_state_monotonic(enum node_stat_item item)
{
return (unsigned long)atomic_long_read(&vm_node_stat[item]);
}
static inline unsigned long global_node_page_state(enum node_stat_item item)
{
VM_WARN_ON_ONCE(vmstat_item_in_bytes(item));
@@ -259,11 +271,14 @@ extern unsigned long node_page_state(struct pglist_data *pgdat,
enum node_stat_item item);
extern unsigned long node_page_state_pages(struct pglist_data *pgdat,
enum node_stat_item item);
extern unsigned long node_page_state_monotonic(struct pglist_data *pgdat,
enum node_stat_item item);
extern void fold_vm_numa_events(void);
#else
#define sum_zone_node_page_state(node, item) global_zone_page_state(item)
#define node_page_state(node, item) global_node_page_state(item)
#define node_page_state_pages(node, item) global_node_page_state_pages(item)
#define node_page_state_monotonic(node, item) global_node_page_state_monotonic(item)
static inline void fold_vm_numa_events(void)
{
}

View File

@@ -25,7 +25,7 @@
/*
* Per-cpu variables require a unique name although static in some
* configurations (e.g. CONFIG_DEBUG_FORCE_WEAK_PER_CPU or alpha modules).
* configurations (e.g. alpha modules).
*/
#define DA_MON_NAME CONCATENATE(da_mon_, MONITOR_NAME)

View File

@@ -0,0 +1,99 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/*
* alloc_tag IOCTL API definition
*
* Copyright (C) 2026 Google, LLC. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _UAPI_ALLOC_TAG_H
#define _UAPI_ALLOC_TAG_H
#include <linux/types.h>
/*
* Function, file and module names often have the same prefixes, therefore
* when filtering by these criteria, we compare the last 64 characters to
* minimize the chances of name collisions
*/
#define ALLOCINFO_STR_SIZE 64
struct allocinfo_content_id {
__u64 id;
};
struct allocinfo_tag {
/* Longer names are trimmed */
char modname[ALLOCINFO_STR_SIZE];
char function[ALLOCINFO_STR_SIZE];
char filename[ALLOCINFO_STR_SIZE];
__u64 lineno;
};
/* The alignment ensures 32-bit compatible interfaces are not broken */
struct allocinfo_counter {
__u64 bytes;
__u64 calls;
__u8 accurate;
} __attribute__((aligned(8)));
struct allocinfo_tag_data {
struct allocinfo_tag tag;
struct allocinfo_counter counter;
};
enum {
ALLOCINFO_FILTER_MODNAME,
ALLOCINFO_FILTER_FUNCTION,
ALLOCINFO_FILTER_FILENAME,
ALLOCINFO_FILTER_LINENO,
ALLOCINFO_FILTER_INACCURATE,
ALLOCINFO_FILTER_MIN_SIZE,
ALLOCINFO_FILTER_MAX_SIZE,
__ALLOCINFO_FILTER_LAST = ALLOCINFO_FILTER_MAX_SIZE
};
#define ALLOCINFO_FILTER_MASK_MODNAME (1 << ALLOCINFO_FILTER_MODNAME)
#define ALLOCINFO_FILTER_MASK_FUNCTION (1 << ALLOCINFO_FILTER_FUNCTION)
#define ALLOCINFO_FILTER_MASK_FILENAME (1 << ALLOCINFO_FILTER_FILENAME)
#define ALLOCINFO_FILTER_MASK_LINENO (1 << ALLOCINFO_FILTER_LINENO)
#define ALLOCINFO_FILTER_MASK_INACCURATE (1 << ALLOCINFO_FILTER_INACCURATE)
#define ALLOCINFO_FILTER_MASK_MIN_SIZE (1 << ALLOCINFO_FILTER_MIN_SIZE)
#define ALLOCINFO_FILTER_MASK_MAX_SIZE (1 << ALLOCINFO_FILTER_MAX_SIZE)
#define ALLOCINFO_FILTER_MASKS \
((1 << (__ALLOCINFO_FILTER_LAST + 1)) - 1)
struct allocinfo_filter {
__u64 mask; /* bitmask of the filter fields used */
struct allocinfo_tag fields;
__u64 min_size;
__u64 max_size;
/* filter criteria only; see allocinfo_counter.accurate for actual accuracy */
__u64 inaccurate;
};
struct allocinfo_get_at {
/* inputs */
__u64 pos;
struct allocinfo_filter filter;
/* output */
struct allocinfo_tag_data data;
};
#define _ALLOCINFO_IOC_CONTENT_ID 0
#define _ALLOCINFO_IOC_GET_AT 1
#define _ALLOCINFO_IOC_GET_NEXT 2
#define ALLOCINFO_IOC_BASE 0xA6
#define ALLOCINFO_IOC_CONTENT_ID _IOR(ALLOCINFO_IOC_BASE, _ALLOCINFO_IOC_CONTENT_ID, \
struct allocinfo_content_id)
#define ALLOCINFO_IOC_GET_AT _IOWR(ALLOCINFO_IOC_BASE, _ALLOCINFO_IOC_GET_AT, \
struct allocinfo_get_at)
#define ALLOCINFO_IOC_GET_NEXT _IOR(ALLOCINFO_IOC_BASE, _ALLOCINFO_IOC_GET_NEXT, \
struct allocinfo_tag_data)
#endif /* _UAPI_ALLOC_TAG_H */

View File

@@ -513,7 +513,7 @@ int uprobe_write(struct arch_uprobe *auprobe, struct vm_area_struct *vma,
uprobe = container_of(auprobe, struct uprobe, arch);
if (WARN_ON_ONCE(!is_cow_mapping(vma->vm_flags)))
if (WARN_ON_ONCE(!vma_is_cow_mapping(vma)))
return -EINVAL;
/*

View File

@@ -5093,7 +5093,7 @@ static int __lock_is_held(const struct lockdep_map *lock, int read);
static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
int trylock, int read, int check, int hardirqs_off,
struct lockdep_map *nest_lock, unsigned long ip,
int references, int pin_count, int sync)
int references, int pin_count, int sync, int seq)
{
struct task_struct *curr = current;
struct lock_class *class = NULL;
@@ -5199,6 +5199,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
hlock->holdtime_stamp = lockstat_clock();
#endif
hlock->pin_count = pin_count;
hlock->seq_count = seq;
if (check_wait_context(curr, hlock))
return 0;
@@ -5404,7 +5405,7 @@ static int reacquire_held_locks(struct task_struct *curr, unsigned int depth,
hlock->read, hlock->check,
hlock->hardirqs_off,
hlock->nest_lock, hlock->acquire_ip,
hlock->references, hlock->pin_count, 0)) {
hlock->references, hlock->pin_count, 0, hlock->seq_count)) {
case 0:
return 1;
case 1:
@@ -5687,14 +5688,17 @@ static void __lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie cookie
struct held_lock *hlock = curr->held_locks + i;
if (match_held_lock(hlock, lock)) {
int pin_count;
if (WARN(!hlock->pin_count, "unpinning an unpinned lock\n"))
return;
hlock->pin_count -= cookie.val;
pin_count = hlock->pin_count - cookie.val;
if (WARN((int)hlock->pin_count < 0, "pin count corrupted\n"))
hlock->pin_count = 0;
if (WARN(pin_count < 0, "pin count corrupted\n"))
pin_count = 0;
hlock->pin_count = pin_count;
return;
}
}
@@ -5702,6 +5706,24 @@ static void __lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie cookie
WARN(1, "unpinning an unheld lock\n");
}
static u32 __lock_sequence(struct lockdep_map *lock)
{
struct task_struct *curr = current;
int i;
if (unlikely(!debug_locks))
return ~0;
for (i = 0; i < curr->lockdep_depth; i++) {
struct held_lock *hlock = curr->held_locks + i;
if (match_held_lock(hlock, lock))
return hlock->seq_count;
}
return ~0;
}
/*
* Check whether we follow the irq-flags state precisely:
*/
@@ -5884,7 +5906,8 @@ void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
lockdep_recursion_inc();
__lock_acquire(lock, subclass, trylock, read, check,
irqs_disabled_flags(flags), nest_lock, ip, 0, 0, 0);
irqs_disabled_flags(flags), nest_lock, ip, 0, 0, 0,
++current->lockdep_seq);
lockdep_recursion_finish();
raw_local_irq_restore(flags);
}
@@ -5932,7 +5955,8 @@ void lock_sync(struct lockdep_map *lock, unsigned subclass, int read,
lockdep_recursion_inc();
__lock_acquire(lock, subclass, 0, read, check,
irqs_disabled_flags(flags), nest_lock, ip, 0, 0, 1);
irqs_disabled_flags(flags), nest_lock, ip, 0, 0, 1,
++current->lockdep_seq);
check_chain_key(current);
lockdep_recursion_finish();
raw_local_irq_restore(flags);
@@ -6018,6 +6042,26 @@ void lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie cookie)
}
EXPORT_SYMBOL_GPL(lock_unpin_lock);
u32 lock_sequence(struct lockdep_map *lock)
{
unsigned long flags;
u32 seq = ~0;
if (unlikely(!lockdep_enabled()))
return seq;
raw_local_irq_save(flags);
check_flags(flags);
lockdep_recursion_inc();
seq = __lock_sequence(lock);
lockdep_recursion_finish();
raw_local_irq_restore(flags);
return seq;
}
EXPORT_SYMBOL_GPL(lock_sequence);
#ifdef CONFIG_LOCK_STAT
static void print_lock_contention_bug(struct task_struct *curr,
struct lockdep_map *lock,

View File

@@ -613,21 +613,6 @@ config BUILTIN_MODULE_RANGES
It also records an anchor symbol to determine the load address of the
section.
config DEBUG_FORCE_WEAK_PER_CPU
bool "Force weak per-cpu definitions"
depends on DEBUG_KERNEL
help
s390 and alpha require percpu variables in modules to be
defined weak to work around addressing range issue which
puts the following two restrictions on percpu variable
definitions.
1. percpu symbols must be unique whether static or not
2. percpu variables can't be defined inside a function
To ensure that generic code follows the above rules, this
option forces all percpu variables to be defined as weak.
config WARN_CONTEXT_ANALYSIS
bool "Compiler context-analysis warnings"
depends on CC_IS_CLANG && CLANG_VERSION >= 230000

View File

@@ -19,6 +19,8 @@ struct codetag_type {
struct codetag_type_desc desc;
/* generates unique sequence number for module load */
unsigned long next_mod_seq;
/* bumped on every module load and unload */
unsigned long content_id;
};
struct codetag_range {
@@ -50,6 +52,20 @@ void codetag_unlock_module_list(struct codetag_type *cttype)
up_read(&cttype->mod_lock);
}
unsigned long codetag_get_content_id(struct codetag_type *cttype)
{
lockdep_assert_held(&cttype->mod_lock);
return cttype->content_id;
}
unsigned int codetag_get_count(struct codetag_type *cttype)
{
lockdep_assert_held(&cttype->mod_lock);
return cttype->count;
}
struct codetag_iterator codetag_get_ct_iter(struct codetag_type *cttype)
{
struct codetag_iterator iter = {
@@ -204,6 +220,7 @@ static int codetag_module_init(struct codetag_type *cttype, struct module *mod)
down_write(&cttype->mod_lock);
cmod->mod_seq = ++cttype->next_mod_seq;
++cttype->content_id;
mod_id = idr_alloc(&cttype->mod_idr, cmod, 0, 0, GFP_KERNEL);
if (mod_id >= 0) {
if (cttype->desc.module_load) {
@@ -368,6 +385,7 @@ void codetag_unload_module(struct module *mod)
cttype->count -= range_size(cttype, &cmod->range);
idr_remove(&cttype->mod_idr, mod_id);
kfree(cmod);
++cttype->content_id;
}
up_write(&cttype->mod_lock);
if (found && cttype->desc.free_section_mem)

View File

@@ -261,6 +261,12 @@ static inline bool mas_is_underflow(struct ma_state *mas)
return mas->status == ma_underflow;
}
static inline void mas_make_walkable(struct ma_state *mas)
{
if (!mas_is_active(mas) && !mas_is_start(mas))
mas->status = ma_start;
}
static __always_inline struct maple_node *mte_to_node(
const struct maple_enode *entry)
{
@@ -356,11 +362,6 @@ static __always_inline bool mte_is_root(const struct maple_enode *node)
return ma_is_root(mte_to_node(node));
}
static inline bool mas_is_root_limits(const struct ma_state *mas)
{
return !mas->min && mas->max == ULONG_MAX;
}
static __always_inline bool mt_is_alloc(struct maple_tree *mt)
{
return (mt->ma_flags & MT_FLAGS_ALLOC_RANGE);
@@ -455,46 +456,6 @@ enum maple_type mas_parent_type(struct ma_state *mas, struct maple_enode *enode)
return 0;
}
/*
* mas_set_parent() - Set the parent node and encode the slot
* @mas: The maple state
* @enode: The encoded maple node.
* @parent: The encoded maple node that is the parent of @enode.
* @slot: The slot that @enode resides in @parent.
*
* Slot number is encoded in the enode->parent bit 3-6 or 2-6, depending on the
* parent type.
*/
static inline
void mas_set_parent(struct ma_state *mas, struct maple_enode *enode,
const struct maple_enode *parent, unsigned char slot)
{
unsigned long val = (unsigned long)parent;
unsigned long shift;
unsigned long type;
enum maple_type p_type = mte_node_type(parent);
MAS_BUG_ON(mas, p_type == maple_dense);
MAS_BUG_ON(mas, p_type == maple_leaf_64);
switch (p_type) {
case maple_range_64:
case maple_arange_64:
shift = MAPLE_PARENT_SLOT_SHIFT;
type = MAPLE_PARENT_RANGE64;
break;
default:
case maple_dense:
case maple_leaf_64:
shift = type = 0;
break;
}
val &= ~MAPLE_NODE_MASK; /* Clear all node metadata in parent */
val |= (slot << shift) | type;
mte_to_node(enode)->parent = ma_parent_ptr(val);
}
/*
* mte_parent_slot() - get the parent slot of @enode.
* @enode: The encoded maple node.
@@ -876,6 +837,42 @@ static inline void ma_set_meta_gap(struct maple_node *mn, enum maple_type mt,
meta->gap = offset;
}
/*
* mas_set_parent_slots() - Bulk operation to set many slot parent pointers
* @mas: The maple state
* @parent: The encoded maple node that is the parent of @enode.
* @slot: The slot that of the @enode.
* @start_slot: The offset into @slot
* @count: The number of slots to set (eg: exclusive)
*/
static inline
void mas_set_parent_slots(struct ma_state *mas, struct maple_enode *parent,
void __rcu **slots, unsigned char start_slot, unsigned char count)
{
unsigned long val;
unsigned long shift;
unsigned long type;
enum maple_type p_type = mte_node_type(parent);
unsigned char i;
MAS_BUG_ON(mas, p_type != maple_range_64 &&
p_type != maple_arange_64);
shift = MAPLE_PARENT_SLOT_SHIFT;
type = MAPLE_PARENT_RANGE64;
val = (unsigned long)parent;
val &= ~MAPLE_NODE_MASK;
for (i = 0; i < count; i++) {
unsigned long pval = val | ((start_slot + i) << shift) | type;
struct maple_enode *child;
child = mt_slot_locked(mas->tree, slots, i);
mte_to_node(child)->parent = ma_parent_ptr(pval);
}
}
/*
* mat_add() - Add a @dead_enode to the ma_topiary of a list of dead nodes.
* @mat: the ma_topiary, a linked list of dead nodes.
@@ -1153,6 +1150,79 @@ static inline void mas_free(struct ma_state *mas, struct maple_enode *used)
ma_free_rcu(mte_to_node(used));
}
#ifdef CONFIG_LOCKDEP
static struct lockdep_map *mas_lockdep_map(struct ma_state *mas)
{
struct maple_tree *mt = mas->tree;
if (mt_external_lock(mt))
return mt->ma_external_lock;
return &(mt->ma_lock).dep_map;
}
#endif
static void mas_lock_check(struct ma_state *mas)
{
#ifdef CONFIG_LOCKDEP
struct lockdep_map *map;
u32 seq;
if (!mas_is_active(mas))
return;
#ifdef CONFIG_RCU_STRICT_GRACE_PERIOD
if (!mt_locked(mas->tree)) {
if (mt_in_rcu(mas->tree))
WARN_ON_ONCE(poll_state_synchronize_rcu(mas->rcu_gp));
}
#endif /* CONFIG_RCU_STRICT_GRACE_PERIOD */
map = mas_lockdep_map(mas);
if (!map)
return;
seq = lock_sequence(map);
if (seq != UINT_MAX && mas->ld_seq != UINT_MAX)
WARN_ON_ONCE(mas->ld_seq != seq);
#endif /* CONFIG_LOCKDEP */
}
static void mas_init_lock_check(struct ma_state *mas)
{
#ifdef CONFIG_LOCKDEP
struct lockdep_map *map;
#ifdef CONFIG_RCU_STRICT_GRACE_PERIOD
if (!mt_locked(mas->tree)) {
if (mt_in_rcu(mas->tree))
mas->rcu_gp = get_state_synchronize_rcu();
return;
}
#endif /* CONFIG_RCU_STRICT_GRACE_PERIOD */
map = mas_lockdep_map(mas);
if (map) /* Update regardless of lock state */
mas->ld_seq = lock_sequence(map);
#endif /* CONFIG_LOCKDEP */
}
static void mas_may_init_lock_check(struct ma_state *mas)
{
#ifdef CONFIG_LOCKDEP
#ifdef CONFIG_RCU_STRICT_GRACE_PERIOD
if (mas_is_start(mas) || mas_is_paused(mas)) {
mas_init_lock_check(mas);
return;
}
#endif /* CONFIG_RCU_STRICT_GRACE_PERIOD */
mas_lock_check(mas);
#endif /* CONFIG_LOCKDEP */
}
/*
* mas_start() - Sets up maple state for operations.
* @mas: The maple state.
@@ -1171,6 +1241,7 @@ static inline struct maple_enode *mas_start(struct ma_state *mas)
if (likely(mas_is_start(mas))) {
struct maple_enode *root;
mas_init_lock_check(mas);
mas->min = 0;
mas->max = ULONG_MAX;
@@ -1501,14 +1572,26 @@ static inline void mas_parent_gap(struct ma_state *mas, unsigned char offset,
goto ascend;
}
static __always_inline void mas_update_gap_known(struct ma_state *mas,
unsigned long gap)
{
unsigned char pslot;
unsigned long p_gap;
pslot = mte_parent_slot(mas->node);
p_gap = ma_gaps(mte_parent(mas->node),
mas_parent_type(mas, mas->node))[pslot];
if (p_gap != gap)
mas_parent_gap(mas, pslot, gap);
}
/*
* mas_update_gap() - Update a nodes gaps and propagate up if necessary.
* @mas: the maple state.
*/
static inline void mas_update_gap(struct ma_state *mas)
{
unsigned char pslot;
unsigned long p_gap;
unsigned long max_gap;
if (!mt_is_alloc(mas->tree))
@@ -1518,13 +1601,7 @@ static inline void mas_update_gap(struct ma_state *mas)
return;
max_gap = mas_max_gap(mas);
pslot = mte_parent_slot(mas->node);
p_gap = ma_gaps(mte_parent(mas->node),
mas_parent_type(mas, mas->node))[pslot];
if (p_gap != max_gap)
mas_parent_gap(mas, pslot, max_gap);
mas_update_gap_known(mas, max_gap);
}
/*
@@ -1540,14 +1617,10 @@ static inline void mas_adopt_children(struct ma_state *mas,
struct maple_node *node = mte_to_node(parent);
void __rcu **slots = ma_slots(node, type);
unsigned long *pivots = ma_pivots(node, type);
struct maple_enode *child;
unsigned char offset;
unsigned char end;
offset = ma_data_end(node, type, pivots, mas->max);
do {
child = mas_slot_locked(mas, slots, offset);
mas_set_parent(mas, child, parent, offset);
} while (offset--);
end = ma_data_end(node, type, pivots, mas->max);
mas_set_parent_slots(mas, parent, slots, 0, end + 1);
}
/*
@@ -1929,15 +2002,10 @@ unsigned long node_copy(struct ma_state *mas, struct maple_node *src,
s_slots = ma_slots(src, s_mt) + start;
s_pivots = ma_pivots(src, s_mt) + start;
memcpy(d_slots, s_slots, size * sizeof(void __rcu *));
if (!ma_is_leaf(d_mt) && s_mt == maple_copy) {
struct maple_enode *edst = mt_mk_node(dst, d_mt);
for (int i = 0; i < size; i++)
mas_set_parent(mas,
mt_slot_locked(mas->tree, d_slots, i),
edst, d_start + i);
}
if (!ma_is_leaf(d_mt) && s_mt == maple_copy)
mas_set_parent_slots(mas, mt_mk_node(dst, d_mt),
d_slots, d_start, size);
d_gaps = ma_gaps(dst, d_mt);
if (d_gaps) {
@@ -2081,8 +2149,8 @@ static inline void mas_wmb_replace(struct ma_state *mas, struct maple_copy *cp)
mas->node = mt_slot_locked(mas->tree, cp->slot, 0);
/* Insert the new data in the tree */
mas_topiary_replace(mas, old_enode, cp->height);
if (!mte_is_leaf(mas->node))
mas_update_gap(mas);
if (mt_is_alloc(mas->tree) && !mte_is_root(mas->node))
mas_update_gap_known(mas, cp->gap[0]);
mtree_range_walk(mas);
}
@@ -2928,13 +2996,6 @@ static inline void mas_extend_spanning_null(struct ma_wr_state *l_wr_mas,
if (r_mas->last < r_wr_mas->r_max)
r_mas->last = r_wr_mas->r_max;
r_mas->offset++;
} else if ((r_mas->last == r_wr_mas->r_max) &&
(r_mas->last < r_mas->max) &&
!mas_slot_locked(r_mas, r_wr_mas->slots, r_mas->offset + 1)) {
r_mas->last = mas_safe_pivot(r_mas, r_wr_mas->pivots,
r_wr_mas->type, r_mas->offset + 1);
r_mas->offset++;
r_wr_mas->r_max = r_mas->last;
}
}
@@ -3125,7 +3186,7 @@ static void mas_wr_spanning_store(struct ma_wr_state *wr_mas)
static inline void mas_wr_node_store(struct ma_wr_state *wr_mas)
{
unsigned char dst_offset, offset_end;
unsigned char copy_size, node_pivots;
unsigned char copy_size, node_pivots, node_slots;
struct maple_node reuse, *newnode;
unsigned long *dst_pivots;
void __rcu **dst_slots;
@@ -3138,6 +3199,7 @@ static inline void mas_wr_node_store(struct ma_wr_state *wr_mas)
in_rcu = mt_in_rcu(mas->tree);
offset_end = wr_mas->offset_end;
node_pivots = mt_pivots[wr_mas->type];
node_slots = mt_slots[wr_mas->type];
/* Assume last adds an entry */
new_end = mas->end + 1 - offset_end + mas->offset;
if (mas->last == wr_mas->end_piv) {
@@ -3149,7 +3211,6 @@ static inline void mas_wr_node_store(struct ma_wr_state *wr_mas)
if (in_rcu) {
newnode = mas_pop_node(mas);
} else {
memset(&reuse, 0, sizeof(struct maple_node));
newnode = &reuse;
}
@@ -3193,7 +3254,21 @@ static inline void mas_wr_node_store(struct ma_wr_state *wr_mas)
dst_pivots[new_end] = mas->max;
done:
mas_leaf_set_meta(newnode, maple_leaf_64, new_end);
if (!in_rcu && new_end + 2 < node_slots) {
unsigned char clear_from = new_end + 1;
/*
* Note that the last slot is never cleared, since the metadata
* will be stored there or it has a value.
*/
memset(dst_slots + clear_from, 0,
sizeof(void __rcu *) * (node_slots - clear_from));
if (clear_from < node_pivots)
memset(dst_pivots + clear_from, 0,
sizeof(unsigned long) * (node_pivots - clear_from));
}
mas_leaf_set_meta(newnode, wr_mas->type, new_end);
if (in_rcu) {
struct maple_enode *old_enode = mas->node;
@@ -3218,7 +3293,7 @@ static inline void mas_wr_slot_store(struct ma_wr_state *wr_mas)
void __rcu **slots = wr_mas->slots;
bool gap = false;
gap |= !mt_slot_locked(mas->tree, slots, offset);
gap |= !wr_mas->content;
gap |= !mt_slot_locked(mas->tree, slots, offset + 1);
if (wr_mas->offset_end - offset == 1) {
@@ -3595,7 +3670,7 @@ static inline void mas_wr_prealloc_setup(struct ma_wr_state *wr_mas)
/**
* mas_prealloc_calc() - Calculate number of nodes needed for a
* given store oepration
* given store operation
* @wr_mas: The maple write state
* @entry: The entry to store into the tree
*
@@ -3661,6 +3736,9 @@ static inline enum store_type mas_wr_store_type(struct ma_wr_state *wr_mas)
{
struct ma_state *mas = wr_mas->mas;
unsigned char new_end;
bool appending;
bool one_slot;
bool in_rcu;
if (unlikely(mas_is_none(mas) || mas_is_ptr(mas)))
return wr_store_root;
@@ -3680,21 +3758,30 @@ static inline enum store_type mas_wr_store_type(struct ma_wr_state *wr_mas)
return wr_new_root;
new_end = mas_wr_new_end(wr_mas);
in_rcu = mt_in_rcu(mas->tree);
appending = mas->offset == mas->end;
one_slot = wr_mas->offset_end - mas->offset == 1;
/* Potential spanning rebalance collapsing a node */
if (new_end < mt_min_slots[wr_mas->type]) {
if (!mte_is_root(mas->node))
return wr_rebalance;
if (!in_rcu) {
if (appending)
return wr_append;
else if (mas->end == new_end && one_slot)
return wr_slot_store;
}
return wr_node_store;
}
if (new_end >= mt_slots[wr_mas->type])
return wr_split_store;
if (!mt_in_rcu(mas->tree) && (mas->offset == mas->end))
if (!in_rcu && appending)
return wr_append;
if ((new_end == mas->end) && (!mt_in_rcu(mas->tree) ||
(wr_mas->offset_end - mas->offset == 1)))
if (new_end == mas->end && (!in_rcu || one_slot))
return wr_slot_store;
return wr_node_store;
@@ -3793,35 +3880,40 @@ int mas_alloc_cyclic(struct ma_state *mas, unsigned long *startp,
void *entry, unsigned long range_lo, unsigned long range_hi,
unsigned long *next, gfp_t gfp)
{
unsigned long min = range_lo;
int ret = 0;
int ret;
unsigned long min;
min = range_lo;
do {
range_lo = max(min, *next);
ret = mas_empty_area(mas, range_lo, range_hi, 1);
if (ret < 0 && range_lo > min) {
mas_reset(mas);
ret = mas_empty_area(mas, min, range_hi, 1);
if (ret == 0)
ret = 1;
}
if (ret < 0)
goto out;
mas_insert(mas, entry);
} while (mas_nomem(mas, gfp));
if (mas_is_err(mas)) {
ret = xa_err(mas->node);
goto out;
}
range_lo = max(min, *next);
ret = mas_empty_area(mas, range_lo, range_hi, 1);
if ((mas->tree->ma_flags & MT_FLAGS_ALLOC_WRAPPED) && ret == 0) {
mas->tree->ma_flags &= ~MT_FLAGS_ALLOC_WRAPPED;
ret = 1;
}
if (ret < 0 && range_lo > min) {
mas_reset(mas);
ret = mas_empty_area(mas, min, range_hi, 1);
if (ret == 0)
ret = 1;
}
if (ret < 0)
return ret;
do {
mas_insert(mas, entry);
} while (mas_nomem(mas, gfp));
if (mas_is_err(mas))
return xa_err(mas->node);
*startp = mas->index;
*next = *startp + 1;
if (*next == 0)
mas->tree->ma_flags |= MT_FLAGS_ALLOC_WRAPPED;
out:
mas_destroy(mas);
return ret;
}
@@ -4360,8 +4452,8 @@ void *mas_walk(struct ma_state *mas)
{
void *entry;
if (!mas_is_active(mas) && !mas_is_start(mas))
mas->status = ma_start;
mas_may_init_lock_check(mas);
mas_make_walkable(mas);
retry:
entry = mas_state_walk(mas);
if (mas_is_start(mas)) {
@@ -4831,6 +4923,7 @@ void *mas_store(struct ma_state *mas, void *entry)
{
MA_WR_STATE(wr_mas, mas, entry);
mas_may_init_lock_check(mas);
trace_ma_write(TP_FCT, mas, 0, entry);
#ifdef CONFIG_DEBUG_MAPLE_TREE
if (MAS_WARN_ON(mas, mas->index > mas->last))
@@ -4889,6 +4982,7 @@ int mas_store_gfp(struct ma_state *mas, void *entry, gfp_t gfp)
MA_WR_STATE(wr_mas, mas, entry);
int ret = 0;
mas_may_init_lock_check(mas);
retry:
mas_wr_preallocate(&wr_mas, entry);
if (unlikely(mas_nomem(mas, gfp))) {
@@ -4919,6 +5013,7 @@ void mas_store_prealloc(struct ma_state *mas, void *entry)
{
MA_WR_STATE(wr_mas, mas, entry);
mas_lock_check(mas);
if (mas->store_type == wr_store_root) {
mas_wr_prealloc_setup(&wr_mas);
goto store;
@@ -4951,6 +5046,7 @@ int mas_preallocate(struct ma_state *mas, void *entry, gfp_t gfp)
{
MA_WR_STATE(wr_mas, mas, entry);
mas_may_init_lock_check(mas);
mas_wr_prealloc_setup(&wr_mas);
mas->store_type = mas_wr_store_type(&wr_mas);
mas_prealloc_calc(&wr_mas, entry);
@@ -4997,6 +5093,7 @@ static void mas_may_activate(struct ma_state *mas)
mas->status = ma_start;
} else {
mas->status = ma_active;
mas_lock_check(mas);
}
}
@@ -5074,6 +5171,7 @@ void *mas_next(struct ma_state *mas, unsigned long max)
{
void *entry = NULL;
mas_may_init_lock_check(mas);
if (mas_next_setup(mas, max, &entry))
return entry;
@@ -5097,6 +5195,7 @@ void *mas_next_range(struct ma_state *mas, unsigned long max)
{
void *entry = NULL;
mas_may_init_lock_check(mas);
if (mas_next_setup(mas, max, &entry))
return entry;
@@ -5205,6 +5304,7 @@ void *mas_prev(struct ma_state *mas, unsigned long min)
{
void *entry = NULL;
mas_may_init_lock_check(mas);
if (mas_prev_setup(mas, min, &entry))
return entry;
@@ -5228,6 +5328,7 @@ void *mas_prev_range(struct ma_state *mas, unsigned long min)
{
void *entry = NULL;
mas_may_init_lock_check(mas);
if (mas_prev_setup(mas, min, &entry))
return entry;
@@ -5274,6 +5375,7 @@ EXPORT_SYMBOL_GPL(mt_prev);
*/
void mas_pause(struct ma_state *mas)
{
mas_lock_check(mas);
mas->status = ma_pause;
mas->node = NULL;
}
@@ -5382,6 +5484,7 @@ void *mas_find(struct ma_state *mas, unsigned long max)
{
void *entry = NULL;
mas_may_init_lock_check(mas);
if (mas_find_setup(mas, max, &entry))
return entry;
@@ -5409,6 +5512,7 @@ void *mas_find_range(struct ma_state *mas, unsigned long max)
{
void *entry = NULL;
mas_may_init_lock_check(mas);
if (mas_find_setup(mas, max, &entry))
return entry;
@@ -5428,7 +5532,6 @@ EXPORT_SYMBOL_GPL(mas_find_range);
static bool mas_find_rev_setup(struct ma_state *mas, unsigned long min,
void **entry)
{
switch (mas->status) {
case ma_active:
goto active;
@@ -5521,6 +5624,7 @@ void *mas_find_rev(struct ma_state *mas, unsigned long min)
{
void *entry = NULL;
mas_may_init_lock_check(mas);
if (mas_find_rev_setup(mas, min, &entry))
return entry;
@@ -5547,6 +5651,7 @@ void *mas_find_range_rev(struct ma_state *mas, unsigned long min)
{
void *entry = NULL;
mas_may_init_lock_check(mas);
if (mas_find_rev_setup(mas, min, &entry))
return entry;
@@ -5564,6 +5669,11 @@ EXPORT_SYMBOL_GPL(mas_find_range_rev);
* Searches for @mas->index, sets @mas->index and @mas->last to the range and
* erases that range.
*
* Note that erase requires allocations and will use GFP_KERNEL | __GFP_NOFAIL
* to do so if necessary. If the allocation fails, the internal lock will be
* dropped to retry. An externally locked tree must be protected by a lock that
* allows blocking for this API.
*
* Return: the entry that was erased or %NULL, @mas->index and @mas->last are updated.
*/
void *mas_erase(struct ma_state *mas)
@@ -5572,25 +5682,25 @@ void *mas_erase(struct ma_state *mas)
unsigned long index = mas->index;
MA_WR_STATE(wr_mas, mas, NULL);
if (!mas_is_active(mas) || !mas_is_start(mas))
mas->status = ma_start;
/*
* In low memory situations, the allocation is retried with the gfp flag
* GFP_KERNEL | __GFP_NOFAIL. The internal spinlock is dropped in
* mas_nomem_nofail(), however the external lock is not dropped.
*/
if (mt_external_lock(mas->tree))
might_alloc(GFP_KERNEL);
mas_make_walkable(mas);
write_retry:
entry = mas_state_walk(mas);
if (!entry)
return NULL;
goto out;
/* Must reset to ensure spanning writes of last slot are detected */
mas_reset(mas);
mas_wr_preallocate(&wr_mas, NULL);
if (mas_nomem(mas, GFP_KERNEL)) {
/* in case the range of entry changed when unlocked */
mas->index = mas->last = index;
if (mas_nomem_nofail(mas, index, index))
goto write_retry;
}
if (mas_is_err(mas))
goto out;
mas_wr_store_entry(&wr_mas);
out:
@@ -5601,10 +5711,11 @@ EXPORT_SYMBOL_GPL(mas_erase);
/**
* mas_nomem() - Check if there was an error allocating and do the allocation
* if necessary If there are allocations, then free them.
* if necessary.
*
* @mas: The maple state
* @gfp: The GFP_FLAGS to use for allocations
* Return: true on allocation, false otherwise.
* Return: False on no memory. True otherwise (partial success as well)
*/
bool mas_nomem(struct ma_state *mas, gfp_t gfp)
__must_hold(mas->tree->ma_lock)
@@ -5612,6 +5723,10 @@ bool mas_nomem(struct ma_state *mas, gfp_t gfp)
if (likely(mas->node != MA_ERROR(-ENOMEM)))
return false;
/* Allocations can fail, don't do this. */
WARN_ON_ONCE(!gfpflags_allow_blocking(gfp) &&
mt_external_lock(mas->tree));
if (gfpflags_allow_blocking(gfp) && !mt_external_lock(mas->tree)) {
mtree_unlock(mas->tree);
mas_alloc_nodes(mas, gfp);
@@ -5620,10 +5735,49 @@ bool mas_nomem(struct ma_state *mas, gfp_t gfp)
mas_alloc_nodes(mas, gfp);
}
/*
* Return false on zero forward progress. Partial allocations are kept
* so the retry path will attempt to get the rest. The failure should
* not happen as we try our best to reclaim. The user would need an
* external lock with a non-blocking gfp in a low memory situation.
*/
if (!mas->sheaf && !mas->alloc)
return false;
mas->status = ma_start;
mas_reset(mas);
return true;
}
/**
* mas_nomem_nofail() - Retry allocations with __GFP_NOFAIL, if the maple state
* has stored the -ENOMEM error.
* @mas: The maple state
* @index: The start of the range for the @mas reset
* @last: The end of the range for the @mas reset
*
* Return: false if @mas isn't in an -ENOMEM state. True if the allocation
* happens, the state is reset. The internal lock will be dropped and external
* locks must allow blocking.
*/
bool mas_nomem_nofail(struct ma_state *mas, unsigned long index,
unsigned long last)
__must_hold(mas->tree->ma_lock)
{
gfp_t gfp;
if (likely(mas->node != MA_ERROR(-ENOMEM)))
return false;
gfp = GFP_KERNEL | __GFP_NOFAIL;
if (!mt_external_lock(mas->tree)) {
mtree_unlock(mas->tree);
mas_alloc_nodes(mas, gfp);
mtree_lock(mas->tree);
} else {
mas_alloc_nodes(mas, gfp);
}
mas_set_range(mas, index, last);
return true;
}
@@ -5913,6 +6067,10 @@ EXPORT_SYMBOL(mtree_alloc_rrange);
* Erasing is the same as a walk to an entry then a store of a NULL to that
* ENTIRE range. In fact, it is implemented as such using the advanced API.
*
* Note that erase requires allocations and will use GFP_KERNEL | __GFP_NOFAIL
* to do so if necessary. If the allocation fails, the internal lock will be
* dropped to retry.
*
* Return: The entry stored at the @index or %NULL
*/
void *mtree_erase(struct maple_tree *mt, unsigned long index)
@@ -5922,6 +6080,7 @@ void *mtree_erase(struct maple_tree *mt, unsigned long index)
MA_STATE(mas, mt, index, index);
trace_ma_op(TP_FCT, &mas);
might_alloc(GFP_KERNEL);
mtree_lock(mt);
entry = mas_erase(&mas);
mtree_unlock(mt);

View File

@@ -1151,10 +1151,9 @@ static vm_fault_t dmirror_devmem_fault_alloc_and_copy(struct migrate_vma *args,
if (!dpage && !order)
return VM_FAULT_OOM;
pr_debug("migrating from sys to dev pfn src: 0x%lx pfn dst: 0x%lx\n",
page_to_pfn(spage), page_to_pfn(dpage));
if (dpage) {
pr_debug("migrating from dev to sys pfn src: 0x%lx pfn dst: 0x%lx\n",
page_to_pfn(spage), page_to_pfn(dpage));
lock_page(dpage);
*dst |= migrate_pfn(page_to_pfn(dpage));
}

View File

@@ -1053,6 +1053,9 @@ void xas_split_alloc(struct xa_state *xas, void *entry, unsigned int order,
if (xas->xa_shift + XA_CHUNK_SHIFT > order)
return;
if (xas->xa->xa_flags & XA_FLAGS_ACCOUNT)
gfp |= __GFP_ACCOUNT;
do {
struct xa_node *node;

View File

@@ -125,8 +125,6 @@ config ZSWAP_COMPRESSOR_DEFAULT
config ZSMALLOC
tristate
if ZSMALLOC
menu "Zsmalloc allocator options"
depends on ZSMALLOC
@@ -161,8 +159,6 @@ config ZSMALLOC_CHAIN_SIZE
endmenu
endif
menu "Slab allocator options"
config SLUB
@@ -394,6 +390,7 @@ choice
config FLATMEM_MANUAL
bool "Flat Memory"
depends on !NUMA
depends on !ARCH_SPARSEMEM_ENABLE || ARCH_FLATMEM_ENABLE
help
This option is best suited for non-NUMA systems with
@@ -428,6 +425,7 @@ config SPARSEMEM
config FLATMEM
def_bool y
depends on !NUMA
depends on !SPARSEMEM || FLATMEM_MANUAL
#
@@ -583,12 +581,10 @@ endchoice
config MEMORY_HOTREMOVE
bool "Allow for memory hot remove"
depends on MEMORY_HOTPLUG
select MIGRATION
config MHP_MEMMAP_ON_MEMORY
def_bool y
depends on MEMORY_HOTPLUG && SPARSEMEM_VMEMMAP
depends on ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE
endif # MEMORY_HOTPLUG
@@ -771,6 +767,7 @@ config MEMORY_FAILURE
depends on ARCH_SUPPORTS_MEMORY_FAILURE
bool "Enable recovery from hardware memory errors"
select INTERVAL_TREE
select MIGRATION
help
Enables code to recover from some memory failures on systems
with MCA recovery. This allows a system to continue running
@@ -1227,9 +1224,7 @@ config ZONE_DMA32
config ZONE_DEVICE
bool "Device memory (pmem, HMM, etc...) hotplug support"
depends on MEMORY_HOTPLUG
depends on MEMORY_HOTREMOVE
depends on SPARSEMEM_VMEMMAP
select XARRAY_MULTI
help
@@ -1395,17 +1390,15 @@ menuconfig USERFAULTFD
Enable the userfaultfd() system call that allows to intercept and
handle page faults in userland.
if USERFAULTFD
config PTE_MARKER_UFFD_WP
bool "Userfaultfd write protection support for shmem/hugetlbfs"
default y
depends on HAVE_ARCH_USERFAULTFD_WP
depends on USERFAULTFD && HAVE_ARCH_USERFAULTFD_WP
help
Allows to create marker PTEs for userfaultfd write protection
purposes. It is required to enable userfaultfd write protection on
file-backed memory types like shmem and hugetlbfs.
endif # USERFAULTFD
# multi-gen LRU {
config LRU_GEN
@@ -1471,6 +1464,13 @@ config NUMA_EMU
into virtual nodes when booted with "numa=fake=N", where N is the
number of nodes. This is only useful for debugging.
config GENERIC_ARCH_NUMA
bool
select NUMA_MEMBLKS
help
Enable support for generic NUMA implementation. Currently, RISC-V
and ARM64 use it.
config ARCH_HAS_USER_SHADOW_STACK
bool
help

View File

@@ -326,7 +326,6 @@ config MEM_ALLOC_PROFILING
default n
depends on MMU
depends on PROC_FS
depends on !DEBUG_FORCE_WEAK_PER_CPU
select CODE_TAGGING
select PAGE_EXTENSION
select SLAB_OBJ_EXT

View File

@@ -122,6 +122,7 @@ obj-$(CONFIG_CMA) += cma.o
obj-$(CONFIG_NUMA) += numa.o
obj-$(CONFIG_NUMA_MEMBLKS) += numa_memblks.o
obj-$(CONFIG_NUMA_EMU) += numa_emulation.o
obj-$(CONFIG_GENERIC_ARCH_NUMA) += arch_numa.o
obj-$(CONFIG_BALLOON) += balloon.o
obj-$(CONFIG_PAGE_EXTENSION) += page_ext.o
obj-$(CONFIG_PAGE_TABLE_CHECK) += page_table_check.o

View File

@@ -5,6 +5,8 @@
#include <linux/gfp.h>
#include <linux/kallsyms.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/compat.h>
#include <linux/page_ext.h>
#include <linux/pgalloc_tag.h>
#include <linux/proc_fs.h>
@@ -14,6 +16,7 @@
#include <linux/string_choices.h>
#include <linux/vmalloc.h>
#include <linux/kmemleak.h>
#include <uapi/linux/alloc_tag.h>
#include "internal.h"
#include "page_alloc.h"
@@ -59,6 +62,11 @@ struct allocinfo_private {
struct codetag_iterator iter;
struct codetag_iterator reported_iter;
bool print_header;
struct allocinfo_filter filter;
/* ioctl uses a separate iterator not to interfere with reads */
struct codetag_iterator ioctl_iter;
bool positioned; /* seq_open_private() sets to 0 */
struct mutex ioctl_lock;
};
static void *allocinfo_start(struct seq_file *m, loff_t *pos)
@@ -142,6 +150,340 @@ static const struct seq_operations allocinfo_seq_op = {
.show = allocinfo_show,
};
/*
* Initializes seq_file operations and allocates private state when opening
* the /proc/allocinfo procfs entry.
*/
static int allocinfo_open(struct inode *inode, struct file *file)
{
int ret;
ret = seq_open_private(file, &allocinfo_seq_op,
sizeof(struct allocinfo_private));
if (!ret) {
struct seq_file *m = file->private_data;
struct allocinfo_private *priv = m->private;
mutex_init(&priv->ioctl_lock);
}
return ret;
}
/*
* Cleans up the seq_file state and frees up the private state allocated in
* allocinfo_open() when closing the /proc/allocinfo file descriptor.
*/
static int allocinfo_release(struct inode *inode, struct file *file)
{
struct seq_file *m = file->private_data;
struct allocinfo_private *priv = m->private;
mutex_destroy(&priv->ioctl_lock);
return seq_release_private(inode, file);
}
/*
* Returns a pointer to the suffix of a string so that its length fits within
* ALLOCINFO_STR_SIZE, preserving the trailing characters.
* Function, file and module names often have the same prefixes, therefore
* when filtering by these criteria, we compare the last 64 characters to
* minimize the chances of name collisions
*/
static const char *allocinfo_str(const char *str)
{
size_t len = strlen(str);
/* Keep an extra space for the trailing NULL. */
if (len >= ALLOCINFO_STR_SIZE)
str += (len - ALLOCINFO_STR_SIZE) + 1;
return str;
}
/* Copy a string and trim from the beginning if it's too long */
static void allocinfo_copy_str(char *dest, const char *src)
{
strscpy_pad(dest, allocinfo_str(src), ALLOCINFO_STR_SIZE);
}
/* Compare two strings and only consider the trimmed suffix if s1 is too long */
static int allocinfo_cmp_str(const char *str, const char *template)
{
return strncmp(allocinfo_str(str), template, ALLOCINFO_STR_SIZE);
}
/* Fetch the per-CPU counters */
static inline struct alloc_tag_counters allocinfo_prefetch_counters(struct codetag *ct)
{
return alloc_tag_read(ct_to_alloc_tag(ct));
}
/*
* Populates the UAPI allocinfo_tag_data structure with active runtime
* profiling counters extracted from the given kernel codetag.
*/
static void allocinfo_to_params(struct codetag *ct,
struct allocinfo_tag_data *data,
struct alloc_tag_counters *counters)
{
if (ct->modname)
allocinfo_copy_str(data->tag.modname, ct->modname);
else
data->tag.modname[0] = '\0';
allocinfo_copy_str(data->tag.function, ct->function);
allocinfo_copy_str(data->tag.filename, ct->filename);
data->tag.lineno = ct->lineno;
data->counter.bytes = counters->bytes;
data->counter.calls = counters->calls;
data->counter.accurate = !alloc_tag_is_inaccurate(ct_to_alloc_tag(ct));
}
/*
* Retrieves the unique content ID representing the current allocation tag module
* layout, allowing userspace to detect if modules were loaded / unloaded.
*/
static int allocinfo_ioctl_get_content_id(struct seq_file *m, void __user *arg)
{
struct allocinfo_content_id params;
codetag_lock_module_list(alloc_tag_cttype);
params.id = codetag_get_content_id(alloc_tag_cttype);
codetag_unlock_module_list(alloc_tag_cttype);
if (copy_to_user(arg, &params, sizeof(params)))
return -EFAULT;
return 0;
}
/*
* Verifies whether a given codetag satisfies the active filtering criteria by
* matching its characteristics against the specified filter.
*/
static bool matches_filter(struct codetag *ct, struct allocinfo_filter *filter,
struct alloc_tag_counters *counters,
bool *fetched_counters)
{
bool inaccurate;
if (!filter || !filter->mask)
return true;
if (filter->mask & ALLOCINFO_FILTER_MASK_MODNAME) {
/* user wants to filter by modname but ct->modname is NULL */
if (!ct->modname) {
/* validate if user was attempting to filter for built-in allocations */
if (filter->fields.modname[0] != '\0')
return false;
} else if (allocinfo_cmp_str(ct->modname, filter->fields.modname))
return false;
}
if ((filter->mask & ALLOCINFO_FILTER_MASK_FUNCTION) &&
ct->function && allocinfo_cmp_str(ct->function, filter->fields.function))
return false;
if ((filter->mask & ALLOCINFO_FILTER_MASK_FILENAME) &&
ct->filename && allocinfo_cmp_str(ct->filename, filter->fields.filename))
return false;
if ((filter->mask & ALLOCINFO_FILTER_MASK_LINENO) &&
ct->lineno != filter->fields.lineno)
return false;
if (filter->mask & ALLOCINFO_FILTER_MASK_INACCURATE) {
inaccurate = !!(ct->flags & CODETAG_FLAG_INACCURATE);
if (inaccurate != !!(filter->inaccurate))
return false;
}
if (filter->mask & (ALLOCINFO_FILTER_MASK_MIN_SIZE | ALLOCINFO_FILTER_MASK_MAX_SIZE)) {
if (!*fetched_counters) {
*counters = allocinfo_prefetch_counters(ct);
*fetched_counters = true;
}
if ((filter->mask & ALLOCINFO_FILTER_MASK_MIN_SIZE) &&
counters->bytes < filter->min_size)
return false;
if ((filter->mask & ALLOCINFO_FILTER_MASK_MAX_SIZE) &&
counters->bytes > filter->max_size)
return false;
}
return true;
}
/*
* Seeks the ioctl iterator to the specified 0-indexed tag position, reads its
* profiling data and returns it to userspace.
*/
static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
{
struct allocinfo_private *priv;
struct codetag *ct;
struct allocinfo_get_at params = {0};
__u64 skip_count;
struct alloc_tag_counters counters;
bool fetched_counters;
if (copy_from_user(&params, arg, sizeof(params)))
return -EFAULT;
if (params.filter.mask & ~ALLOCINFO_FILTER_MASKS)
return -EINVAL;
if ((params.filter.mask & ALLOCINFO_FILTER_MASK_MIN_SIZE) &&
(params.filter.mask & ALLOCINFO_FILTER_MASK_MAX_SIZE) &&
params.filter.min_size > params.filter.max_size)
return -EINVAL;
priv = m->private;
mutex_lock(&priv->ioctl_lock);
codetag_lock_module_list(alloc_tag_cttype);
if (params.pos >= codetag_get_count(alloc_tag_cttype)) {
codetag_unlock_module_list(alloc_tag_cttype);
mutex_unlock(&priv->ioctl_lock);
return -ENOENT;
}
skip_count = params.pos;
if (params.filter.mask)
priv->filter = params.filter;
else
priv->filter.mask = 0;
/* Find the codetag */
priv->ioctl_iter = codetag_get_ct_iter(alloc_tag_cttype);
ct = codetag_next_ct(&priv->ioctl_iter);
while (ct) {
fetched_counters = false;
if (matches_filter(ct, &priv->filter, &counters, &fetched_counters)) {
if (skip_count == 0)
break;
skip_count--;
}
ct = codetag_next_ct(&priv->ioctl_iter);
}
if (ct) {
if (!fetched_counters)
counters = allocinfo_prefetch_counters(ct);
allocinfo_to_params(ct, &params.data, &counters);
priv->positioned = true;
}
codetag_unlock_module_list(alloc_tag_cttype);
mutex_unlock(&priv->ioctl_lock);
if (!ct)
return -ENOENT;
if (copy_to_user(arg, &params, sizeof(params)))
return -EFAULT;
return 0;
}
/*
* Advances the ioctl iterator to the next allocation tag in the sequence and
* returns its profiling data to userspace.
*/
static int allocinfo_ioctl_get_next(struct seq_file *m, void __user *arg)
{
struct allocinfo_private *priv;
struct codetag *ct;
struct allocinfo_tag_data params;
int ret = 0;
struct alloc_tag_counters counters;
bool fetched_counters;
memset(&params, 0, sizeof(params));
priv = m->private;
mutex_lock(&priv->ioctl_lock);
codetag_lock_module_list(alloc_tag_cttype);
if (!priv->positioned) {
priv->ioctl_iter = codetag_get_ct_iter(alloc_tag_cttype);
priv->positioned = true;
}
ct = codetag_next_ct(&priv->ioctl_iter);
while (ct) {
fetched_counters = false;
if (matches_filter(ct, &priv->filter, &counters, &fetched_counters))
break;
ct = codetag_next_ct(&priv->ioctl_iter);
}
if (ct) {
if (!fetched_counters)
counters = allocinfo_prefetch_counters(ct);
allocinfo_to_params(ct, &params, &counters);
}
if (!ct) {
priv->positioned = false;
ret = -ENOENT;
}
codetag_unlock_module_list(alloc_tag_cttype);
mutex_unlock(&priv->ioctl_lock);
if (ret == 0) {
if (copy_to_user(arg, &params, sizeof(params)))
return -EFAULT;
}
return ret;
}
/*
* Entry point ioctl function for /proc/allocinfo routing requests to fetch the
* layout content ID, seek to a specific tag, or read sequential tags.
*/
static long allocinfo_ioctl(struct file *file, unsigned int cmd,
unsigned long __arg)
{
void __user *arg = (void __user *)__arg;
int ret;
switch (cmd) {
case ALLOCINFO_IOC_CONTENT_ID:
ret = allocinfo_ioctl_get_content_id(file->private_data, arg);
break;
case ALLOCINFO_IOC_GET_AT:
ret = allocinfo_ioctl_get_at(file->private_data, arg);
break;
case ALLOCINFO_IOC_GET_NEXT:
ret = allocinfo_ioctl_get_next(file->private_data, arg);
break;
default:
ret = -ENOIOCTLCMD;
break;
}
return ret;
}
#ifdef CONFIG_COMPAT
static long allocinfo_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
return allocinfo_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
}
#endif
static const struct proc_ops allocinfo_proc_ops = {
.proc_open = allocinfo_open,
.proc_read_iter = seq_read_iter,
.proc_lseek = seq_lseek,
.proc_release = allocinfo_release,
.proc_ioctl = allocinfo_ioctl,
#ifdef CONFIG_COMPAT
.proc_compat_ioctl = allocinfo_compat_ioctl,
#endif
};
size_t alloc_tag_top_users(struct codetag_bytes *tags, size_t count, bool can_sleep)
{
struct codetag_iterator iter;
@@ -961,6 +1303,12 @@ static const struct ctl_table memory_allocation_profiling_sysctls[] = {
.mode = 0644,
.proc_handler = proc_mem_profiling_handler,
},
{
.procname = "mem_profiling_compressed",
.data = &mem_profiling_compressed,
.mode = 0444,
.proc_handler = proc_do_static_key,
},
};
static void __init sysctl_init(void)
@@ -993,8 +1341,7 @@ static int __init alloc_tag_init(void)
return 0;
}
if (!proc_create_seq_private(ALLOCINFO_FILE_NAME, 0400, NULL, &allocinfo_seq_op,
sizeof(struct allocinfo_private), NULL)) {
if (!proc_create(ALLOCINFO_FILE_NAME, 0400, NULL, &allocinfo_proc_ops)) {
pr_err("Failed to create %s file\n", ALLOCINFO_FILE_NAME);
shutdown_mem_profiling(false);
return -ENOMEM;

View File

@@ -105,6 +105,18 @@ static void __init setup_node_to_cpumask_map(void)
if (nr_node_ids == MAX_NUMNODES)
setup_nr_node_ids();
/*
* This check should never be true but it makes it clear to compilers
* that node_to_cpumask_map is bound by nr_node_ids, avoiding false
* positive fortify warnings when accessing node_to_cpumask_map in the
* for loop below.
*/
if (unlikely(nr_node_ids > MAX_NUMNODES)) {
pr_err("nr_node_ids (%u) is larger than MAX_NUMNODES (%u)\n",
nr_node_ids, MAX_NUMNODES);
return;
}
/* allocate and clear the mapping */
for (node = 0; node < nr_node_ids; node++) {
alloc_bootmem_cpumask_var(&node_to_cpumask_map[node]);

View File

@@ -242,7 +242,7 @@ static int __init cma_new_area(const char *name, phys_addr_t size,
if (name)
strscpy(cma->name, name);
else
snprintf(cma->name, CMA_MAX_NAME, "cma%d\n", cma_area_count);
snprintf(cma->name, CMA_MAX_NAME, "cma%d", cma_area_count);
cma->available_count = cma->count = size >> PAGE_SHIFT;
cma->order_per_bit = order_per_bit;

View File

@@ -2,6 +2,7 @@
#ifndef __MM_CMA_H__
#define __MM_CMA_H__
#include <linux/cma.h>
#include <linux/debugfs.h>
#include <linux/kobject.h>

View File

@@ -20,14 +20,14 @@ early_param("debug_pagealloc", early_debug_pagealloc);
static int __init debug_guardpage_minorder_setup(char *buf)
{
unsigned long res;
unsigned int res;
if (kstrtoul(buf, 10, &res) < 0 || res > MAX_PAGE_ORDER / 2) {
pr_err("Bad debug_guardpage_minorder value: %s\n", buf);
if (!buf || kstrtouint(buf, 10, &res) < 0 || res > MAX_PAGE_ORDER / 2) {
pr_err("Bad debug_guardpage_minorder value: %s\n", buf ?: "(missing)");
return 0;
}
_debug_guardpage_minorder = res;
pr_info("Setting debug_guardpage_minorder to %lu\n", res);
pr_info("Setting debug_guardpage_minorder to %u\n", res);
return 0;
}
early_param("debug_guardpage_minorder", debug_guardpage_minorder_setup);

View File

@@ -41,6 +41,7 @@ void set_page_writeback(struct page *page)
}
EXPORT_SYMBOL(set_page_writeback);
/* Read the comment above folio_mark_dirty() regarding required locks! */
bool set_page_dirty(struct page *page)
{
return folio_mark_dirty(page_folio(page));

View File

@@ -265,73 +265,6 @@ void folio_rotate_reclaimable(struct folio *folio)
folio_batch_add_and_move(folio, lru_move_tail);
}
void lru_note_cost_unlock_irq(struct lruvec *lruvec, bool file,
unsigned int nr_io, unsigned int nr_rotated)
__releases(lruvec->lru_lock)
__releases(rcu)
{
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;
if (!cost) {
spin_unlock_irq(&lruvec->lru_lock);
rcu_read_unlock();
return;
}
for (;;) {
unsigned long lrusize;
/* 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);
lruvec = parent_lruvec(lruvec);
if (!lruvec) {
rcu_read_unlock();
break;
}
spin_lock_irq(&lruvec->lru_lock);
}
}
void lru_note_cost_refault(struct folio *folio)
{
struct lruvec *lruvec;
lruvec = folio_lruvec_lock_irq(folio);
lru_note_cost_unlock_irq(lruvec, 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);
@@ -948,6 +881,52 @@ void lru_add_drain_all(void)
}
#endif /* CONFIG_SMP */
/**
* lru_cache_drain_for_folio() - drain LRU caches if the caches might hold
* folio references
* @folio: The folio.
* @extra_refs: Extra folio references held by the caller.
* @drained: Drain status for batch folio processing.
*
* Drain LRU caches if the caches might hold folio references. Start
* with a local LRU cache drain, to then drain LRU caches on all CPUs if
* local draining was insufficient.
*
* This function detects LRU cache references by comparing the folio refcount
* with the sum of the expected folio refcount + extra references held by the
* caller. Note that we cannot rely on PG_lru to reliably detect all LRU
* cache references, and there are rare scenarios (concurrent folio (un)mapping)
* where this function might miss detecting LRU cache references.
*
* If @drained is not NULL, the function will avoid re-draining LRU caches
* when processing multiple folios in a row. In that case, the variable
* @drained points at must be initialized to LRU_CACHE_NOT_DRAINED before
* the first invocation by the caller.
*/
void lru_cache_drain_for_folio(const struct folio *folio,
unsigned int extra_refs, enum lru_cache_drained *drained)
{
if (!folio_may_be_lru_cached(folio))
return;
if (!drained || *drained == LRU_CACHE_NOT_DRAINED) {
if (folio_ref_count(folio) ==
folio_expected_ref_count(folio) + extra_refs)
return;
lru_add_drain();
if (drained)
*drained = LRU_CACHE_DRAINED;
}
if (!drained || *drained == LRU_CACHE_DRAINED) {
if (folio_ref_count(folio) ==
folio_expected_ref_count(folio) + extra_refs)
return;
lru_add_drain_all();
if (drained)
*drained = LRU_CACHE_DRAINED_ALL;
}
}
atomic_t lru_disable_count = ATOMIC_INIT(0);
/*
@@ -1151,7 +1130,16 @@ static void lruvec_reparent_lru(struct lruvec *child_lruvec,
for_each_managed_zone_pgdat(zone, NODE_DATA(nid), zid, MAX_NR_ZONES - 1) {
unsigned long size = mem_cgroup_get_zone_lru_size(child_lruvec, lru, zid);
if (!size)
continue;
/*
* The folios are accounted to the parent from now on, so the
* size has to be moved, not just copied. Leaving it behind
* makes the dying child describe folios it no longer owns.
*/
mem_cgroup_update_lru_size(parent_lruvec, lru, zid, size);
mem_cgroup_update_lru_size(child_lruvec, lru, zid, -(long)size);
}
}
@@ -1162,8 +1150,6 @@ void lru_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int
child_lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid));
parent_lruvec = mem_cgroup_lruvec(parent, NODE_DATA(nid));
parent_lruvec->anon_cost += child_lruvec->anon_cost;
parent_lruvec->file_cost += child_lruvec->file_cost;
for_each_lru(lru)
lruvec_reparent_lru(child_lruvec, parent_lruvec, lru, nid);

View File

@@ -1236,7 +1236,7 @@ static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags)
* Anon pages in shared mappings are surprising: now
* just reject it.
*/
if (!is_cow_mapping(vm_flags))
if (!vma_is_cow_mapping(vma))
return -EFAULT;
}
} else if (!(vm_flags & VM_READ)) {
@@ -2266,13 +2266,14 @@ static unsigned long collect_longterm_unpinnable_folios(
struct list_head *movable_folio_list,
struct pages_or_folios *pofs)
{
enum lru_cache_drained drained = LRU_CACHE_NOT_DRAINED;
unsigned long collected = 0;
struct folio *folio;
int drained = 0;
long i = 0;
for (folio = pofs_get_folio(pofs, i); folio;
folio = pofs_next_folio(folio, pofs, &i)) {
const int pin_refs = folio_has_pincount(folio) ? 1 : GUP_PIN_COUNTING_BIAS;
if (folio_is_longterm_pinnable(folio))
continue;
@@ -2287,18 +2288,12 @@ static unsigned long collect_longterm_unpinnable_folios(
continue;
}
if (drained == 0 && folio_may_be_lru_cached(folio) &&
folio_ref_count(folio) !=
folio_expected_ref_count(folio) + 1) {
lru_add_drain();
drained = 1;
}
if (drained == 1 && folio_may_be_lru_cached(folio) &&
folio_ref_count(folio) !=
folio_expected_ref_count(folio) + 1) {
lru_add_drain_all();
drained = 2;
}
/*
* We drain not only to make the folio_isolate_lru() succeed,
* but also to remove any other folio references from LRU
* caches.
*/
lru_cache_drain_for_folio(folio, pin_refs, &drained);
if (!folio_isolate_lru(folio))
continue;

View File

@@ -8,6 +8,12 @@
#include <linux/highmem.h>
#include "gup_test.h"
struct gup_test_data {
struct mutex longterm_mutex;
struct page **longterm_pages;
unsigned long longterm_nr_pages;
};
static void put_back_pages(unsigned int cmd, struct page **pages,
unsigned long nr_pages, unsigned int gup_test_flags)
{
@@ -208,23 +214,20 @@ static int __gup_test_ioctl(unsigned int cmd,
return ret;
}
static DEFINE_MUTEX(pin_longterm_test_mutex);
static struct page **pin_longterm_test_pages;
static unsigned long pin_longterm_test_nr_pages;
static inline void pin_longterm_test_stop(void)
static inline void pin_longterm_test_stop(struct gup_test_data *data)
{
if (pin_longterm_test_pages) {
if (pin_longterm_test_nr_pages)
unpin_user_pages(pin_longterm_test_pages,
pin_longterm_test_nr_pages);
kvfree(pin_longterm_test_pages);
pin_longterm_test_pages = NULL;
pin_longterm_test_nr_pages = 0;
if (data->longterm_pages) {
if (data->longterm_nr_pages)
unpin_user_pages(data->longterm_pages,
data->longterm_nr_pages);
kvfree(data->longterm_pages);
data->longterm_pages = NULL;
data->longterm_nr_pages = 0;
}
}
static inline int pin_longterm_test_start(unsigned long arg)
static inline int pin_longterm_test_start(struct gup_test_data *data,
unsigned long arg)
{
long nr_pages, cur_pages, addr, remaining_pages;
int gup_flags = FOLL_LONGTERM;
@@ -233,7 +236,7 @@ static inline int pin_longterm_test_start(unsigned long arg)
int ret = 0;
bool fast;
if (pin_longterm_test_pages)
if (data->longterm_pages)
return -EINVAL;
if (copy_from_user(&args, (void __user *)arg, sizeof(args)))
@@ -263,12 +266,12 @@ static inline int pin_longterm_test_start(unsigned long arg)
return -EINTR;
}
pin_longterm_test_pages = pages;
pin_longterm_test_nr_pages = 0;
data->longterm_pages = pages;
data->longterm_nr_pages = 0;
while (nr_pages - pin_longterm_test_nr_pages) {
remaining_pages = nr_pages - pin_longterm_test_nr_pages;
addr = args.addr + pin_longterm_test_nr_pages * PAGE_SIZE;
while (nr_pages - data->longterm_nr_pages) {
remaining_pages = nr_pages - data->longterm_nr_pages;
addr = args.addr + data->longterm_nr_pages * PAGE_SIZE;
if (fast)
cur_pages = pin_user_pages_fast(addr, remaining_pages,
@@ -277,11 +280,11 @@ static inline int pin_longterm_test_start(unsigned long arg)
cur_pages = pin_user_pages(addr, remaining_pages,
gup_flags, pages);
if (cur_pages < 0) {
pin_longterm_test_stop();
pin_longterm_test_stop(data);
ret = cur_pages;
break;
}
pin_longterm_test_nr_pages += cur_pages;
data->longterm_nr_pages += cur_pages;
pages += cur_pages;
}
@@ -290,19 +293,20 @@ static inline int pin_longterm_test_start(unsigned long arg)
return ret;
}
static inline int pin_longterm_test_read(unsigned long arg)
static inline int pin_longterm_test_read(struct gup_test_data *data,
unsigned long arg)
{
__u64 user_addr;
unsigned long i;
if (!pin_longterm_test_pages)
if (!data->longterm_pages)
return -EINVAL;
if (copy_from_user(&user_addr, (void __user *)arg, sizeof(user_addr)))
return -EFAULT;
for (i = 0; i < pin_longterm_test_nr_pages; i++) {
void *addr = kmap_local_page(pin_longterm_test_pages[i]);
for (i = 0; i < data->longterm_nr_pages; i++) {
void *addr = kmap_local_page(data->longterm_pages[i]);
unsigned long ret;
ret = copy_to_user((void __user *)(unsigned long)user_addr, addr,
@@ -318,25 +322,26 @@ static inline int pin_longterm_test_read(unsigned long arg)
static long pin_longterm_test_ioctl(struct file *filep, unsigned int cmd,
unsigned long arg)
{
struct gup_test_data *data = filep->private_data;
int ret = -EINVAL;
if (mutex_lock_killable(&pin_longterm_test_mutex))
if (mutex_lock_killable(&data->longterm_mutex))
return -EINTR;
switch (cmd) {
case PIN_LONGTERM_TEST_START:
ret = pin_longterm_test_start(arg);
ret = pin_longterm_test_start(data, arg);
break;
case PIN_LONGTERM_TEST_STOP:
pin_longterm_test_stop();
pin_longterm_test_stop(data);
ret = 0;
break;
case PIN_LONGTERM_TEST_READ:
ret = pin_longterm_test_read(arg);
ret = pin_longterm_test_read(data, arg);
break;
}
mutex_unlock(&pin_longterm_test_mutex);
mutex_unlock(&data->longterm_mutex);
return ret;
}
@@ -375,15 +380,40 @@ static long gup_test_ioctl(struct file *filep, unsigned int cmd,
return 0;
}
static int gup_test_open(struct inode *inode, struct file *file)
{
struct gup_test_data *data;
int ret;
data = kzalloc_obj(*data);
if (!data)
return -ENOMEM;
ret = nonseekable_open(inode, file);
if (ret) {
kfree(data);
return ret;
}
mutex_init(&data->longterm_mutex);
file->private_data = data;
return 0;
}
static int gup_test_release(struct inode *inode, struct file *file)
{
pin_longterm_test_stop();
struct gup_test_data *data = file->private_data;
pin_longterm_test_stop(data);
mutex_destroy(&data->longterm_mutex);
kfree(data);
file->private_data = NULL;
return 0;
}
static const struct file_operations gup_test_fops = {
.open = nonseekable_open,
.open = gup_test_open,
.unlocked_ioctl = gup_test_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.release = gup_test_release,

View File

@@ -670,7 +670,10 @@ static int hmm_do_fault(struct mm_struct *mm,
ret = handle_mm_fault(vma, addr, fault_flags, NULL);
if (ret & (VM_FAULT_COMPLETED | VM_FAULT_RETRY)) {
*hmm_vma_walk->locked = false;
if (hmm_vma_walk->locked) /* needed by sparse */
*hmm_vma_walk->locked = false;
else
WARN_ON_ONCE(1); /* broken fault handler */
return HMM_FAULT_UNLOCKED;
}

View File

@@ -1681,7 +1681,7 @@ vm_fault_t vmf_insert_pfn_pmd(struct vm_fault *vmf, unsigned long pfn,
BUG_ON(!(vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP)));
BUG_ON((vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP)) ==
(VM_PFNMAP|VM_MIXEDMAP));
BUG_ON((vma->vm_flags & VM_PFNMAP) && is_cow_mapping(vma->vm_flags));
BUG_ON((vma->vm_flags & VM_PFNMAP) && vma_is_cow_mapping(vma));
pfnmap_setup_cachemode_pfn(pfn, &pgprot);
@@ -1789,7 +1789,7 @@ vm_fault_t vmf_insert_pfn_pud(struct vm_fault *vmf, unsigned long pfn,
BUG_ON(!(vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP)));
BUG_ON((vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP)) ==
(VM_PFNMAP|VM_MIXEDMAP));
BUG_ON((vma->vm_flags & VM_PFNMAP) && is_cow_mapping(vma->vm_flags));
BUG_ON((vma->vm_flags & VM_PFNMAP) && vma_is_cow_mapping(vma));
pfnmap_setup_cachemode_pfn(pfn, &pgprot);
@@ -1931,7 +1931,7 @@ int copy_huge_pmd(struct mm_struct *dst_mm, struct mm_struct *src_mm,
* applied special bit, or we made the PRIVATE mapping be
* able to wrongly write to the backend MMIO.
*/
VM_WARN_ON_ONCE(is_cow_mapping(src_vma->vm_flags) && pmd_write(pmd));
VM_WARN_ON_ONCE(vma_is_cow_mapping(src_vma) && pmd_write(pmd));
goto set_pmd;
}
@@ -2052,7 +2052,7 @@ int copy_huge_pud(struct mm_struct *dst_mm, struct mm_struct *src_mm,
* TODO: once we support anonymous pages, use
* folio_try_dup_anon_rmap_*() and split if duplicating fails.
*/
if (is_cow_mapping(vma->vm_flags) && pud_write(pud)) {
if (vma_is_cow_mapping(vma) && pud_write(pud)) {
pudp_set_wrprotect(src_mm, addr, src_pud);
pud = pud_wrprotect(pud);
}
@@ -2930,7 +2930,7 @@ int move_pages_huge_pmd(struct mm_struct *mm, pmd_t *dst_pmd, pmd_t *src_pmd, pm
}
folio_move_anon_rmap(src_folio, dst_vma);
src_folio->index = linear_page_index(dst_vma, dst_addr);
src_folio->index = linear_anon_page_index(dst_vma, dst_addr);
_dst_pmd = folio_mk_pmd(src_folio, dst_vma->vm_page_prot);
/* Follow mremap() behavior and treat the entry dirty after the move */
@@ -4105,34 +4105,42 @@ static int __folio_split(struct folio *folio, unsigned int new_order,
XA_STATE(xas, &folio->mapping->i_pages, folio->index);
struct folio *end_folio = folio_next(folio);
bool is_anon = folio_test_anon(folio);
struct mem_cgroup *memcg, *old_memcg;
struct address_space *mapping = NULL;
struct anon_vma *anon_vma = NULL;
int old_order = folio_order(folio);
struct folio *new_folio, *next;
int nr_shmem_dropped = 0;
enum ttu_flags ttu_flags = 0;
int ret;
pgoff_t end = 0;
int ret;
VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio);
VM_WARN_ON_ONCE_FOLIO(!folio_test_large(folio), folio);
if (folio != page_folio(split_at) || folio != page_folio(lock_at)) {
ret = -EINVAL;
goto out;
goto out_no_memcg;
}
if (new_order >= old_order) {
ret = -EINVAL;
goto out;
goto out_no_memcg;
}
ret = folio_check_splittable(folio, new_order, split_type);
if (ret) {
VM_WARN_ONCE(ret == -EINVAL, "Tried to split an unsplittable folio");
goto out;
goto out_no_memcg;
}
/*
* switch to folio's memcg as xarray node allocation can happen and
* needs to charge to it.
*/
memcg = get_mem_cgroup_from_folio(folio);
old_memcg = set_active_memcg(memcg);
if (is_anon) {
/*
* The caller does not necessarily hold an mmap_lock that would
@@ -4275,6 +4283,10 @@ static int __folio_split(struct folio *folio, unsigned int new_order,
if (mapping)
i_mmap_unlock_read(mapping);
out:
/* restore to caller's old_memcg */
set_active_memcg(old_memcg);
mem_cgroup_put(memcg);
out_no_memcg:
xas_destroy(&xas);
if (is_pmd_order(old_order))
count_vm_event(!ret ? THP_SPLIT_PAGE : THP_SPLIT_PAGE_FAILED);
@@ -5077,9 +5089,8 @@ int set_pmd_migration_entry(struct page_vma_mapped_walk *pvmw,
return 0;
}
void remove_migration_pmd(struct page_vma_mapped_walk *pvmw, struct page *new)
void remove_migration_pmd(struct page_vma_mapped_walk *pvmw, struct folio *folio)
{
struct folio *folio = page_folio(new);
struct vm_area_struct *vma = pvmw->vma;
struct mm_struct *mm = vma->vm_mm;
unsigned long address = pvmw->address;
@@ -5115,11 +5126,9 @@ void remove_migration_pmd(struct page_vma_mapped_walk *pvmw, struct page *new)
swp_entry_t entry;
if (pmd_write(pmde))
entry = make_writable_device_private_entry(
page_to_pfn(new));
entry = make_writable_device_private_entry(folio_pfn(folio));
else
entry = make_readable_device_private_entry(
page_to_pfn(new));
entry = make_readable_device_private_entry(folio_pfn(folio));
pmde = softleaf_to_pmd(entry);
if (pmd_swp_soft_dirty(*pvmw->pmd))
@@ -5134,11 +5143,12 @@ void remove_migration_pmd(struct page_vma_mapped_walk *pvmw, struct page *new)
if (!softleaf_is_migration_read(entry))
rmap_flags |= RMAP_EXCLUSIVE;
folio_add_anon_rmap_pmd(folio, new, vma, haddr, rmap_flags);
folio_add_anon_rmap_pmd(folio, &folio->page, vma, haddr, rmap_flags);
} else {
folio_add_file_rmap_pmd(folio, new, vma);
folio_add_file_rmap_pmd(folio, &folio->page, vma);
}
VM_BUG_ON(pmd_write(pmde) && folio_test_anon(folio) && !PageAnonExclusive(new));
VM_WARN_ON_ONCE(pmd_write(pmde) && folio_test_anon(folio) &&
!PageAnonExclusive(&folio->page));
set_pmd_at(mm, haddr, pvmw->pmd, pmde);
/* No need to invalidate - it was non-present before */

View File

@@ -4888,7 +4888,7 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
pte_t *src_pte, *dst_pte, entry;
struct folio *pte_folio;
unsigned long addr;
bool cow = is_cow_mapping(src_vma->vm_flags);
bool cow = vma_is_cow_mapping(src_vma);
struct hstate *h = hstate_vma(src_vma);
unsigned long sz = huge_page_size(h);
unsigned long npages = pages_per_huge_page(h);
@@ -5208,6 +5208,7 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
bool adjust_reservation;
unsigned long last_addr_mask;
i_mmap_assert_write_locked(vma->vm_file->f_mapping);
WARN_ON(!is_vm_hugetlb_page(vma));
BUG_ON(start & ~huge_page_mask(h));
BUG_ON(end & ~huge_page_mask(h));
@@ -5299,7 +5300,10 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
/*
* Restore the reservation for anonymous page, otherwise the
* backing page could be stolen by someone.
* backing page could be stolen by someone. Restore only on the
* last unmap, otherwise the owner could empty its resv map
* while the folio is still mapped by a child. Note that holding
* i_mmap_lock_write is needed to check the number of mappings.
* If there we are freeing a surplus, do not set the restore
* reservation bit.
*/
@@ -5307,7 +5311,7 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
spin_lock_irq(&hugetlb_lock);
if (!h->surplus_huge_pages && __vma_private_lock(vma) &&
folio_test_anon(folio)) {
!folio_mapped(folio) && folio_test_anon(folio)) {
folio_set_hugetlb_restore_reserve(folio);
/* Reservation to be adjusted after the spin lock */
adjust_reservation = true;

View File

@@ -9,6 +9,9 @@
#include <asm/setup.h>
#include <linux/hugetlb.h>
#include <linux/memblock.h>
#include <linux/math.h>
#include <linux/math64.h>
#include "internal.h"
#include "hugetlb_cma.h"
@@ -18,6 +21,28 @@ static unsigned long hugetlb_cma_size_in_node[MAX_NUMNODES] __initdata;
static bool hugetlb_cma_only __ro_after_init;
static unsigned long hugetlb_cma_size __ro_after_init;
static unsigned int hugetlb_cma_percent __initdata;
static unsigned int hugetlb_cma_percent_in_node[MAX_NUMNODES] __initdata;
#ifdef CONFIG_NUMA
static phys_addr_t __init memblock_node_memory_size(int nid)
{
struct memblock_region *reg;
phys_addr_t size = 0;
for_each_mem_region(reg) {
if (reg->nid == nid)
size += reg->size;
}
return size;
}
#else
static phys_addr_t __init memblock_node_memory_size(int nid)
{
return memblock_phys_mem_size();
}
#endif
void hugetlb_cma_free_frozen_folio(struct folio *folio)
{
WARN_ON_ONCE(!cma_release_frozen(hugetlb_cma[folio_nid(folio)],
@@ -90,14 +115,31 @@ static int __init cmdline_parse_hugetlb_cma(char *p)
break;
if (s[count] == ':') {
char *next;
if (tmp >= MAX_NUMNODES)
break;
nid = array_index_nospec(tmp, MAX_NUMNODES);
hugetlb_cma_size = 0;
hugetlb_cma_percent = 0;
s += count + 1;
tmp = memparse(s, &s);
hugetlb_cma_size_in_node[nid] = tmp;
hugetlb_cma_size += tmp;
tmp = memparse(s, &next);
if (*next == '%') {
if (tmp > 100) {
pr_warn("hugetlb_cma: invalid percentage %lu for node %d\n",
tmp, nid);
break;
}
hugetlb_cma_percent_in_node[nid] = tmp;
hugetlb_cma_size_in_node[nid] = 0;
s = next + 1;
} else {
hugetlb_cma_size_in_node[nid] = tmp;
hugetlb_cma_percent_in_node[nid] = 0;
s = next;
}
/*
* Skip the separator if have one, otherwise
@@ -108,7 +150,28 @@ static int __init cmdline_parse_hugetlb_cma(char *p)
else
break;
} else {
hugetlb_cma_size = memparse(p, &p);
char *next;
tmp = memparse(p, &next);
if (*next == '%') {
if (tmp > 100) {
pr_warn("hugetlb_cma: invalid percentage %lu\n", tmp);
} else {
hugetlb_cma_percent = tmp;
hugetlb_cma_size = 0;
for (nid = 0; nid < MAX_NUMNODES; nid++) {
hugetlb_cma_size_in_node[nid] = 0;
hugetlb_cma_percent_in_node[nid] = 0;
}
}
} else {
hugetlb_cma_size = tmp;
hugetlb_cma_percent = 0;
for (nid = 0; nid < MAX_NUMNODES; nid++) {
hugetlb_cma_size_in_node[nid] = 0;
hugetlb_cma_percent_in_node[nid] = 0;
}
}
break;
}
}
@@ -134,8 +197,36 @@ void __init hugetlb_cma_reserve(void)
{
unsigned long size, reserved, per_node, order, gigantic_page_size;
bool node_specific_cma_alloc = false;
bool has_node_specific_param = false;
int nid;
for (nid = 0; nid < MAX_NUMNODES; nid++) {
if (hugetlb_cma_size_in_node[nid] || hugetlb_cma_percent_in_node[nid]) {
has_node_specific_param = true;
break;
}
}
if (has_node_specific_param) {
hugetlb_cma_size = 0;
for (nid = 0; nid < MAX_NUMNODES; nid++) {
if (hugetlb_cma_percent_in_node[nid]) {
phys_addr_t node_gfp_mem = memblock_node_memory_size(nid);
u64 s;
s = mul_u64_u32_div((u64)node_gfp_mem,
hugetlb_cma_percent_in_node[nid],
100);
hugetlb_cma_size_in_node[nid] = s;
}
hugetlb_cma_size += hugetlb_cma_size_in_node[nid];
}
} else if (hugetlb_cma_percent) {
hugetlb_cma_size = mul_u64_u32_div((u64)memblock_phys_mem_size(),
hugetlb_cma_percent, 100);
}
if (!hugetlb_cma_size)
return;
@@ -154,6 +245,32 @@ void __init hugetlb_cma_reserve(void)
VM_WARN_ON(order <= MAX_PAGE_ORDER);
gigantic_page_size = PAGE_SIZE << order;
if (hugetlb_cma_percent) {
unsigned long orig_size = hugetlb_cma_size;
hugetlb_cma_size = ALIGN_DOWN(hugetlb_cma_size, PAGE_SIZE << order);
if (orig_size && !hugetlb_cma_size)
pr_warn("hugetlb_cma: reservation size rounded down to 0 from %lu MiB (%u%%)\n",
orig_size / SZ_1M, hugetlb_cma_percent);
} else if (has_node_specific_param) {
hugetlb_cma_size = 0;
for (nid = 0; nid < MAX_NUMNODES; nid++) {
if (hugetlb_cma_percent_in_node[nid]) {
unsigned long orig_size = hugetlb_cma_size_in_node[nid];
hugetlb_cma_size_in_node[nid] =
ALIGN_DOWN(hugetlb_cma_size_in_node[nid],
PAGE_SIZE << order);
if (orig_size && !hugetlb_cma_size_in_node[nid])
pr_warn("hugetlb_cma: reservation size rounded down to 0 from %lu MiB (%u%%) on node %d\n",
orig_size / SZ_1M,
hugetlb_cma_percent_in_node[nid],
nid);
}
hugetlb_cma_size += hugetlb_cma_size_in_node[nid];
}
}
hugetlb_bootmem_set_nodes();
for (nid = 0; nid < MAX_NUMNODES; nid++) {
@@ -194,8 +311,13 @@ void __init hugetlb_cma_reserve(void)
per_node = DIV_ROUND_UP(hugetlb_cma_size,
nodes_weight(hugetlb_bootmem_nodes));
per_node = round_up(per_node, gigantic_page_size);
pr_info("hugetlb_cma: reserve %lu MiB, up to %lu MiB per node\n",
hugetlb_cma_size / SZ_1M, per_node / SZ_1M);
if (hugetlb_cma_percent)
pr_info("hugetlb_cma: reserve %lu MiB (%u%%), up to %lu MiB per node\n",
hugetlb_cma_size / SZ_1M, hugetlb_cma_percent,
per_node / SZ_1M);
else
pr_info("hugetlb_cma: reserve %lu MiB, up to %lu MiB per node\n",
hugetlb_cma_size / SZ_1M, per_node / SZ_1M);
}
reserved = 0;
@@ -230,8 +352,12 @@ void __init hugetlb_cma_reserve(void)
}
reserved += size;
pr_info("hugetlb_cma: reserved %lu MiB on node %d\n",
size / SZ_1M, nid);
if (hugetlb_cma_percent_in_node[nid])
pr_info("hugetlb_cma: reserved %lu MiB (%u%%) on node %d\n",
size / SZ_1M, hugetlb_cma_percent_in_node[nid], nid);
else
pr_info("hugetlb_cma: reserved %lu MiB on node %d\n",
size / SZ_1M, nid);
if (reserved >= hugetlb_cma_size)
break;

View File

@@ -2,6 +2,8 @@
#ifndef _LINUX_HUGETLB_CMA_H
#define _LINUX_HUGETLB_CMA_H
#include <linux/hugetlb.h>
#ifdef CONFIG_CMA
void hugetlb_cma_free_frozen_folio(struct folio *folio);
struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask,

View File

@@ -41,12 +41,9 @@ void workingset_refault(struct folio *folio, void *shadow);
void workingset_activation(struct folio *folio);
/* mm/folio.c */
void lru_note_cost_unlock_irq(struct lruvec *lruvec, bool file,
unsigned int nr_io, unsigned int nr_rotated);
void lru_note_cost_refault(struct folio *folio);
void folio_add_lru_vma(struct folio *folio, struct vm_area_struct *vma);
static inline bool folio_may_be_lru_cached(struct folio *folio)
static inline bool folio_may_be_lru_cached(const struct folio *folio)
{
/*
* Holding PMD-sized folios in per-CPU LRU cache unbalances accounting.
@@ -945,7 +942,8 @@ folio_within_range(struct folio *folio, struct vm_area_struct *vma,
return false;
pgoff_folio = folio_pgoff(folio);
pgoff_vma_start = vma_start_pgoff(vma);
pgoff_vma_start = folio_test_anon(folio) ?
vma_start_anon_pgoff(vma) : vma_start_pgoff(vma);
if (start < vma->vm_start)
start = vma->vm_start;
@@ -1017,19 +1015,9 @@ void mlock_drain_remote(int cpu);
extern pmd_t maybe_pmd_mkwrite(pmd_t pmd, struct vm_area_struct *vma);
/**
* vma_address - Find the virtual address a page range is mapped at
* @vma: The vma which maps this object.
* @pgoff: The page offset within its object.
* @nr_pages: The number of pages to consider.
*
* If any page in this range is mapped by this VMA, return the first address
* where any of these pages appear. Otherwise, return -EFAULT.
*/
static inline unsigned long vma_address(const struct vm_area_struct *vma,
pgoff_t pgoff, unsigned long nr_pages)
static inline unsigned long __vma_address(const struct vm_area_struct *vma,
pgoff_t pgoff, pgoff_t pgoff_start, unsigned long nr_pages)
{
const pgoff_t pgoff_start = vma_start_pgoff(vma);
unsigned long address;
if (pgoff >= pgoff_start) {
@@ -1047,23 +1035,66 @@ static inline unsigned long vma_address(const struct vm_area_struct *vma,
return address;
}
/**
* vma_filebacked_address - Find the virtual address a file-backed page range is
* mapped at.
* @vma: The vma which maps this object.
* @pgoff: The page offset within its object.
* @nr_pages: The number of pages to consider.
*
* Returns: If any page in this range is mapped by this VMA, return the first
* address where any of these pages appear. Otherwise, return -EFAULT.
*/
static inline unsigned long vma_filebacked_address(const struct vm_area_struct *vma,
pgoff_t pgoff, unsigned long nr_pages)
{
VM_WARN_ON_ONCE(vma_is_anonymous(vma));
return __vma_address(vma, pgoff, vma_start_pgoff(vma), nr_pages);
}
/**
* vma_anon_address - Find the virtual address an anonymous page range is mapped
* at.
* @vma: The vma which maps this object.
* @pgoff_anon: The anonymous page index belonging to the folio.
* @nr_pages: The number of pages to consider.
*
* This is only valid for anonymous or MAP_PRIVATE-mapped file-backed VMAs.
*
* Returns: If any page in this range is mapped by this VMA, return the first
* address where any of these pages appear. Otherwise, return -EFAULT.
*/
static inline unsigned long vma_anon_address(const struct vm_area_struct *vma,
pgoff_t pgoff_anon, unsigned long nr_pages)
{
VM_WARN_ON_ONCE(!vma_is_cow_mapping(vma));
return __vma_address(vma, pgoff_anon, vma_start_anon_pgoff(vma), nr_pages);
}
/*
* Then at what user virtual address will none of the range be found in vma?
* At what user virtual address will none of the range be found in vma?
* Assumes that vma_address() already returned a good starting address.
*/
static inline unsigned long vma_address_end(struct page_vma_mapped_walk *pvmw)
{
struct vm_area_struct *vma = pvmw->vma;
pgoff_t pgoff;
const pgoff_t pgoff_end = pvmw->pgoff + pvmw->nr_pages;
const struct vm_area_struct *vma = pvmw->vma;
pgoff_t pgoff_vma_start;
unsigned long address;
/* Common case, plus ->pgoff is invalid for KSM */
if (pvmw->nr_pages == 1)
return pvmw->address + PAGE_SIZE;
pgoff = pvmw->pgoff + pvmw->nr_pages;
if (pvmw->pgoff_is_anon)
pgoff_vma_start = vma_start_anon_pgoff(vma);
else
pgoff_vma_start = vma_start_pgoff(vma);
address = vma->vm_start +
((pgoff - vma_start_pgoff(vma)) << PAGE_SHIFT);
((pgoff_end - pgoff_vma_start) << PAGE_SHIFT);
/* Check for address beyond vma (or wrapped through 0?) */
if (address < vma->vm_start || address > vma->vm_end)
address = vma->vm_end;
@@ -1353,7 +1384,7 @@ static inline bool gup_must_unshare(struct vm_area_struct *vma,
* ... because we only care about writable private ("COW")
* mappings where we have to break COW early.
*/
return is_cow_mapping(vma->vm_flags);
return vma_is_cow_mapping(vma);
}
/* Paired with a memory barrier in folio_try_share_anon_rmap_*(). */

View File

@@ -83,12 +83,12 @@ mapping_rmap_tree_iter_next(struct vm_area_struct *vma,
static pgoff_t avc_start_pgoff(struct anon_vma_chain *avc)
{
return vma_start_pgoff(avc->vma);
return vma_start_anon_pgoff(avc->vma);
}
static pgoff_t avc_last_pgoff(struct anon_vma_chain *avc)
{
return vma_last_pgoff(avc->vma);
return vma_last_anon_pgoff(avc->vma);
}
INTERVAL_TREE_DEFINE(struct anon_vma_chain, rb, pgoff_t, rb_subtree_last,

View File

@@ -355,7 +355,12 @@ void kasan_quarantine_remove_cache(struct kmem_cache *cache)
*/
on_each_cpu(per_cpu_remove_cache, cache, 1);
for_each_online_cpu(cpu) {
/*
* A CPU can go offline after on_each_cpu() returns, leaving cache
* objects on that CPU's shrink list. Scan all possible CPUs to
* drain those lists.
*/
for_each_possible_cpu(cpu) {
sq = per_cpu_ptr(&shrink_qlist, cpu);
raw_spin_lock_irqsave(&sq->lock, flags);
qlist_move_cache(&sq->qlist, &to_free, cache);
@@ -365,9 +370,14 @@ void kasan_quarantine_remove_cache(struct kmem_cache *cache)
raw_spin_lock_irqsave(&quarantine_lock, flags);
for (i = 0; i < QUARANTINE_BATCHES; i++) {
size_t old_bytes;
if (qlist_empty(&global_quarantine[i]))
continue;
old_bytes = global_quarantine[i].bytes;
qlist_move_cache(&global_quarantine[i], &to_free, cache);
WRITE_ONCE(quarantine_size, quarantine_size -
(old_bytes - global_quarantine[i].bytes));
/* Scanning whole quarantine can take a while. */
raw_spin_unlock_irqrestore(&quarantine_lock, flags);
cond_resched();

View File

@@ -620,7 +620,7 @@ void __khugepaged_exit(struct mm_struct *mm)
/*
* This is required to serialize against
* collapse_test_exit() (which is guaranteed to run
* under mmap sem read mode). Stop here (after we return all
* under mmap_lock read mode). Stop here (after we return all
* pagetables will be destroyed) until khugepaged has finished
* working on the pagetables under the mmap_lock.
*/
@@ -629,6 +629,13 @@ void __khugepaged_exit(struct mm_struct *mm)
}
}
static void collapse_control_init_scan(struct collapse_control *cc)
{
memset(cc->node_load, 0, sizeof(cc->node_load));
nodes_clear(cc->alloc_nmask);
bitmap_zero(cc->mthp_present_ptes, MAX_PTRS_PER_PTE);
}
static void release_pte_folio(struct folio *folio)
{
node_stat_mod_folio(folio,
@@ -665,6 +672,24 @@ static void release_pte_pages(pte_t *pte, pte_t *_pte,
}
}
static bool folio_pte_referenced(struct folio *folio,
struct vm_area_struct *vma, unsigned long addr, pte_t pteval)
{
/* The folio was referenced previously ... */
if (folio_test_young(folio) || folio_test_referenced(folio))
return true;
/* ... or the PTE mapping was recently used */
return pte_young(pteval) || mmu_notifier_test_young(vma->vm_mm, addr);
}
static void count_collapse_event(unsigned int order, enum vm_event_item vm_event,
enum mthp_stat_item mthp_event)
{
if (is_pmd_order(order))
count_vm_event(vm_event);
count_mthp_stat(order, mthp_event);
}
static enum scan_result __collapse_huge_page_isolate(struct vm_area_struct *vma,
unsigned long start_addr, pte_t *pte, struct collapse_control *cc,
unsigned int order, struct list_head *compound_pagelist)
@@ -685,9 +710,8 @@ static enum scan_result __collapse_huge_page_isolate(struct vm_area_struct *vma,
if (pte_none_or_zero(pteval)) {
if (++none_or_zero > max_ptes_none) {
result = SCAN_EXCEED_NONE_PTE;
if (is_pmd_order(order))
count_vm_event(THP_SCAN_EXCEED_NONE_PTE);
count_mthp_stat(order, MTHP_STAT_COLLAPSE_EXCEED_NONE);
count_collapse_event(order, THP_SCAN_EXCEED_NONE_PTE,
MTHP_STAT_COLLAPSE_EXCEED_NONE);
goto out;
}
continue;
@@ -729,9 +753,8 @@ static enum scan_result __collapse_huge_page_isolate(struct vm_area_struct *vma,
*/
if (++shared > max_ptes_shared) {
result = SCAN_EXCEED_SHARED_PTE;
if (is_pmd_order(order))
count_vm_event(THP_SCAN_EXCEED_SHARED_PTE);
count_mthp_stat(order, MTHP_STAT_COLLAPSE_EXCEED_SHARED);
count_collapse_event(order, THP_SCAN_EXCEED_SHARED_PTE,
MTHP_STAT_COLLAPSE_EXCEED_SHARED);
goto out;
}
}
@@ -759,8 +782,8 @@ static enum scan_result __collapse_huge_page_isolate(struct vm_area_struct *vma,
/*
* We can do it before folio_isolate_lru because the
* folio can't be freed from under us. NOTE: PG_lock
* is needed to serialize against split_huge_page
* folio can't be freed from under us. NOTE: folio lock
* is needed to serialize against split_huge_page()
* when invoked from the VM.
*/
if (!folio_trylock(folio)) {
@@ -786,7 +809,7 @@ static enum scan_result __collapse_huge_page_isolate(struct vm_area_struct *vma,
}
/*
* Isolate the page to avoid collapsing an hugepage
* Isolate the folio to avoid collapsing a hugepage
* currently in use by the VM.
*/
if (!folio_isolate_lru(folio)) {
@@ -803,14 +826,8 @@ static enum scan_result __collapse_huge_page_isolate(struct vm_area_struct *vma,
if (folio_test_large(folio))
list_add_tail(&folio->lru, compound_pagelist);
next:
/*
* If collapse was initiated by khugepaged, check that there is
* enough young pte to justify collapsing the page
*/
if (cc->is_khugepaged &&
(pte_young(pteval) || folio_test_young(folio) ||
folio_test_referenced(folio) ||
mmu_notifier_test_young(vma->vm_mm, addr)))
folio_pte_referenced(folio, vma, addr, pteval))
referenced++;
}
@@ -904,7 +921,7 @@ static void __collapse_huge_page_copy_failed(pte_t *pte,
* Re-establish the PMD to point to the original page table
* entry. Restoring PMD needs to be done prior to releasing
* pages. Since pages are still isolated and locked here,
* acquiring anon_vma_lock_write is unnecessary.
* acquiring anon_vma_lock_write() is unnecessary.
*/
pmd_ptl = pmd_lock(vma->vm_mm, pmd);
pmd_populate(vma->vm_mm, pmd, pmd_pgtable(orig_pmd));
@@ -1078,9 +1095,9 @@ static enum scan_result hugepage_vma_revalidate(struct mm_struct *mm, unsigned l
return SCAN_VMA_CHECK;
/*
* Anon VMA expected, the address may be unmapped then
* remapped to file after khugepaged reaquired the mmap_lock.
* remapped to file after khugepaged reacquired the mmap_lock.
*
* thp_vma_allowable_orders may return true for qualified file
* thp_vma_allowable_orders() may return true for qualified file
* vmas.
*/
if (expect_anon && (!(*vmap)->anon_vma || !vma_is_anonymous(*vmap)))
@@ -1136,7 +1153,7 @@ static enum scan_result check_pmd_still_valid(struct mm_struct *mm,
/*
* Bring missing pages in from swap, to complete THP collapse.
* Only done if khugepaged_scan_pmd believes it is worthwhile.
* Only done if collapse_scan_pmd() believes it is worthwhile.
*
* For mTHP orders the function bails on the first swap entry, because
* faulting pages back in during collapse could re-populate PTEs that
@@ -1204,7 +1221,7 @@ static enum scan_result __collapse_huge_page_swapin(struct mm_struct *mm,
pte = NULL;
/*
* do_swap_page returns VM_FAULT_RETRY with released mmap_lock.
* do_swap_page() returns VM_FAULT_RETRY with released mmap_lock.
* Note we treat VM_FAULT_RETRY as VM_FAULT_ERROR here because
* we do not retry here and swap entry will remain in pagetable
* resulting in later failure.
@@ -1247,15 +1264,12 @@ static enum scan_result alloc_charge_folio(struct folio **foliop, struct mm_stru
folio = __folio_alloc(gfp, order, node, &cc->alloc_nmask);
if (!folio) {
*foliop = NULL;
if (is_pmd_order(order))
count_vm_event(THP_COLLAPSE_ALLOC_FAILED);
count_mthp_stat(order, MTHP_STAT_COLLAPSE_ALLOC_FAILED);
count_collapse_event(order, THP_COLLAPSE_ALLOC_FAILED,
MTHP_STAT_COLLAPSE_ALLOC_FAILED);
return SCAN_ALLOC_HUGE_PAGE_FAIL;
}
if (is_pmd_order(order))
count_vm_event(THP_COLLAPSE_ALLOC);
count_mthp_stat(order, MTHP_STAT_COLLAPSE_ALLOC);
count_collapse_event(order, THP_COLLAPSE_ALLOC, MTHP_STAT_COLLAPSE_ALLOC);
if (unlikely(mem_cgroup_charge(folio, mm, gfp))) {
folio_put(folio);
@@ -1271,7 +1285,7 @@ static enum scan_result alloc_charge_folio(struct folio **foliop, struct mm_stru
}
/*
* collapse_huge_page expects the mmap_lock to be unlocked before entering and
* collapse_huge_page() expects the mmap_lock to be unlocked before entering and
* will always return with the lock unlocked, to avoid holding the mmap_lock
* while allocating a THP, as that could trigger direct reclaim/compaction.
* Note that the VMA must be rechecked after grabbing the mmap_lock again.
@@ -1318,7 +1332,7 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long s
if (unmapped) {
/*
* __collapse_huge_page_swapin will return with mmap_lock
* __collapse_huge_page_swapin() will return with mmap_lock
* released when it fails. So we jump out_nolock directly in
* that case. Continuing to collapse causes inconsistency.
*/
@@ -1331,8 +1345,8 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long s
mmap_read_unlock(mm);
/*
* Prevent all access to pagetables with the exception of
* gup_fast later handled by the ptep_clear_flush and the VM
* handled by the anon_vma lock + PG_lock.
* gup_fast later handled by the pmdp_collapse_flush() and the VM
* handled by the anon_vma lock + folio lock.
*
* UFFDIO_MOVE is prevented to race as well thanks to the
* mmap_lock.
@@ -1389,9 +1403,9 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long s
spin_lock(pmd_ptl);
VM_WARN_ON_ONCE(!pmd_none(*pmd));
/*
* We can only use set_pmd_at when establishing
* We can only use set_pmd_at() when establishing
* hugepmds and never for establishing regular pmds that
* points to regular pagetables. Use pmd_populate for that
* points to regular pagetables. Use pmd_populate() for that
*/
pmd_populate(mm, pmd, pmd_pgtable(_pmd));
spin_unlock(pmd_ptl);
@@ -1449,10 +1463,10 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long s
result = SCAN_SUCCEED;
out_up_write:
if (anon_vma_locked)
anon_vma_unlock_write(vma->anon_vma);
if (pte)
pte_unmap(pte);
if (anon_vma_locked)
anon_vma_unlock_write(vma->anon_vma);
mmap_write_unlock(mm);
out_nolock:
if (folio)
@@ -1617,15 +1631,14 @@ static enum scan_result collapse_scan_pmd(struct mm_struct *mm,
goto out;
}
bitmap_zero(cc->mthp_present_ptes, MAX_PTRS_PER_PTE);
memset(cc->node_load, 0, sizeof(cc->node_load));
nodes_clear(cc->alloc_nmask);
collapse_control_init_scan(cc);
enabled_orders = collapse_possible_orders(vma, vma->vm_flags, tva_flags);
/*
* If PMD is the only enabled order, enforce max_ptes_none, otherwise
* scan all pages to populate the bitmap for mTHP collapse.
* scan all pages to populate the bitmap for mTHP collapse. The bitmap
* is then checked again in mthp_collapse() for each attempted order.
*/
if (enabled_orders != BIT(HPAGE_PMD_ORDER))
max_ptes_none = KHUGEPAGED_MAX_PTES_LIMIT;
@@ -1647,9 +1660,8 @@ static enum scan_result collapse_scan_pmd(struct mm_struct *mm,
if (pte_none_or_zero(pteval)) {
if (++none_or_zero > max_ptes_none) {
result = SCAN_EXCEED_NONE_PTE;
count_vm_event(THP_SCAN_EXCEED_NONE_PTE);
count_mthp_stat(HPAGE_PMD_ORDER,
MTHP_STAT_COLLAPSE_EXCEED_NONE);
count_collapse_event(HPAGE_PMD_ORDER, THP_SCAN_EXCEED_NONE_PTE,
MTHP_STAT_COLLAPSE_EXCEED_NONE);
goto out_unmap;
}
continue;
@@ -1657,9 +1669,8 @@ static enum scan_result collapse_scan_pmd(struct mm_struct *mm,
if (!pte_present(pteval)) {
if (++unmapped > max_ptes_swap) {
result = SCAN_EXCEED_SWAP_PTE;
count_vm_event(THP_SCAN_EXCEED_SWAP_PTE);
count_mthp_stat(HPAGE_PMD_ORDER,
MTHP_STAT_COLLAPSE_EXCEED_SWAP);
count_collapse_event(HPAGE_PMD_ORDER, THP_SCAN_EXCEED_SWAP_PTE,
MTHP_STAT_COLLAPSE_EXCEED_SWAP);
goto out_unmap;
}
/*
@@ -1716,9 +1727,8 @@ static enum scan_result collapse_scan_pmd(struct mm_struct *mm,
if (folio_maybe_mapped_shared(folio)) {
if (++shared > max_ptes_shared) {
result = SCAN_EXCEED_SHARED_PTE;
count_vm_event(THP_SCAN_EXCEED_SHARED_PTE);
count_mthp_stat(HPAGE_PMD_ORDER,
MTHP_STAT_COLLAPSE_EXCEED_SHARED);
count_collapse_event(HPAGE_PMD_ORDER, THP_SCAN_EXCEED_SHARED_PTE,
MTHP_STAT_COLLAPSE_EXCEED_SHARED);
goto out_unmap;
}
}
@@ -1749,26 +1759,17 @@ static enum scan_result collapse_scan_pmd(struct mm_struct *mm,
/*
* Check if the page has any GUP (or other external) pins.
*
* Here the check may be racy:
* it may see folio_mapcount() > folio_ref_count().
* But such case is ephemeral we could always retry collapse
* later. However it may report false positive if the page
* has excessive GUP pins (i.e. 512). Anyway the same check
* will be done again later the risk seems low.
* Here the check is racy, but such cases are ephemeral and
* we can always retry collapse later. Anyway the same
* check will be done again later, so the risk seems to be low.
*/
if (folio_expected_ref_count(folio) != folio_ref_count(folio)) {
result = SCAN_PAGE_COUNT;
goto out_unmap;
}
/*
* If collapse was initiated by khugepaged, check that there is
* enough young pte to justify collapsing the page
*/
if (cc->is_khugepaged &&
(pte_young(pteval) || folio_test_young(folio) ||
folio_test_referenced(folio) ||
mmu_notifier_test_young(vma->vm_mm, addr)))
folio_pte_referenced(folio, vma, addr, pteval))
referenced++;
}
if (cc->is_khugepaged &&
@@ -1781,7 +1782,7 @@ static enum scan_result collapse_scan_pmd(struct mm_struct *mm,
out_unmap:
pte_unmap_unlock(pte, ptl);
if (result == SCAN_SUCCEED) {
/* collapse_huge_page expects the lock to be dropped before calling */
/* collapse_huge_page() expects the lock to be dropped before calling */
mmap_read_unlock(mm);
result = mthp_collapse(mm, start_addr, referenced,
unmapped, cc, enabled_orders);
@@ -2691,8 +2692,7 @@ static enum scan_result collapse_scan_file(struct mm_struct *mm,
present = 0;
swap = 0;
memset(cc->node_load, 0, sizeof(cc->node_load));
nodes_clear(cc->alloc_nmask);
collapse_control_init_scan(cc);
rcu_read_lock();
xas_for_each(&xas, folio, start + HPAGE_PMD_NR - 1) {
if (xas_retry(&xas, folio))

View File

@@ -151,6 +151,8 @@ struct kmemleak_object {
int min_count;
/* the total number of pointers found pointing to this object */
int count;
/* consecutive scans the object has been seen unreferenced */
unsigned int unref_scans;
/* checksum for detecting modified objects */
u32 checksum;
depot_stack_handle_t trace_handle;
@@ -175,6 +177,8 @@ struct kmemleak_object {
#define OBJECT_PHYS (1 << 4)
/* flag set for per-CPU pointers */
#define OBJECT_PERCPU (1 << 5)
/* flag set on an object left unreferenced by the full scan, pending confirmation */
#define OBJECT_SUSPECT (1 << 6)
/* set when __remove_object() called */
#define DELSTATE_REMOVED (1 << 0)
@@ -232,9 +236,15 @@ static unsigned long max_percpu_addr;
static struct task_struct *scan_thread;
/* used to avoid reporting of recently allocated objects */
static unsigned long jiffies_min_age;
/* consecutive scans an object must stay unreferenced before reporting */
static unsigned int min_unref_scans =
IS_ENABLED(CONFIG_DEBUG_KMEMLEAK_VERBOSE) ? 2 : 1;
module_param(min_unref_scans, uint, 0644);
static unsigned long jiffies_last_scan;
/* delay between automatic memory scannings */
static unsigned long jiffies_scan_wait;
/* number of objects flagged OBJECT_SUSPECT during the current scan */
static int nr_suspects;
/* enables or disables the task stacks scanning */
static int kmemleak_stack_scan = 1;
/* protects the memory scanning, parameters and debug/kmemleak file access */
@@ -688,6 +698,7 @@ static struct kmemleak_object *__alloc_object(gfp_t gfp)
object->excess_ref = 0;
object->count = 0; /* white color initially */
object->checksum = ~0;
object->unref_scans = 0;
object->del_state = 0;
/* task information */
@@ -1440,6 +1451,11 @@ static void update_refs(struct kmemleak_object *object)
*/
object->count++;
if (color_gray(object)) {
/* referenced after all, no longer a suspect */
if (object->flags & OBJECT_SUSPECT) {
object->flags &= ~OBJECT_SUSPECT;
nr_suspects--;
}
/* put_object() called when removing from gray_list */
WARN_ON(!get_object(object));
list_add_tail(&object->gray_list, &gray_list);
@@ -1571,7 +1587,7 @@ static int scan_large_block(void *start, void *end)
if (scan_block(start, next, NULL))
return 1;
start = next;
cond_resched();
cond_resched_tasks_rcu_qs();
}
return 0;
@@ -1608,7 +1624,7 @@ static void scan_object(struct kmemleak_object *object)
scan_block(start, end, object);
raw_spin_unlock_irqrestore(&object->lock, flags);
cond_resched();
cond_resched_tasks_rcu_qs();
raw_spin_lock_irqsave(&object->lock, flags);
if (!(object->flags & OBJECT_ALLOCATED))
break;
@@ -1630,7 +1646,7 @@ static void scan_object(struct kmemleak_object *object)
break;
raw_spin_unlock_irqrestore(&object->lock, flags);
cond_resched();
cond_resched_tasks_rcu_qs();
raw_spin_lock_irqsave(&object->lock, flags);
} while (object->flags & OBJECT_ALLOCATED);
} else {
@@ -1658,7 +1674,7 @@ static void scan_gray_list(void)
*/
object = list_entry(gray_list.next, typeof(*object), gray_list);
while (&object->gray_list != &gray_list) {
cond_resched();
cond_resched_tasks_rcu_qs();
/* may add new objects to the list */
if (!scan_should_stop())
@@ -1693,7 +1709,7 @@ static void kmemleak_cond_resched(struct kmemleak_object *object)
raw_spin_unlock_irq(&kmemleak_lock);
rcu_read_unlock();
cond_resched();
cond_resched_tasks_rcu_qs();
rcu_read_lock();
raw_spin_lock_irq(&kmemleak_lock);
@@ -1738,7 +1754,7 @@ static void kmemleak_scan_task_stacks(void)
}
put_task_struct(p);
}
cond_resched();
cond_resched_tasks_rcu_qs();
} while (pid && !stop);
}
@@ -1844,16 +1860,16 @@ static void dedup_flush(struct xarray *dedup)
* kernel's standard allocators. This function must be called with the
* scan_mutex held.
*/
static void kmemleak_scan(void)
static int __kmemleak_scan(bool full)
{
struct kmemleak_object *object;
struct zone *zone;
int __maybe_unused i;
struct xarray dedup;
int new_leaks = 0;
int stop = 0;
jiffies_last_scan = jiffies;
if (full)
nr_suspects = 0;
/* prepare the kmemleak_object's */
rcu_read_lock();
@@ -1881,8 +1897,13 @@ static void kmemleak_scan(void)
__paint_it(object, KMEMLEAK_BLACK);
}
/* referenced last scan: restart the unreferenced run */
if (!color_white(object))
object->unref_scans = 0;
/* reset the reference count (whiten the object) */
object->count = 0;
if (full)
object->flags &= ~OBJECT_SUSPECT;
if (color_gray(object) && get_object(object))
list_add_tail(&object->gray_list, &gray_list);
@@ -1915,7 +1936,7 @@ static void kmemleak_scan(void)
struct page *page = pfn_to_online_page(pfn);
if (!(pfn & 63))
cond_resched();
cond_resched_tasks_rcu_qs();
if (!page)
continue;
@@ -1950,6 +1971,10 @@ static void kmemleak_scan(void)
scan_gray:
scan_gray_list();
/* a confirmation scan does not look for modified objects */
if (!full)
return nr_suspects;
/*
* Check for new or unreferenced objects modified since the previous
* scan and color them gray until the next scan.
@@ -1972,6 +1997,11 @@ static void kmemleak_scan(void)
/* color it gray temporarily */
object->count = object->min_count;
list_add_tail(&object->gray_list, &gray_list);
} else if (unreferenced_object(object) &&
!(object->flags & OBJECT_REPORTED)) {
/* flag the objects left unreferenced by this scan */
object->flags |= OBJECT_SUSPECT;
nr_suspects++;
}
raw_spin_unlock_irq(&object->lock);
}
@@ -1982,12 +2012,62 @@ static void kmemleak_scan(void)
*/
scan_gray_list();
return nr_suspects;
}
/*
* Promote a suspected object to a reported leak once it has stayed
* unreferenced for min_unref_scans consecutive scans. Called with
* object->lock held; returns true when the object is newly reported.
*/
static bool confirm_leak(struct kmemleak_object *object)
{
if (!unreferenced_object(object) ||
!(object->flags & OBJECT_SUSPECT) ||
(object->flags & OBJECT_REPORTED))
return false;
object->unref_scans += 1;
if (object->unref_scans < min_unref_scans)
return false;
object->flags |= OBJECT_REPORTED;
return true;
}
/*
* Scan the memory and report the unreferenced objects as leaks. Must be
* called with the scan_mutex held.
*/
static void kmemleak_scan(void)
{
struct kmemleak_object *object;
struct xarray dedup;
int new_leaks = 0;
/*
* Full scan. Objects left unreferenced are flagged OBJECT_SUSPECT and
* counted in the return value; nothing to confirm or report otherwise.
*/
if (!__kmemleak_scan(true))
return;
/*
* If scanning was stopped do not report any new unreferenced objects.
*/
if (scan_should_stop())
return;
/*
* A live object whose only reference is moved by, for example, a
* concurrent RCU update can be missed for one scan and reported as a
* transient false positive. Scan again and only report the objects
* left unreferenced (still flagged OBJECT_SUSPECT) by both scans.
*/
__kmemleak_scan(false);
if (scan_should_stop())
return;
/*
* Scanning result reporting. When verbose printing is enabled, dedupe
* by stackdepot trace_handle so each unique backtrace is logged once
@@ -2014,9 +2094,8 @@ static void kmemleak_scan(void)
raw_spin_lock_irq(&object->lock);
trace_handle = 0;
dedup_print = false;
if (unreferenced_object(object) &&
!(object->flags & OBJECT_REPORTED)) {
object->flags |= OBJECT_REPORTED;
if (confirm_leak(object)) {
if (kmemleak_verbose) {
trace_handle = object->trace_handle;
dedup_print = true;

View File

@@ -959,10 +959,9 @@ enum ksm_get_folio_flags {
* seconds or even minutes: much too unresponsive. So instead we use a
* "keyhole reference": access to the ksm page from the stable node peeps
* out through its keyhole to see if that page still holds the right key,
* pointing back to this stable node. This relies on freeing a PageAnon
* page to reset its page->mapping to NULL, and relies on no other use of
* a page to put something that might look like our key in page->mapping.
* is on its way to being freed; but it is an anomaly to bear in mind.
* pointing back to this stable node. This relies on freeing an anon
* folio to reset its mapping to NULL, and relies on no other use of a
* folio to put something that might look like our key in its mapping.
*/
static struct folio *ksm_get_folio(struct ksm_stable_node *stable_node,
enum ksm_get_folio_flags flags)
@@ -1625,7 +1624,7 @@ static int try_to_merge_with_ksm_page(struct ksm_rmap_item *rmap_item,
* stable_tree, break_cow() will clean it up.
*/
rmap_item->anon_vma = vma->anon_vma;
rmap_item->linear_page_index = linear_page_index(vma, rmap_item->address);
rmap_item->linear_page_index = linear_anon_page_index(vma, rmap_item->address);
get_anon_vma(vma->anon_vma);
out:
mmap_read_unlock(mm);
@@ -3060,10 +3059,9 @@ int __ksm_enter(struct mm_struct *mm)
slot = &mm_slot->slot;
spin_lock(&ksm_mmlist_lock);
/* Check ksm_run too? Would need tighter locking */
needs_wakeup = list_empty(&ksm_mm_head.slot.mm_node);
spin_lock(&ksm_mmlist_lock);
mm_slot_insert(mm_slots_hash, mm, slot);
/*
* When KSM_RUN_MERGE (or KSM_RUN_STOP),
@@ -3152,7 +3150,7 @@ struct folio *ksm_might_need_to_copy(struct folio *folio,
return folio; /* no need to copy it */
} else if (!anon_vma) {
return folio; /* no need to copy it */
} else if (folio->index == linear_page_index(vma, addr) &&
} else if (folio->index == linear_anon_page_index(vma, addr) &&
anon_vma->root == vma->anon_vma->root) {
return folio; /* still no need to copy it */
}
@@ -3222,7 +3220,7 @@ void rmap_walk_ksm(struct folio *folio, struct rmap_walk_control *rwc)
/*
* Currently, KSM folios are always small folios, so it's
* sufficient to search for a single page. We can simply use
* the linear_page_index of the original de-duplicate
* the linear_anon_page_index of the original de-duplicate
* anonymous page that we remembered in the rmap_item while
* de-duplicating. Note that mremap() always de-duplicates KSM
* folios: so if there was mremap() in our parent or our child,

View File

@@ -32,6 +32,7 @@
#include <linux/leafops.h>
#include <linux/shmem_fs.h>
#include <linux/mmu_notifier.h>
#include <linux/swap_ops.h>
#include <asm/tlb.h>
@@ -188,7 +189,7 @@ static int swapin_walk_pmd_entry(pmd_t *pmd, unsigned long start,
unsigned long end, struct mm_walk *walk)
{
struct vm_area_struct *vma = walk->private;
struct swap_iocb *splug = NULL;
struct swap_io_ctx ctx = {};
pte_t *ptep = NULL;
spinlock_t *ptl;
unsigned long addr;
@@ -212,15 +213,15 @@ static int swapin_walk_pmd_entry(pmd_t *pmd, unsigned long start,
pte_unmap_unlock(ptep, ptl);
ptep = NULL;
folio = read_swap_cache_async(entry, GFP_HIGHUSER_MOVABLE,
vma, addr, &splug);
folio = read_swap_cache_async(&ctx, entry, GFP_HIGHUSER_MOVABLE,
vma, addr);
if (folio)
folio_put(folio);
}
if (ptep)
pte_unmap_unlock(ptep, ptl);
swap_read_unplug(splug);
swap_read_submit(&ctx);
cond_resched();
return 0;
@@ -238,7 +239,7 @@ static void shmem_swapin_range(struct vm_area_struct *vma,
XA_STATE(xas, &mapping->i_pages, linear_page_index(vma, start));
pgoff_t end_index = linear_page_index(vma, end) - 1;
struct folio *folio;
struct swap_iocb *splug = NULL;
struct swap_io_ctx ctx = {};
rcu_read_lock();
xas_for_each(&xas, folio, end_index) {
@@ -257,15 +258,15 @@ static void shmem_swapin_range(struct vm_area_struct *vma,
xas_pause(&xas);
rcu_read_unlock();
folio = read_swap_cache_async(entry, mapping_gfp_mask(mapping),
vma, addr, &splug);
folio = read_swap_cache_async(&ctx, entry,
mapping_gfp_mask(mapping), vma, addr);
if (folio)
folio_put(folio);
rcu_read_lock();
}
rcu_read_unlock();
swap_read_unplug(splug);
swap_read_submit(&ctx);
}
#endif /* CONFIG_SWAP */

View File

@@ -2287,8 +2287,8 @@ void memcg1_stat_format(struct mem_cgroup *memcg, struct seq_buf *s)
for_each_online_pgdat(pgdat) {
mz = memcg->nodeinfo[pgdat->node_id];
anon_cost += mz->lruvec.anon_cost;
file_cost += mz->lruvec.file_cost;
anon_cost += mz->lruvec.cost[WORKINGSET_ANON].count;
file_cost += mz->lruvec.cost[WORKINGSET_FILE].count;
}
seq_buf_printf(s, "anon_cost %lu\n", anon_cost);
seq_buf_printf(s, "file_cost %lu\n", file_cost);

View File

@@ -4,6 +4,7 @@
#define __MM_MEMCONTROL_V1_H
#include <linux/cgroup-defs.h>
#include <linux/memcontrol.h>
/* Cgroup v1 and v2 common declarations */

View File

@@ -63,6 +63,7 @@
#include <linux/sched/isolation.h>
#include <linux/kmemleak.h>
#include "internal.h"
#include "swap.h"
#include "swap_table.h"
#include <net/sock.h>
#include <net/ip.h>
@@ -398,6 +399,7 @@ static const unsigned int memcg_node_stat_items[] = {
NR_SHMEM_THPS,
NR_FILE_THPS,
NR_ANON_THPS,
NR_VMSCAN_WRITE,
NR_VMALLOC,
NR_KERNEL_STACK_KB,
NR_PAGETABLE,
@@ -424,6 +426,8 @@ static const unsigned int memcg_node_stat_items[] = {
PGSCAN_PROACTIVE,
PGSCAN_ANON,
PGSCAN_FILE,
PGROTATE_ANON,
PGROTATE_FILE,
PGREFILL,
#ifdef CONFIG_HUGETLB_PAGE
NR_HUGETLB,
@@ -507,6 +511,42 @@ unsigned long lruvec_page_state(struct lruvec *lruvec, enum node_stat_item idx)
return x;
}
/**
* lruvec_page_state_monotonic - non-clamping lruvec stat read for delta sampling
* @lruvec: the LRU vector to read from
* @idx: the node_stat_item to read
*
* Returns the raw state[idx] value cast to unsigned long, skipping the
* clamp-negative-to-zero step in lruvec_page_state(). Intended for callers
* that snapshot a monotonically-incremented counter and subtract two
* samples: unsigned modular arithmetic then yields the correct delta across
* a signed-long wraparound (a real hazard on 32-bit) that the clamp would
* otherwise turn into a huge spurious delta.
*
* Do NOT use for non-monotonic page-count reads where a transient negative
* reading from per-CPU delta skew must present as zero.
*
* XXX: This helper (and its node/global peers) exists because some
* monotonically-incremented event counters are stored in
* enum node_stat_item.
*/
unsigned long lruvec_page_state_monotonic(struct lruvec *lruvec,
enum node_stat_item idx)
{
struct mem_cgroup_per_node *pn;
int i;
if (mem_cgroup_disabled())
return node_page_state_monotonic(lruvec_pgdat(lruvec), idx);
i = memcg_stats_index(idx);
if (WARN_ONCE(BAD_STAT_IDX(i), "%s: missing stat item %d\n", __func__, idx))
return 0;
pn = container_of(lruvec, struct mem_cgroup_per_node, lruvec);
return (unsigned long)READ_ONCE(pn->lruvec_stats->state[i]);
}
unsigned long lruvec_page_state_local(struct lruvec *lruvec,
enum node_stat_item idx)
{
@@ -2100,7 +2140,12 @@ static bool consume_stock(struct mem_cgroup *memcg, unsigned int nr_pages)
stock_pages = READ_ONCE(stock->nr_pages[i]);
if (stock_pages >= nr_pages) {
WRITE_ONCE(stock->nr_pages[i], stock_pages - nr_pages);
stock_pages -= nr_pages;
WRITE_ONCE(stock->nr_pages[i], stock_pages);
if (!stock_pages) {
css_put(&memcg->css);
WRITE_ONCE(stock->cached[i], NULL);
}
ret = true;
}
break;
@@ -2653,6 +2698,19 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,
if (!gfpflags_allow_blocking(gfp_mask))
goto nomem;
/*
* OOM victim still needs to charge memory to exit. OOM reaper should
* help but it might fail on mmap_lock contention. If the victim is a
* large thread group then all exiting threads might compete on oom_lock
* just to learn that there is nothing really killable anymore. Bail
* out early and fail the charge to expedite their exit. They are
* considered fully reclaimed by the oom reaper and they shouldn't
* contribute further charges.
*/
if (tsk_is_oom_victim(current) &&
mm_flags_test(MMF_OOM_SKIP, current->signal->oom_mm))
goto nomem;
__memcg_memory_event(mem_over_limit, MEMCG_MAX, allow_spinning);
raised_max_event = true;
@@ -4177,11 +4235,10 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
#endif
page_counter_set_high(&memcg->swap, PAGE_COUNTER_MAX);
if (parent) {
WRITE_ONCE(memcg->swappiness, mem_cgroup_swappiness(parent));
page_counter_init(&memcg->memory, &parent->memory, memcg_on_dfl);
page_counter_init(&memcg->swap, &parent->swap, false);
#ifdef CONFIG_MEMCG_V1
WRITE_ONCE(memcg->swappiness, mem_cgroup_swappiness(parent));
memcg->memory.track_failcnt = !memcg_on_dfl;
memcg->memsw.track_failcnt = !memcg_on_dfl;
WRITE_ONCE(memcg->oom_kill_disable, READ_ONCE(parent->oom_kill_disable));
@@ -4801,6 +4858,9 @@ static ssize_t memory_high_write(struct kernfs_open_file *of,
unsigned long nr_pages = page_counter_read(&memcg->memory);
unsigned long reclaimed;
if (high != READ_ONCE(memcg->memory.high))
break;
if (nr_pages <= high)
break;
@@ -4856,6 +4916,9 @@ static ssize_t memory_max_write(struct kernfs_open_file *of,
for (;;) {
unsigned long nr_pages = page_counter_read(&memcg->memory);
if (max != READ_ONCE(memcg->memory.max))
break;
if (nr_pages <= max)
break;

View File

@@ -620,7 +620,7 @@ static void add_to_kill_fsdax(struct task_struct *tsk, const struct page *p,
struct vm_area_struct *vma,
struct list_head *to_kill, pgoff_t pgoff)
{
unsigned long addr = vma_address(vma, pgoff, 1);
unsigned long addr = vma_filebacked_address(vma, pgoff, 1);
__add_to_kill(tsk, p, vma, to_kill, addr);
}
@@ -2265,7 +2265,7 @@ static void add_to_kill_pgoff(struct task_struct *tsk,
}
/* Check for pgoff not backed by struct page */
tk->addr = vma_address(vma, pgoff, 1);
tk->addr = vma_filebacked_address(vma, pgoff, 1);
tk->size_shift = PAGE_SHIFT;
if (tk->addr == -EFAULT)

View File

@@ -519,9 +519,52 @@ static bool is_bad_page_map_ratelimited(void)
return false;
}
static void ptval_bytes_to_hex_str(char *buf, size_t buf_size, const void *entry, size_t entry_size)
{
if (WARN_ON_ONCE(buf_size < entry_size * 2 + 1)) {
snprintf(buf, buf_size, "overflow");
return;
}
switch (entry_size) {
case sizeof(u32):
snprintf(buf, buf_size, "%08x", *(const u32 *)entry);
break;
case sizeof(u64):
snprintf(buf, buf_size, "%016llx", *(const u64 *)entry);
break;
#if defined(__SIZEOF_INT128__)
case sizeof(u128):
snprintf(buf, buf_size, "%016llx%016llx",
(unsigned long long)(*(const u128 *)entry >> 64),
(unsigned long long)*(const u128 *)entry);
break;
#endif
default:
snprintf(buf, buf_size, "unsupported");
break;
}
}
#define ptval_to_str(buf, val) \
do { \
auto __val = (val); \
\
ptval_bytes_to_hex_str((buf), sizeof(buf), &__val, sizeof(__val)); \
} while (0)
#if defined(__SIZEOF_INT128__)
#define PTVAL_STR_MAX (32 + 1) /* Max 128-bit value in hex + NUL */
#else
#define PTVAL_STR_MAX (16 + 1) /* Max 64-bit value in hex + NUL */
#endif
static void __print_bad_page_map_pgtable(struct mm_struct *mm, unsigned long addr)
{
unsigned long long pgdv, p4dv, pudv, pmdv;
char pgd_str[PTVAL_STR_MAX];
char p4d_str[PTVAL_STR_MAX];
char pud_str[PTVAL_STR_MAX];
char pmd_str[PTVAL_STR_MAX];
p4d_t p4d, *p4dp;
pud_t pud, *pudp;
pmd_t pmd, *pmdp;
@@ -532,34 +575,34 @@ static void __print_bad_page_map_pgtable(struct mm_struct *mm, unsigned long add
* see locking requirements for print_bad_page_map().
*/
pgdp = pgd_offset(mm, addr);
pgdv = pgd_val(*pgdp);
ptval_to_str(pgd_str, pgd_val(*pgdp));
if (!pgd_present(*pgdp) || pgd_leaf(*pgdp)) {
pr_alert("pgd:%08llx\n", pgdv);
pr_alert("pgd:%s\n", pgd_str);
return;
}
p4dp = p4d_offset(pgdp, addr);
p4d = p4dp_get(p4dp);
p4dv = p4d_val(p4d);
ptval_to_str(p4d_str, p4d_val(p4d));
if (!p4d_present(p4d) || p4d_leaf(p4d)) {
pr_alert("pgd:%08llx p4d:%08llx\n", pgdv, p4dv);
pr_alert("pgd:%s p4d:%s\n", pgd_str, p4d_str);
return;
}
pudp = pud_offset(p4dp, addr);
pud = pudp_get(pudp);
pudv = pud_val(pud);
ptval_to_str(pud_str, pud_val(pud));
if (!pud_present(pud) || pud_leaf(pud)) {
pr_alert("pgd:%08llx p4d:%08llx pud:%08llx\n", pgdv, p4dv, pudv);
pr_alert("pgd:%s p4d:%s pud:%s\n", pgd_str, p4d_str, pud_str);
return;
}
pmdp = pmd_offset(pudp, addr);
pmd = pmdp_get(pmdp);
pmdv = pmd_val(pmd);
ptval_to_str(pmd_str, pmd_val(pmd));
/*
* Dumping the PTE would be nice, but it's tricky with CONFIG_HIGHPTE,
@@ -567,8 +610,7 @@ static void __print_bad_page_map_pgtable(struct mm_struct *mm, unsigned long add
* doing another map would be bad. print_bad_page_map() should
* already take care of printing the PTE.
*/
pr_alert("pgd:%08llx p4d:%08llx pud:%08llx pmd:%08llx\n", pgdv,
p4dv, pudv, pmdv);
pr_alert("pgd:%s p4d:%s pud:%s pmd:%s\n", pgd_str, p4d_str, pud_str, pmd_str);
}
/*
@@ -584,25 +626,34 @@ static void __print_bad_page_map_pgtable(struct mm_struct *mm, unsigned long add
* page table lock.
*/
static void print_bad_page_map(struct vm_area_struct *vma,
unsigned long addr, unsigned long long entry, struct page *page,
enum pgtable_level level)
unsigned long addr, const void *entry, size_t entry_size,
struct page *page, enum pgtable_level level)
{
struct address_space *mapping;
pgoff_t index;
char entry_str[PTVAL_STR_MAX];
pgoff_t index, anon_index;
if (is_bad_page_map_ratelimited())
return;
mapping = vma->vm_file ? vma->vm_file->f_mapping : NULL;
index = linear_page_index(vma, addr);
anon_index = __linear_anon_page_index(vma, addr);
pr_alert("BUG: Bad page map in process %s %s:%08llx", current->comm,
pgtable_level_to_str(level), entry);
ptval_bytes_to_hex_str(entry_str, sizeof(entry_str), entry, entry_size);
pr_alert("BUG: Bad page map in process %s %s:%s", current->comm,
pgtable_level_to_str(level), entry_str);
__print_bad_page_map_pgtable(vma->vm_mm, addr);
if (page)
dump_page(page, "bad page map");
pr_alert("addr:%px vm_flags:%08lx anon_vma:%px mapping:%px index:%lx\n",
(void *)addr, vma->vm_flags, vma->anon_vma, mapping, index);
pr_alert("addr:%px vm_flags:%08lx anon_vma:%px mapping:%px",
(void *)addr, vma->vm_flags, vma->anon_vma, mapping);
if (!vma_is_cow_mapping(vma) || index == anon_index) {
pr_cont(" index:%lx\n", index);
} else {
pr_cont(" index:%lx (file) %lx (anon)\n", index, anon_index);
}
pr_alert("file:%pD fault:%ps mmap:%ps mmap_prepare: %ps read_folio:%ps\n",
vma->vm_file,
vma->vm_ops ? vma->vm_ops->fault : NULL,
@@ -627,8 +678,13 @@ static inline bool pgtable_level_has_pxx_special(enum pgtable_level level)
}
}
#define print_bad_pte(vma, addr, pte, page) \
print_bad_page_map(vma, addr, pte_val(pte), page, PGTABLE_LEVEL_PTE)
static void print_bad_pte(struct vm_area_struct *vma, unsigned long addr,
pte_t pte, struct page *page)
{
auto entry = pte_val(pte);
print_bad_page_map(vma, addr, &entry, sizeof(entry), page, PGTABLE_LEVEL_PTE);
}
/**
* __vm_normal_page() - Get the "struct page" associated with a page table entry.
@@ -636,8 +692,9 @@ static inline bool pgtable_level_has_pxx_special(enum pgtable_level level)
* @addr: The address where the page table entry is mapped.
* @pfn: The PFN stored in the page table entry.
* @special: Whether the page table entry is marked "special".
* @level: The page table level for error reporting purposes only.
* @entry: The page table entry value for error reporting purposes only.
* @entry_size: The size of @entry.
* @level: The page table level for error reporting purposes only.
*
* "Special" mappings do not wish to be associated with a "struct page" (either
* it doesn't exist, or it exists but they don't want to touch it). In this
@@ -697,7 +754,7 @@ static inline bool pgtable_level_has_pxx_special(enum pgtable_level level)
*/
static inline struct page *__vm_normal_page(struct vm_area_struct *vma,
unsigned long addr, unsigned long pfn, bool special,
unsigned long long entry, enum pgtable_level level)
const void *entry, size_t entry_size, enum pgtable_level level)
{
if (pgtable_level_has_pxx_special(level)) {
if (unlikely(special)) {
@@ -710,7 +767,7 @@ static inline struct page *__vm_normal_page(struct vm_area_struct *vma,
if (is_zero_pfn(pfn) || is_huge_zero_pfn(pfn))
return NULL;
print_bad_page_map(vma, addr, entry, NULL, level);
print_bad_page_map(vma, addr, entry, entry_size, NULL, level);
return NULL;
}
/*
@@ -730,7 +787,7 @@ static inline struct page *__vm_normal_page(struct vm_area_struct *vma,
/* Only CoW'ed anon folios are "normal". */
if (pfn == index)
return NULL;
if (!is_cow_mapping(vma->vm_flags))
if (!vma_is_cow_mapping(vma))
return NULL;
}
}
@@ -741,7 +798,7 @@ static inline struct page *__vm_normal_page(struct vm_area_struct *vma,
if (unlikely(pfn > highest_memmap_pfn)) {
/* Corrupted page table entry. */
print_bad_page_map(vma, addr, entry, NULL, level);
print_bad_page_map(vma, addr, entry, entry_size, NULL, level);
return NULL;
}
/*
@@ -767,8 +824,10 @@ static inline struct page *__vm_normal_page(struct vm_area_struct *vma,
struct page *vm_normal_page(struct vm_area_struct *vma, unsigned long addr,
pte_t pte)
{
auto entry = pte_val(pte);
return __vm_normal_page(vma, addr, pte_pfn(pte), pte_special(pte),
pte_val(pte), PGTABLE_LEVEL_PTE);
&entry, sizeof(entry), PGTABLE_LEVEL_PTE);
}
/**
@@ -809,8 +868,10 @@ struct folio *vm_normal_folio(struct vm_area_struct *vma, unsigned long addr,
struct page *vm_normal_page_pmd(struct vm_area_struct *vma, unsigned long addr,
pmd_t pmd)
{
auto entry = pmd_val(pmd);
return __vm_normal_page(vma, addr, pmd_pfn(pmd), pmd_special(pmd),
pmd_val(pmd), PGTABLE_LEVEL_PMD);
&entry, sizeof(entry), PGTABLE_LEVEL_PMD);
}
/**
@@ -850,8 +911,10 @@ struct folio *vm_normal_folio_pmd(struct vm_area_struct *vma,
struct page *vm_normal_page_pud(struct vm_area_struct *vma,
unsigned long addr, pud_t pud)
{
auto entry = pud_val(pud);
return __vm_normal_page(vma, addr, pud_pfn(pud), pud_special(pud),
pud_val(pud), PGTABLE_LEVEL_PUD);
&entry, sizeof(entry), PGTABLE_LEVEL_PUD);
}
#endif
@@ -946,7 +1009,6 @@ copy_nonpresent_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
pte_t *dst_pte, pte_t *src_pte, struct vm_area_struct *dst_vma,
struct vm_area_struct *src_vma, unsigned long addr, int *rss)
{
vm_flags_t vm_flags = dst_vma->vm_flags;
pte_t orig_pte = ptep_get(src_pte);
softleaf_t entry = softleaf_from_pte(orig_pte);
pte_t pte = orig_pte;
@@ -970,7 +1032,7 @@ copy_nonpresent_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
rss[mm_counter(folio)]++;
if (!softleaf_is_migration_read(entry) &&
is_cow_mapping(vm_flags)) {
vma_is_cow_mapping(dst_vma)) {
/*
* COW mappings require pages in both parent and child
* to be set to read. A previously exclusive entry is
@@ -1011,7 +1073,7 @@ copy_nonpresent_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
* save and restore device driver state).
*/
if (softleaf_is_device_private_write(entry) &&
is_cow_mapping(vm_flags)) {
vma_is_cow_mapping(dst_vma)) {
entry = make_readable_device_private_entry(
swp_offset(entry));
pte = swp_entry_to_pte(entry);
@@ -1026,7 +1088,7 @@ copy_nonpresent_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
* exclusive entries currently only support private writable
* (ie. COW) mappings.
*/
VM_BUG_ON(!is_cow_mapping(src_vma->vm_flags));
VM_BUG_ON(!vma_is_cow_mapping(src_vma));
if (try_restore_exclusive_pte(src_vma, addr, src_pte, orig_pte))
return -EBUSY;
return -ENOENT;
@@ -1125,7 +1187,7 @@ static __always_inline void __copy_present_ptes(struct vm_area_struct *dst_vma,
}
/* If it's a COW mapping, write protect it both processes. */
if (is_cow_mapping(src_vma->vm_flags) && writable) {
if (vma_is_cow_mapping(src_vma) && writable) {
wrprotect_ptes(src_mm, addr, src_pte, nr);
pte = pte_wrprotect(pte);
}
@@ -1546,9 +1608,9 @@ copy_page_range(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma)
* We need to invalidate the secondary MMU mappings only when
* there could be a permission downgrade on the ptes of the
* parent mm. And a permission downgrade will only happen if
* is_cow_mapping() returns true.
* vma_is_cow_mapping() returns true.
*/
is_cow = is_cow_mapping(src_vma->vm_flags);
is_cow = vma_is_cow_mapping(src_vma);
if (is_cow) {
mmu_notifier_range_init(&range, MMU_NOTIFY_PROTECTION_PAGE,
@@ -2381,7 +2443,7 @@ static bool vm_mixed_zeropage_allowed(struct vm_area_struct *vma)
if (mm_forbids_zeropage(vma->vm_mm))
return false;
/* zeropages in COW mappings are common and unproblematic. */
if (is_cow_mapping(vma->vm_flags))
if (vma_is_cow_mapping(vma))
return true;
/* Mappings that do not allow for writable PTEs are unproblematic. */
if (!(vma->vm_flags & (VM_WRITE | VM_MAYWRITE)))
@@ -2832,7 +2894,7 @@ vm_fault_t vmf_insert_pfn_prot(struct vm_area_struct *vma, unsigned long addr,
BUG_ON(!(vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP)));
BUG_ON((vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP)) ==
(VM_PFNMAP|VM_MIXEDMAP));
BUG_ON((vma->vm_flags & VM_PFNMAP) && is_cow_mapping(vma->vm_flags));
BUG_ON((vma->vm_flags & VM_PFNMAP) && vma_is_cow_mapping(vma));
BUG_ON((vma->vm_flags & VM_MIXEDMAP) && pfn_valid(pfn));
if (addr < vma->vm_start || addr >= vma->vm_end)
@@ -3244,7 +3306,7 @@ static int remap_pfn_range_prepare_vma(struct vm_area_struct *vma,
unsigned long size)
{
const unsigned long end = addr + PAGE_ALIGN(size);
const bool is_cow = is_cow_mapping(vma->vm_flags);
const bool is_cow = vma_is_cow_mapping(vma);
int err;
err = get_remap_pgoff(is_cow, addr, end, vma->vm_start, vma->vm_end,
@@ -6744,7 +6806,7 @@ static vm_fault_t sanitize_fault_flags(struct vm_area_struct *vma,
* FAULT_FLAG_UNSHARE only applies to COW mappings. Let's
* just treat it like an ordinary read-fault otherwise.
*/
if (!is_cow_mapping(vma->vm_flags))
if (!vma_is_cow_mapping(vma))
*flags &= ~FAULT_FLAG_UNSHARE;
} else if (*flags & FAULT_FLAG_WRITE) {
/* Write faults on read-only mappings are impossible ... */
@@ -6752,7 +6814,7 @@ static vm_fault_t sanitize_fault_flags(struct vm_area_struct *vma,
return VM_FAULT_SIGSEGV;
/* ... and FOLL_FORCE only applies to COW mappings. */
if (WARN_ON_ONCE(!(vma->vm_flags & VM_WRITE) &&
!is_cow_mapping(vma->vm_flags)))
!vma_is_cow_mapping(vma)))
return VM_FAULT_SIGSEGV;
}
#ifdef CONFIG_PER_VMA_LOCK

View File

@@ -844,7 +844,7 @@ bool folio_can_map_prot_numa(struct folio *folio, struct vm_area_struct *vma,
return false;
/* Also skip shared copy-on-write folios */
if (is_cow_mapping(vma->vm_flags) && folio_maybe_mapped_shared(folio))
if (vma_is_cow_mapping(vma) && folio_maybe_mapped_shared(folio))
return false;
/* Folios are pinned and can't be migrated */
@@ -2060,24 +2060,15 @@ struct mempolicy *get_vma_policy(struct vm_area_struct *vma,
bool vma_policy_mof(struct vm_area_struct *vma)
{
struct mempolicy *pol;
pgoff_t ilx;
bool mof;
if (vma->vm_ops && vma->vm_ops->get_policy) {
bool ret = false;
pgoff_t ilx; /* ignored here */
pol = vma->vm_ops->get_policy(vma, vma->vm_start, &ilx);
if (pol && (pol->flags & MPOL_F_MOF))
ret = true;
mpol_cond_put(pol);
return ret;
}
pol = vma->vm_policy;
pol = __get_vma_policy(vma, vma->vm_start, &ilx);
if (!pol)
pol = get_task_policy(current);
return pol->flags & MPOL_F_MOF;
mof = pol->flags & MPOL_F_MOF;
mpol_cond_put(pol);
return mof;
}
bool apply_policy_zone(struct mempolicy *policy, enum zone_type zone)

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