Merge tag 'mm-stable-2022-08-03' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 5 Aug 2022 23:32:45 +0000 (16:32 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 5 Aug 2022 23:32:45 +0000 (16:32 -0700)
Pull MM updates from Andrew Morton:
 "Most of the MM queue. A few things are still pending.

  Liam's maple tree rework didn't make it. This has resulted in a few
  other minor patch series being held over for next time.

  Multi-gen LRU still isn't merged as we were waiting for mapletree to
  stabilize. The current plan is to merge MGLRU into -mm soon and to
  later reintroduce mapletree, with a view to hopefully getting both
  into 6.1-rc1.

  Summary:

   - The usual batches of cleanups from Baoquan He, Muchun Song, Miaohe
     Lin, Yang Shi, Anshuman Khandual and Mike Rapoport

   - Some kmemleak fixes from Patrick Wang and Waiman Long

   - DAMON updates from SeongJae Park

   - memcg debug/visibility work from Roman Gushchin

   - vmalloc speedup from Uladzislau Rezki

   - more folio conversion work from Matthew Wilcox

   - enhancements for coherent device memory mapping from Alex Sierra

   - addition of shared pages tracking and CoW support for fsdax, from
     Shiyang Ruan

   - hugetlb optimizations from Mike Kravetz

   - Mel Gorman has contributed some pagealloc changes to improve
     latency and realtime behaviour.

   - mprotect soft-dirty checking has been improved by Peter Xu

   - Many other singleton patches all over the place"

 [ XFS merge from hell as per Darrick Wong in

   https://lore.kernel.org/all/YshKnxb4VwXycPO8@magnolia/ ]

* tag 'mm-stable-2022-08-03' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm: (282 commits)
  tools/testing/selftests/vm/hmm-tests.c: fix build
  mm: Kconfig: fix typo
  mm: memory-failure: convert to pr_fmt()
  mm: use is_zone_movable_page() helper
  hugetlbfs: fix inaccurate comment in hugetlbfs_statfs()
  hugetlbfs: cleanup some comments in inode.c
  hugetlbfs: remove unneeded header file
  hugetlbfs: remove unneeded hugetlbfs_ops forward declaration
  hugetlbfs: use helper macro SZ_1{K,M}
  mm: cleanup is_highmem()
  mm/hmm: add a test for cross device private faults
  selftests: add soft-dirty into run_vmtests.sh
  selftests: soft-dirty: add test for mprotect
  mm/mprotect: fix soft-dirty check in can_change_pte_writable()
  mm: memcontrol: fix potential oom_lock recursion deadlock
  mm/gup.c: fix formatting in check_and_migrate_movable_page()
  xfs: fail dax mount if reflink is enabled on a partition
  mm/memcontrol.c: remove the redundant updating of stats_flush_threshold
  userfaultfd: don't fail on unrecognized features
  hugetlb_cgroup: fix wrong hugetlb cgroup numa stat
  ...

451 files changed:
Documentation/ABI/testing/procfs-smaps_rollup
Documentation/ABI/testing/sysfs-kernel-mm-ksm
Documentation/ABI/testing/sysfs-kernel-slab
Documentation/admin-guide/cgroup-v2.rst
Documentation/admin-guide/kernel-parameters.txt
Documentation/admin-guide/mm/concepts.rst
Documentation/admin-guide/mm/damon/index.rst
Documentation/admin-guide/mm/damon/lru_sort.rst [new file with mode: 0644]
Documentation/admin-guide/mm/damon/reclaim.rst
Documentation/admin-guide/mm/damon/usage.rst
Documentation/admin-guide/mm/index.rst
Documentation/admin-guide/mm/shrinker_debugfs.rst [new file with mode: 0644]
Documentation/admin-guide/sysctl/vm.rst
Documentation/core-api/index.rst
Documentation/dev-tools/kmemleak.rst
Documentation/filesystems/proc.rst
Documentation/index.rst
Documentation/mm/active_mm.rst [new file with mode: 0644]
Documentation/mm/arch_pgtable_helpers.rst [new file with mode: 0644]
Documentation/mm/balance.rst [new file with mode: 0644]
Documentation/mm/bootmem.rst [new file with mode: 0644]
Documentation/mm/damon/api.rst [new file with mode: 0644]
Documentation/mm/damon/design.rst [new file with mode: 0644]
Documentation/mm/damon/faq.rst [new file with mode: 0644]
Documentation/mm/damon/index.rst [new file with mode: 0644]
Documentation/mm/free_page_reporting.rst [new file with mode: 0644]
Documentation/mm/frontswap.rst [new file with mode: 0644]
Documentation/mm/highmem.rst [new file with mode: 0644]
Documentation/mm/hmm.rst [new file with mode: 0644]
Documentation/mm/hugetlbfs_reserv.rst [new file with mode: 0644]
Documentation/mm/hwpoison.rst [new file with mode: 0644]
Documentation/mm/index.rst [new file with mode: 0644]
Documentation/mm/ksm.rst [new file with mode: 0644]
Documentation/mm/memory-model.rst [new file with mode: 0644]
Documentation/mm/mmu_notifier.rst [new file with mode: 0644]
Documentation/mm/numa.rst [new file with mode: 0644]
Documentation/mm/oom.rst [new file with mode: 0644]
Documentation/mm/overcommit-accounting.rst [new file with mode: 0644]
Documentation/mm/page_allocation.rst [new file with mode: 0644]
Documentation/mm/page_cache.rst [new file with mode: 0644]
Documentation/mm/page_frags.rst [new file with mode: 0644]
Documentation/mm/page_migration.rst [new file with mode: 0644]
Documentation/mm/page_owner.rst [new file with mode: 0644]
Documentation/mm/page_reclaim.rst [new file with mode: 0644]
Documentation/mm/page_table_check.rst [new file with mode: 0644]
Documentation/mm/page_tables.rst [new file with mode: 0644]
Documentation/mm/physical_memory.rst [new file with mode: 0644]
Documentation/mm/process_addrs.rst [new file with mode: 0644]
Documentation/mm/remap_file_pages.rst [new file with mode: 0644]
Documentation/mm/shmfs.rst [new file with mode: 0644]
Documentation/mm/slab.rst [new file with mode: 0644]
Documentation/mm/slub.rst [new file with mode: 0644]
Documentation/mm/split_page_table_lock.rst [new file with mode: 0644]
Documentation/mm/swap.rst [new file with mode: 0644]
Documentation/mm/transhuge.rst [new file with mode: 0644]
Documentation/mm/unevictable-lru.rst [new file with mode: 0644]
Documentation/mm/vmalloc.rst [new file with mode: 0644]
Documentation/mm/vmalloced-kernel-stacks.rst [new file with mode: 0644]
Documentation/mm/vmemmap_dedup.rst [new file with mode: 0644]
Documentation/mm/z3fold.rst [new file with mode: 0644]
Documentation/mm/zsmalloc.rst [new file with mode: 0644]
Documentation/translations/zh_CN/admin-guide/mm/damon/index.rst
Documentation/translations/zh_CN/admin-guide/mm/damon/reclaim.rst
Documentation/translations/zh_CN/admin-guide/mm/damon/usage.rst
Documentation/translations/zh_CN/core-api/index.rst
Documentation/translations/zh_CN/index.rst
Documentation/translations/zh_CN/mm/active_mm.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/balance.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/damon/api.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/damon/design.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/damon/faq.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/damon/index.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/free_page_reporting.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/frontswap.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/highmem.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/hmm.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/hugetlbfs_reserv.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/hwpoison.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/index.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/ksm.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/memory-model.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/mmu_notifier.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/numa.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/overcommit-accounting.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/page_frags.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/page_migration.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/page_owner.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/page_table_check.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/remap_file_pages.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/split_page_table_lock.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/vmalloced-kernel-stacks.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/z3fold.rst [new file with mode: 0644]
Documentation/translations/zh_CN/mm/zsmalloc.rst [new file with mode: 0644]
Documentation/translations/zh_CN/vm/active_mm.rst [deleted file]
Documentation/translations/zh_CN/vm/balance.rst [deleted file]
Documentation/translations/zh_CN/vm/damon/api.rst [deleted file]
Documentation/translations/zh_CN/vm/damon/design.rst [deleted file]
Documentation/translations/zh_CN/vm/damon/faq.rst [deleted file]
Documentation/translations/zh_CN/vm/damon/index.rst [deleted file]
Documentation/translations/zh_CN/vm/free_page_reporting.rst [deleted file]
Documentation/translations/zh_CN/vm/frontswap.rst [deleted file]
Documentation/translations/zh_CN/vm/highmem.rst [deleted file]
Documentation/translations/zh_CN/vm/hmm.rst [deleted file]
Documentation/translations/zh_CN/vm/hugetlbfs_reserv.rst [deleted file]
Documentation/translations/zh_CN/vm/hwpoison.rst [deleted file]
Documentation/translations/zh_CN/vm/index.rst [deleted file]
Documentation/translations/zh_CN/vm/ksm.rst [deleted file]
Documentation/translations/zh_CN/vm/memory-model.rst [deleted file]
Documentation/translations/zh_CN/vm/mmu_notifier.rst [deleted file]
Documentation/translations/zh_CN/vm/numa.rst [deleted file]
Documentation/translations/zh_CN/vm/overcommit-accounting.rst [deleted file]
Documentation/translations/zh_CN/vm/page_frags.rst [deleted file]
Documentation/translations/zh_CN/vm/page_migration.rst [deleted file]
Documentation/translations/zh_CN/vm/page_owner.rst [deleted file]
Documentation/translations/zh_CN/vm/page_table_check.rst [deleted file]
Documentation/translations/zh_CN/vm/remap_file_pages.rst [deleted file]
Documentation/translations/zh_CN/vm/split_page_table_lock.rst [deleted file]
Documentation/translations/zh_CN/vm/vmalloced-kernel-stacks.rst [deleted file]
Documentation/translations/zh_CN/vm/z3fold.rst [deleted file]
Documentation/translations/zh_CN/vm/zsmalloc.rst [deleted file]
Documentation/translations/zh_TW/index.rst
Documentation/vm/.gitignore [deleted file]
Documentation/vm/active_mm.rst [deleted file]
Documentation/vm/arch_pgtable_helpers.rst [deleted file]
Documentation/vm/balance.rst [deleted file]
Documentation/vm/bootmem.rst [deleted file]
Documentation/vm/damon/api.rst [deleted file]
Documentation/vm/damon/design.rst [deleted file]
Documentation/vm/damon/faq.rst [deleted file]
Documentation/vm/damon/index.rst [deleted file]
Documentation/vm/free_page_reporting.rst [deleted file]
Documentation/vm/frontswap.rst [deleted file]
Documentation/vm/highmem.rst [deleted file]
Documentation/vm/hmm.rst [deleted file]
Documentation/vm/hugetlbfs_reserv.rst [deleted file]
Documentation/vm/hwpoison.rst [deleted file]
Documentation/vm/index.rst [deleted file]
Documentation/vm/ksm.rst [deleted file]
Documentation/vm/memory-model.rst [deleted file]
Documentation/vm/mmu_notifier.rst [deleted file]
Documentation/vm/numa.rst [deleted file]
Documentation/vm/oom.rst [deleted file]
Documentation/vm/overcommit-accounting.rst [deleted file]
Documentation/vm/page_allocation.rst [deleted file]
Documentation/vm/page_cache.rst [deleted file]
Documentation/vm/page_frags.rst [deleted file]
Documentation/vm/page_migration.rst [deleted file]
Documentation/vm/page_owner.rst [deleted file]
Documentation/vm/page_reclaim.rst [deleted file]
Documentation/vm/page_table_check.rst [deleted file]
Documentation/vm/page_tables.rst [deleted file]
Documentation/vm/physical_memory.rst [deleted file]
Documentation/vm/process_addrs.rst [deleted file]
Documentation/vm/remap_file_pages.rst [deleted file]
Documentation/vm/shmfs.rst [deleted file]
Documentation/vm/slab.rst [deleted file]
Documentation/vm/slub.rst [deleted file]
Documentation/vm/split_page_table_lock.rst [deleted file]
Documentation/vm/swap.rst [deleted file]
Documentation/vm/transhuge.rst [deleted file]
Documentation/vm/unevictable-lru.rst [deleted file]
Documentation/vm/vmalloc.rst [deleted file]
Documentation/vm/vmalloced-kernel-stacks.rst [deleted file]
Documentation/vm/vmemmap_dedup.rst [deleted file]
Documentation/vm/z3fold.rst [deleted file]
Documentation/vm/zsmalloc.rst [deleted file]
MAINTAINERS
arch/alpha/include/asm/pgtable.h
arch/alpha/mm/fault.c
arch/alpha/mm/init.c
arch/arc/include/asm/pgtable-bits-arcv2.h
arch/arc/mm/fault.c
arch/arc/mm/mmap.c
arch/arm/include/asm/pgtable.h
arch/arm/kernel/head.S
arch/arm/lib/uaccess_with_memcpy.c
arch/arm/mm/fault.c
arch/arm/mm/mmu.c
arch/arm64/Kconfig
arch/arm64/include/asm/hugetlb.h
arch/arm64/include/asm/memory.h
arch/arm64/include/asm/pgtable-prot.h
arch/arm64/mm/fault.c
arch/arm64/mm/hugetlbpage.c
arch/arm64/mm/mmap.c
arch/csky/include/asm/pgalloc.h
arch/csky/include/asm/pgtable.h
arch/csky/mm/fault.c
arch/csky/mm/init.c
arch/hexagon/include/asm/pgtable.h
arch/hexagon/mm/init.c
arch/hexagon/mm/vm_fault.c
arch/ia64/include/asm/pgtable.h
arch/ia64/mm/fault.c
arch/ia64/mm/init.c
arch/loongarch/Kconfig
arch/loongarch/include/asm/pgalloc.h
arch/loongarch/include/asm/pgtable-bits.h
arch/loongarch/include/asm/pgtable.h
arch/loongarch/kernel/asm-offsets.c
arch/loongarch/mm/cache.c
arch/loongarch/mm/pgtable.c
arch/loongarch/mm/tlbex.S
arch/m68k/include/asm/mcf_pgtable.h
arch/m68k/include/asm/motorola_pgtable.h
arch/m68k/include/asm/sun3_pgtable.h
arch/m68k/mm/fault.c
arch/m68k/mm/mcfmmu.c
arch/m68k/mm/motorola.c
arch/m68k/mm/sun3mmu.c
arch/microblaze/include/asm/pgtable.h
arch/microblaze/mm/fault.c
arch/microblaze/mm/init.c
arch/mips/include/asm/pgalloc.h
arch/mips/include/asm/pgtable-32.h
arch/mips/include/asm/pgtable-64.h
arch/mips/include/asm/pgtable.h
arch/mips/kernel/asm-offsets.c
arch/mips/kernel/mips-mt.c
arch/mips/kvm/mmu.c
arch/mips/mm/cache.c
arch/mips/mm/fault.c
arch/mips/mm/pgtable.c
arch/mips/mm/tlbex.c
arch/nios2/include/asm/pgtable.h
arch/nios2/mm/fault.c
arch/nios2/mm/init.c
arch/nios2/mm/pgtable.c
arch/openrisc/include/asm/pgtable.h
arch/openrisc/mm/fault.c
arch/openrisc/mm/init.c
arch/parisc/include/asm/pgalloc.h
arch/parisc/include/asm/pgtable.h
arch/parisc/mm/fault.c
arch/parisc/mm/init.c
arch/powerpc/Kconfig
arch/powerpc/include/asm/book3s/64/pgtable.h
arch/powerpc/include/asm/pgtable.h
arch/powerpc/mm/copro_fault.c
arch/powerpc/mm/fault.c
arch/powerpc/mm/pgtable.c
arch/riscv/include/asm/pgtable.h
arch/riscv/mm/fault.c
arch/riscv/mm/init.c
arch/s390/include/asm/pgtable.h
arch/s390/mm/fault.c
arch/s390/mm/mmap.c
arch/sh/include/asm/pgtable.h
arch/sh/mm/fault.c
arch/sh/mm/mmap.c
arch/sparc/Kconfig
arch/sparc/include/asm/pgtable_32.h
arch/sparc/include/asm/pgtable_64.h
arch/sparc/mm/fault_32.c
arch/sparc/mm/fault_64.c
arch/sparc/mm/init_32.c
arch/sparc/mm/init_64.c
arch/um/include/asm/pgtable.h
arch/um/kernel/mem.c
arch/um/kernel/trap.c
arch/x86/Kconfig
arch/x86/include/asm/mem_encrypt.h
arch/x86/include/asm/pgtable_types.h
arch/x86/kvm/mmu/mmu.c
arch/x86/mm/fault.c
arch/x86/mm/hugetlbpage.c
arch/x86/mm/mem_encrypt_amd.c
arch/x86/mm/pgprot.c
arch/x86/um/mem_32.c
arch/xtensa/include/asm/pgalloc.h
arch/xtensa/include/asm/pgtable.h
arch/xtensa/mm/fault.c
arch/xtensa/mm/init.c
drivers/android/binder_alloc.c
drivers/android/binder_alloc.h
drivers/android/binder_alloc_selftest.c
drivers/block/zram/zcomp.c
drivers/block/zram/zram_drv.c
drivers/dax/super.c
drivers/gpu/drm/amd/amdkfd/kfd_migrate.c
drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
drivers/gpu/drm/msm/msm_gem_shrinker.c
drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
drivers/gpu/drm/ttm/ttm_pool.c
drivers/md/bcache/btree.c
drivers/md/dm-bufio.c
drivers/md/dm-zoned-metadata.c
drivers/md/dm.c
drivers/md/raid5.c
drivers/misc/vmw_balloon.c
drivers/nvdimm/pmem.c
drivers/of/fdt.c
drivers/virtio/virtio_balloon.c
drivers/virtio/virtio_mem.c
drivers/xen/xenbus/xenbus_probe_backend.c
fs/btrfs/super.c
fs/dax.c
fs/erofs/super.c
fs/erofs/utils.c
fs/ext2/super.c
fs/ext4/extents_status.c
fs/ext4/super.c
fs/f2fs/super.c
fs/gfs2/glock.c
fs/gfs2/main.c
fs/hugetlbfs/inode.c
fs/jbd2/journal.c
fs/mbcache.c
fs/nfs/nfs42xattr.c
fs/nfs/super.c
fs/nfsd/filecache.c
fs/nfsd/nfscache.c
fs/proc/task_mmu.c
fs/quota/dquot.c
fs/remap_range.c
fs/super.c
fs/ubifs/super.c
fs/userfaultfd.c
fs/xfs/Makefile
fs/xfs/xfs_buf.c
fs/xfs/xfs_file.c
fs/xfs/xfs_fsops.c
fs/xfs/xfs_icache.c
fs/xfs/xfs_inode.c
fs/xfs/xfs_inode.h
fs/xfs/xfs_iomap.c
fs/xfs/xfs_iomap.h
fs/xfs/xfs_mount.h
fs/xfs/xfs_notify_failure.c [new file with mode: 0644]
fs/xfs/xfs_qm.c
fs/xfs/xfs_reflink.c
fs/xfs/xfs_super.c
fs/xfs/xfs_super.h
include/linux/backing-dev.h
include/linux/damon.h
include/linux/dax.h
include/linux/fs.h
include/linux/highmem.h
include/linux/hmm.h
include/linux/huge_mm.h
include/linux/hugetlb.h
include/linux/khugepaged.h
include/linux/kmemleak.h
include/linux/memcontrol.h
include/linux/memory_hotplug.h
include/linux/memremap.h
include/linux/migrate.h
include/linux/mm.h
include/linux/mm_types.h
include/linux/mmu_notifier.h
include/linux/mmzone.h
include/linux/page-flags.h
include/linux/pagemap.h
include/linux/pagevec.h
include/linux/pgtable.h
include/linux/rmap.h
include/linux/sched/mm.h
include/linux/shmem_fs.h
include/linux/shrinker.h
include/linux/swap.h
include/linux/swapops.h
kernel/rcu/tree.c
lib/Kconfig.debug
lib/test_free_pages.c
lib/test_hmm.c
lib/test_hmm_uapi.h
lib/test_vmalloc.c
mm/Kconfig
mm/Makefile
mm/cma_debug.c
mm/compaction.c
mm/damon/Kconfig
mm/damon/Makefile
mm/damon/dbgfs.c
mm/damon/lru_sort.c [new file with mode: 0644]
mm/damon/ops-common.c
mm/damon/ops-common.h
mm/damon/paddr.c
mm/damon/reclaim.c
mm/damon/sysfs.c
mm/debug_vm_pgtable.c
mm/filemap.c
mm/frontswap.c
mm/gup.c
mm/gup_test.c
mm/highmem.c
mm/huge_memory.c
mm/hugetlb.c
mm/hugetlb_cgroup.c
mm/hugetlb_vmemmap.c
mm/internal.h
mm/kasan/common.c
mm/kasan/hw_tags.c
mm/kasan/kasan.h
mm/kasan/report.c
mm/kfence/core.c
mm/khugepaged.c
mm/kmemleak.c
mm/ksm.c
mm/list_lru.c
mm/madvise.c
mm/memblock.c
mm/memcontrol.c
mm/memory-failure.c
mm/memory.c
mm/memory_hotplug.c
mm/mempolicy.c
mm/mempool.c
mm/memremap.c
mm/migrate.c
mm/migrate_device.c
mm/mlock.c
mm/mmap.c
mm/mprotect.c
mm/nommu.c
mm/page_alloc.c
mm/page_vma_mapped.c
mm/percpu.c
mm/rmap.c
mm/shmem.c
mm/shrinker_debug.c [new file with mode: 0644]
mm/slab.c
mm/sparse-vmemmap.c
mm/sparse.c
mm/swap.c
mm/swap.h
mm/swap_state.c
mm/swapfile.c
mm/util.c
mm/vmalloc.c
mm/vmscan.c
mm/workingset.c
mm/zsmalloc.c
net/core/net_namespace.c
net/core/page_pool.c
net/sunrpc/auth.c
tools/cgroup/memcg_shrinker.py [new file with mode: 0644]
tools/testing/memblock/linux/kmemleak.h
tools/testing/selftests/vm/Makefile
tools/testing/selftests/vm/hmm-tests.c
tools/testing/selftests/vm/hugepage-mremap.c
tools/testing/selftests/vm/hugetlb-madvise.c
tools/testing/selftests/vm/mrelease_test.c
tools/testing/selftests/vm/run_vmtests.sh
tools/testing/selftests/vm/soft-dirty.c
tools/testing/selftests/vm/test_hmm.sh
tools/testing/selftests/vm/userfaultfd.c
tools/testing/selftests/vm/va_128TBswitch.c
tools/testing/selftests/vm/va_128TBswitch.sh [new file with mode: 0755]
tools/vm/page_owner_sort.c
tools/vm/slabinfo.c

index a4e31c4..b446a71 100644 (file)
@@ -22,6 +22,7 @@ Description:
                        MMUPageSize:           4 kB
                        Rss:                 884 kB
                        Pss:                 385 kB
+                       Pss_Dirty:            68 kB
                        Pss_Anon:            301 kB
                        Pss_File:             80 kB
                        Pss_Shmem:             4 kB
index 1c9bed5..d244674 100644 (file)
@@ -41,7 +41,7 @@ Description:  Kernel Samepage Merging daemon sysfs interface
                sleep_millisecs: how many milliseconds ksm should sleep between
                scans.
 
-               See Documentation/vm/ksm.rst for more information.
+               See Documentation/mm/ksm.rst for more information.
 
 What:          /sys/kernel/mm/ksm/merge_across_nodes
 Date:          January 2013
index c440f49..cd5fb8f 100644 (file)
@@ -37,7 +37,7 @@ Description:
                The alloc_calls file is read-only and lists the kernel code
                locations from which allocations for this cache were performed.
                The alloc_calls file only contains information if debugging is
-               enabled for that cache (see Documentation/vm/slub.rst).
+               enabled for that cache (see Documentation/mm/slub.rst).
 
 What:          /sys/kernel/slab/<cache>/alloc_fastpath
 Date:          February 2008
@@ -219,7 +219,7 @@ Contact:    Pekka Enberg <penberg@cs.helsinki.fi>,
 Description:
                The free_calls file is read-only and lists the locations of
                object frees if slab debugging is enabled (see
-               Documentation/vm/slub.rst).
+               Documentation/mm/slub.rst).
 
 What:          /sys/kernel/slab/<cache>/free_fastpath
 Date:          February 2008
index bf842b8..be4a77b 100644 (file)
@@ -1237,6 +1237,13 @@ PAGE_SIZE multiple when read back.
        the target cgroup. If less bytes are reclaimed than the
        specified amount, -EAGAIN is returned.
 
+       Please note that the proactive reclaim (triggered by this
+       interface) is not meant to indicate memory pressure on the
+       memory cgroup. Therefore socket memory balancing triggered by
+       the memory reclaim normally is not exercised in this case.
+       This means that the networking layer will not adapt based on
+       reclaim induced by memory.reclaim.
+
   memory.peak
        A read-only single value file which exists on non-root
        cgroups.
@@ -1441,6 +1448,24 @@ PAGE_SIZE multiple when read back.
          workingset_nodereclaim
                Number of times a shadow node has been reclaimed
 
+         pgscan (npn)
+               Amount of scanned pages (in an inactive LRU list)
+
+         pgsteal (npn)
+               Amount of reclaimed pages
+
+         pgscan_kswapd (npn)
+               Amount of scanned pages by kswapd (in an inactive LRU list)
+
+         pgscan_direct (npn)
+               Amount of scanned pages directly  (in an inactive LRU list)
+
+         pgsteal_kswapd (npn)
+               Amount of reclaimed pages by kswapd
+
+         pgsteal_direct (npn)
+               Amount of reclaimed pages directly
+
          pgfault (npn)
                Total number of page faults incurred
 
@@ -1450,12 +1475,6 @@ PAGE_SIZE multiple when read back.
          pgrefill (npn)
                Amount of scanned pages (in an active LRU list)
 
-         pgscan (npn)
-               Amount of scanned pages (in an inactive LRU list)
-
-         pgsteal (npn)
-               Amount of reclaimed pages
-
          pgactivate (npn)
                Amount of pages moved to the active LRU list
 
index ef9f80b..43c31e9 100644 (file)
                        Built with CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON=y,
                        the default is on.
 
-                       This is not compatible with memory_hotplug.memmap_on_memory.
-                       If both parameters are enabled, hugetlb_free_vmemmap takes
-                       precedence over memory_hotplug.memmap_on_memory.
+                       Note that the vmemmap pages may be allocated from the added
+                       memory block itself when memory_hotplug.memmap_on_memory is
+                       enabled, those vmemmap pages cannot be optimized even if this
+                       feature is enabled.  Other vmemmap pages not allocated from
+                       the added memory block itself do not be affected.
 
        hung_task_panic=
                        [KNL] Should the hung task detector generate panics.
                        [KNL,X86,ARM] Boolean flag to enable this feature.
                        Format: {on | off (default)}
                        When enabled, runtime hotplugged memory will
-                       allocate its internal metadata (struct pages)
-                       from the hotadded memory which will allow to
-                       hotadd a lot of memory without requiring
-                       additional memory to do so.
+                       allocate its internal metadata (struct pages,
+                       those vmemmap pages cannot be optimized even
+                       if hugetlb_free_vmemmap is enabled) from the
+                       hotadded memory which will allow to hotadd a
+                       lot of memory without requiring additional
+                       memory to do so.
                        This feature is disabled by default because it
                        has some implication on large (e.g. GB)
                        allocations in some configurations (e.g. small
                        Note that even when enabled, there are a few cases where
                        the feature is not effective.
 
-                       This is not compatible with hugetlb_free_vmemmap. If
-                       both parameters are enabled, hugetlb_free_vmemmap takes
-                       precedence over memory_hotplug.memmap_on_memory.
-
        memtest=        [KNL,X86,ARM,M68K,PPC,RISCV] Enable memtest
                        Format: <integer>
                        default : 0 <disable>
                        cache (risks via metadata attacks are mostly
                        unchanged). Debug options disable merging on their
                        own.
-                       For more information see Documentation/vm/slub.rst.
+                       For more information see Documentation/mm/slub.rst.
 
        slab_max_order= [MM, SLAB]
                        Determines the maximum allowed order for slabs.
                        slub_debug can create guard zones around objects and
                        may poison objects when not in use. Also tracks the
                        last alloc / free. For more information see
-                       Documentation/vm/slub.rst.
+                       Documentation/mm/slub.rst.
 
        slub_max_order= [MM, SLUB]
                        Determines the maximum allowed order for slabs.
                        A high setting may cause OOMs due to memory
                        fragmentation. For more information see
-                       Documentation/vm/slub.rst.
+                       Documentation/mm/slub.rst.
 
        slub_min_objects=       [MM, SLUB]
                        The minimum number of objects per slab. SLUB will
                        the number of objects indicated. The higher the number
                        of objects the smaller the overhead of tracking slabs
                        and the less frequently locks need to be acquired.
-                       For more information see Documentation/vm/slub.rst.
+                       For more information see Documentation/mm/slub.rst.
 
        slub_min_order= [MM, SLUB]
                        Determines the minimum page order for slabs. Must be
                        lower than slub_max_order.
-                       For more information see Documentation/vm/slub.rst.
+                       For more information see Documentation/mm/slub.rst.
 
        slub_merge      [MM, SLUB]
                        Same with slab_merge.
index b966fcf..c79f1e3 100644 (file)
@@ -125,7 +125,7 @@ processor. Each bank is referred to as a `node` and for each node Linux
 constructs an independent memory management subsystem. A node has its
 own set of zones, lists of free and used pages and various statistics
 counters. You can find more details about NUMA in
-:ref:`Documentation/vm/numa.rst <numa>` and in
+:ref:`Documentation/mm/numa.rst <numa>` and in
 :ref:`Documentation/admin-guide/mm/numa_memory_policy.rst <numa_memory_policy>`.
 
 Page cache
index 61aff88..0550004 100644 (file)
@@ -4,7 +4,7 @@
 Monitoring Data Accesses
 ========================
 
-:doc:`DAMON </vm/damon/index>` allows light-weight data access monitoring.
+:doc:`DAMON </mm/damon/index>` allows light-weight data access monitoring.
 Using DAMON, users can analyze the memory access patterns of their systems and
 optimize those.
 
@@ -14,3 +14,4 @@ optimize those.
    start
    usage
    reclaim
+   lru_sort
diff --git a/Documentation/admin-guide/mm/damon/lru_sort.rst b/Documentation/admin-guide/mm/damon/lru_sort.rst
new file mode 100644 (file)
index 0000000..c09cace
--- /dev/null
@@ -0,0 +1,294 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=============================
+DAMON-based LRU-lists Sorting
+=============================
+
+DAMON-based LRU-lists Sorting (DAMON_LRU_SORT) is a static kernel module that
+aimed to be used for proactive and lightweight data access pattern based
+(de)prioritization of pages on their LRU-lists for making LRU-lists a more
+trusworthy data access pattern source.
+
+Where Proactive LRU-lists Sorting is Required?
+==============================================
+
+As page-granularity access checking overhead could be significant on huge
+systems, LRU lists are normally not proactively sorted but partially and
+reactively sorted for special events including specific user requests, system
+calls and memory pressure.  As a result, LRU lists are sometimes not so
+perfectly prepared to be used as a trustworthy access pattern source for some
+situations including reclamation target pages selection under sudden memory
+pressure.
+
+Because DAMON can identify access patterns of best-effort accuracy while
+inducing only user-specified range of overhead, proactively running
+DAMON_LRU_SORT could be helpful for making LRU lists more trustworthy access
+pattern source with low and controlled overhead.
+
+How It Works?
+=============
+
+DAMON_LRU_SORT finds hot pages (pages of memory regions that showing access
+rates that higher than a user-specified threshold) and cold pages (pages of
+memory regions that showing no access for a time that longer than a
+user-specified threshold) using DAMON, and prioritizes hot pages while
+deprioritizing cold pages on their LRU-lists.  To avoid it consuming too much
+CPU for the prioritizations, a CPU time usage limit can be configured.  Under
+the limit, it prioritizes and deprioritizes more hot and cold pages first,
+respectively.  System administrators can also configure under what situation
+this scheme should automatically activated and deactivated with three memory
+pressure watermarks.
+
+Its default parameters for hotness/coldness thresholds and CPU quota limit are
+conservatively chosen.  That is, the module under its default parameters could
+be widely used without harm for common situations while providing a level of
+benefits for systems having clear hot/cold access patterns under memory
+pressure while consuming only a limited small portion of CPU time.
+
+Interface: Module Parameters
+============================
+
+To use this feature, you should first ensure your system is running on a kernel
+that is built with ``CONFIG_DAMON_LRU_SORT=y``.
+
+To let sysadmins enable or disable it and tune for the given system,
+DAMON_LRU_SORT utilizes module parameters.  That is, you can put
+``damon_lru_sort.<parameter>=<value>`` on the kernel boot command line or write
+proper values to ``/sys/modules/damon_lru_sort/parameters/<parameter>`` files.
+
+Below are the description of each parameter.
+
+enabled
+-------
+
+Enable or disable DAMON_LRU_SORT.
+
+You can enable DAMON_LRU_SORT by setting the value of this parameter as ``Y``.
+Setting it as ``N`` disables DAMON_LRU_SORT.  Note that DAMON_LRU_SORT could do
+no real monitoring and LRU-lists sorting due to the watermarks-based activation
+condition.  Refer to below descriptions for the watermarks parameter for this.
+
+commit_inputs
+-------------
+
+Make DAMON_LRU_SORT reads the input parameters again, except ``enabled``.
+
+Input parameters that updated while DAMON_LRU_SORT is running are not applied
+by default.  Once this parameter is set as ``Y``, DAMON_LRU_SORT reads values
+of parametrs except ``enabled`` again.  Once the re-reading is done, this
+parameter is set as ``N``.  If invalid parameters are found while the
+re-reading, DAMON_LRU_SORT will be disabled.
+
+hot_thres_access_freq
+---------------------
+
+Access frequency threshold for hot memory regions identification in permil.
+
+If a memory region is accessed in frequency of this or higher, DAMON_LRU_SORT
+identifies the region as hot, and mark it as accessed on the LRU list, so that
+it could not be reclaimed under memory pressure.  50% by default.
+
+cold_min_age
+------------
+
+Time threshold for cold memory regions identification in microseconds.
+
+If a memory region is not accessed for this or longer time, DAMON_LRU_SORT
+identifies the region as cold, and mark it as unaccessed on the LRU list, so
+that it could be reclaimed first under memory pressure.  120 seconds by
+default.
+
+quota_ms
+--------
+
+Limit of time for trying the LRU lists sorting in milliseconds.
+
+DAMON_LRU_SORT tries to use only up to this time within a time window
+(quota_reset_interval_ms) for trying LRU lists sorting.  This can be used
+for limiting CPU consumption of DAMON_LRU_SORT.  If the value is zero, the
+limit is disabled.
+
+10 ms by default.
+
+quota_reset_interval_ms
+-----------------------
+
+The time quota charge reset interval in milliseconds.
+
+The charge reset interval for the quota of time (quota_ms).  That is,
+DAMON_LRU_SORT does not try LRU-lists sorting for more than quota_ms
+milliseconds or quota_sz bytes within quota_reset_interval_ms milliseconds.
+
+1 second by default.
+
+wmarks_interval
+---------------
+
+The watermarks check time interval in microseconds.
+
+Minimal time to wait before checking the watermarks, when DAMON_LRU_SORT is
+enabled but inactive due to its watermarks rule.  5 seconds by default.
+
+wmarks_high
+-----------
+
+Free memory rate (per thousand) for the high watermark.
+
+If free memory of the system in bytes per thousand bytes is higher than this,
+DAMON_LRU_SORT becomes inactive, so it does nothing but periodically checks the
+watermarks.  200 (20%) by default.
+
+wmarks_mid
+----------
+
+Free memory rate (per thousand) for the middle watermark.
+
+If free memory of the system in bytes per thousand bytes is between this and
+the low watermark, DAMON_LRU_SORT becomes active, so starts the monitoring and
+the LRU-lists sorting.  150 (15%) by default.
+
+wmarks_low
+----------
+
+Free memory rate (per thousand) for the low watermark.
+
+If free memory of the system in bytes per thousand bytes is lower than this,
+DAMON_LRU_SORT becomes inactive, so it does nothing but periodically checks the
+watermarks.  50 (5%) by default.
+
+sample_interval
+---------------
+
+Sampling interval for the monitoring in microseconds.
+
+The sampling interval of DAMON for the cold memory monitoring.  Please refer to
+the DAMON documentation (:doc:`usage`) for more detail.  5ms by default.
+
+aggr_interval
+-------------
+
+Aggregation interval for the monitoring in microseconds.
+
+The aggregation interval of DAMON for the cold memory monitoring.  Please
+refer to the DAMON documentation (:doc:`usage`) for more detail.  100ms by
+default.
+
+min_nr_regions
+--------------
+
+Minimum number of monitoring regions.
+
+The minimal number of monitoring regions of DAMON for the cold memory
+monitoring.  This can be used to set lower-bound of the monitoring quality.
+But, setting this too high could result in increased monitoring overhead.
+Please refer to the DAMON documentation (:doc:`usage`) for more detail.  10 by
+default.
+
+max_nr_regions
+--------------
+
+Maximum number of monitoring regions.
+
+The maximum number of monitoring regions of DAMON for the cold memory
+monitoring.  This can be used to set upper-bound of the monitoring overhead.
+However, setting this too low could result in bad monitoring quality.  Please
+refer to the DAMON documentation (:doc:`usage`) for more detail.  1000 by
+defaults.
+
+monitor_region_start
+--------------------
+
+Start of target memory region in physical address.
+
+The start physical address of memory region that DAMON_LRU_SORT will do work
+against.  By default, biggest System RAM is used as the region.
+
+monitor_region_end
+------------------
+
+End of target memory region in physical address.
+
+The end physical address of memory region that DAMON_LRU_SORT will do work
+against.  By default, biggest System RAM is used as the region.
+
+kdamond_pid
+-----------
+
+PID of the DAMON thread.
+
+If DAMON_LRU_SORT is enabled, this becomes the PID of the worker thread.  Else,
+-1.
+
+nr_lru_sort_tried_hot_regions
+-----------------------------
+
+Number of hot memory regions that tried to be LRU-sorted.
+
+bytes_lru_sort_tried_hot_regions
+--------------------------------
+
+Total bytes of hot memory regions that tried to be LRU-sorted.
+
+nr_lru_sorted_hot_regions
+-------------------------
+
+Number of hot memory regions that successfully be LRU-sorted.
+
+bytes_lru_sorted_hot_regions
+----------------------------
+
+Total bytes of hot memory regions that successfully be LRU-sorted.
+
+nr_hot_quota_exceeds
+--------------------
+
+Number of times that the time quota limit for hot regions have exceeded.
+
+nr_lru_sort_tried_cold_regions
+------------------------------
+
+Number of cold memory regions that tried to be LRU-sorted.
+
+bytes_lru_sort_tried_cold_regions
+---------------------------------
+
+Total bytes of cold memory regions that tried to be LRU-sorted.
+
+nr_lru_sorted_cold_regions
+--------------------------
+
+Number of cold memory regions that successfully be LRU-sorted.
+
+bytes_lru_sorted_cold_regions
+-----------------------------
+
+Total bytes of cold memory regions that successfully be LRU-sorted.
+
+nr_cold_quota_exceeds
+---------------------
+
+Number of times that the time quota limit for cold regions have exceeded.
+
+Example
+=======
+
+Below runtime example commands make DAMON_LRU_SORT to find memory regions
+having >=50% access frequency and LRU-prioritize while LRU-deprioritizing
+memory regions that not accessed for 120 seconds.  The prioritization and
+deprioritization is limited to be done using only up to 1% CPU time to avoid
+DAMON_LRU_SORT consuming too much CPU time for the (de)prioritization.  It also
+asks DAMON_LRU_SORT to do nothing if the system's free memory rate is more than
+50%, but start the real works if it becomes lower than 40%.  If DAMON_RECLAIM
+doesn't make progress and therefore the free memory rate becomes lower than
+20%, it asks DAMON_LRU_SORT to do nothing again, so that we can fall back to
+the LRU-list based page granularity reclamation. ::
+
+    # cd /sys/modules/damon_lru_sort/parameters
+    # echo 500 > hot_thres_access_freq
+    # echo 120000000 > cold_min_age
+    # echo 10 > quota_ms
+    # echo 1000 > quota_reset_interval_ms
+    # echo 500 > wmarks_high
+    # echo 400 > wmarks_mid
+    # echo 200 > wmarks_low
+    # echo Y > enabled
index 46306f1..4f1479a 100644 (file)
@@ -48,12 +48,6 @@ DAMON_RECLAIM utilizes module parameters.  That is, you can put
 ``damon_reclaim.<parameter>=<value>`` on the kernel boot command line or write
 proper values to ``/sys/modules/damon_reclaim/parameters/<parameter>`` files.
 
-Note that the parameter values except ``enabled`` are applied only when
-DAMON_RECLAIM starts.  Therefore, if you want to apply new parameter values in
-runtime and DAMON_RECLAIM is already enabled, you should disable and re-enable
-it via ``enabled`` parameter file.  Writing of the new values to proper
-parameter values should be done before the re-enablement.
-
 Below are the description of each parameter.
 
 enabled
@@ -268,4 +262,4 @@ granularity reclamation. ::
 
 .. [1] https://research.google/pubs/pub48551/
 .. [2] https://lwn.net/Articles/787611/
-.. [3] https://www.kernel.org/doc/html/latest/vm/free_page_reporting.html
+.. [3] https://www.kernel.org/doc/html/latest/mm/free_page_reporting.html
index 1bb7b72..d52f572 100644 (file)
@@ -30,11 +30,11 @@ DAMON provides below interfaces for different users.
   <sysfs_interface>`.  This will be removed after next LTS kernel is released,
   so users should move to the :ref:`sysfs interface <sysfs_interface>`.
 - *Kernel Space Programming Interface.*
-  :doc:`This </vm/damon/api>` is for kernel space programmers.  Using this,
+  :doc:`This </mm/damon/api>` is for kernel space programmers.  Using this,
   users can utilize every feature of DAMON most flexibly and efficiently by
   writing kernel space DAMON application programs for you.  You can even extend
   DAMON for various address spaces.  For detail, please refer to the interface
-  :doc:`document </vm/damon/api>`.
+  :doc:`document </mm/damon/api>`.
 
 .. _sysfs_interface:
 
@@ -185,7 +185,7 @@ controls the monitoring overhead, exist.  You can set and get the values by
 writing to and rading from the files.
 
 For more details about the intervals and monitoring regions range, please refer
-to the Design document (:doc:`/vm/damon/design`).
+to the Design document (:doc:`/mm/damon/design`).
 
 contexts/<N>/targets/
 ---------------------
@@ -264,6 +264,8 @@ that can be written to and read from the file and their meaning are as below.
  - ``pageout``: Call ``madvise()`` for the region with ``MADV_PAGEOUT``
  - ``hugepage``: Call ``madvise()`` for the region with ``MADV_HUGEPAGE``
  - ``nohugepage``: Call ``madvise()`` for the region with ``MADV_NOHUGEPAGE``
+ - ``lru_prio``: Prioritize the region on its LRU lists.
+ - ``lru_deprio``: Deprioritize the region on its LRU lists.
  - ``stat``: Do nothing but count the statistics
 
 schemes/<N>/access_pattern/
@@ -402,7 +404,7 @@ Attributes
 Users can get and set the ``sampling interval``, ``aggregation interval``,
 ``update interval``, and min/max number of monitoring target regions by
 reading from and writing to the ``attrs`` file.  To know about the monitoring
-attributes in detail, please refer to the :doc:`/vm/damon/design`.  For
+attributes in detail, please refer to the :doc:`/mm/damon/design`.  For
 example, below commands set those values to 5 ms, 100 ms, 1,000 ms, 10 and
 1000, and then check it again::
 
index c21b582..1bd1111 100644 (file)
@@ -36,6 +36,7 @@ the Linux memory management.
    numa_memory_policy
    numaperf
    pagemap
+   shrinker_debugfs
    soft-dirty
    swap_numa
    transhuge
diff --git a/Documentation/admin-guide/mm/shrinker_debugfs.rst b/Documentation/admin-guide/mm/shrinker_debugfs.rst
new file mode 100644 (file)
index 0000000..3887f0b
--- /dev/null
@@ -0,0 +1,135 @@
+.. _shrinker_debugfs:
+
+==========================
+Shrinker Debugfs Interface
+==========================
+
+Shrinker debugfs interface provides a visibility into the kernel memory
+shrinkers subsystem and allows to get information about individual shrinkers
+and interact with them.
+
+For each shrinker registered in the system a directory in **<debugfs>/shrinker/**
+is created. The directory's name is composed from the shrinker's name and an
+unique id: e.g. *kfree_rcu-0* or *sb-xfs:vda1-36*.
+
+Each shrinker directory contains **count** and **scan** files, which allow to
+trigger *count_objects()* and *scan_objects()* callbacks for each memcg and
+numa node (if applicable).
+
+Usage:
+------
+
+1. *List registered shrinkers*
+
+  ::
+
+    $ cd /sys/kernel/debug/shrinker/
+    $ ls
+    dquota-cache-16     sb-devpts-28     sb-proc-47       sb-tmpfs-42
+    mm-shadow-18        sb-devtmpfs-5    sb-proc-48       sb-tmpfs-43
+    mm-zspool:zram0-34  sb-hugetlbfs-17  sb-pstore-31     sb-tmpfs-44
+    rcu-kfree-0         sb-hugetlbfs-33  sb-rootfs-2      sb-tmpfs-49
+    sb-aio-20           sb-iomem-12      sb-securityfs-6  sb-tracefs-13
+    sb-anon_inodefs-15  sb-mqueue-21     sb-selinuxfs-22  sb-xfs:vda1-36
+    sb-bdev-3           sb-nsfs-4        sb-sockfs-8      sb-zsmalloc-19
+    sb-bpf-32           sb-pipefs-14     sb-sysfs-26      thp-deferred_split-10
+    sb-btrfs:vda2-24    sb-proc-25       sb-tmpfs-1       thp-zero-9
+    sb-cgroup2-30       sb-proc-39       sb-tmpfs-27      xfs-buf:vda1-37
+    sb-configfs-23      sb-proc-41       sb-tmpfs-29      xfs-inodegc:vda1-38
+    sb-dax-11           sb-proc-45       sb-tmpfs-35
+    sb-debugfs-7        sb-proc-46       sb-tmpfs-40
+
+2. *Get information about a specific shrinker*
+
+  ::
+
+    $ cd sb-btrfs\:vda2-24/
+    $ ls
+    count            scan
+
+3. *Count objects*
+
+  Each line in the output has the following format::
+
+    <cgroup inode id> <nr of objects on node 0> <nr of objects on node 1> ...
+    <cgroup inode id> <nr of objects on node 0> <nr of objects on node 1> ...
+    ...
+
+  If there are no objects on all numa nodes, a line is omitted. If there
+  are no objects at all, the output might be empty.
+
+  If the shrinker is not memcg-aware or CONFIG_MEMCG is off, 0 is printed
+  as cgroup inode id. If the shrinker is not numa-aware, 0's are printed
+  for all nodes except the first one.
+  ::
+
+    $ cat count
+    1 224 2
+    21 98 0
+    55 818 10
+    2367 2 0
+    2401 30 0
+    225 13 0
+    599 35 0
+    939 124 0
+    1041 3 0
+    1075 1 0
+    1109 1 0
+    1279 60 0
+    1313 7 0
+    1347 39 0
+    1381 3 0
+    1449 14 0
+    1483 63 0
+    1517 53 0
+    1551 6 0
+    1585 1 0
+    1619 6 0
+    1653 40 0
+    1687 11 0
+    1721 8 0
+    1755 4 0
+    1789 52 0
+    1823 888 0
+    1857 1 0
+    1925 2 0
+    1959 32 0
+    2027 22 0
+    2061 9 0
+    2469 799 0
+    2537 861 0
+    2639 1 0
+    2707 70 0
+    2775 4 0
+    2877 84 0
+    293 1 0
+    735 8 0
+
+4. *Scan objects*
+
+  The expected input format::
+
+    <cgroup inode id> <numa id> <number of objects to scan>
+
+  For a non-memcg-aware shrinker or on a system with no memory
+  cgrups **0** should be passed as cgroup id.
+  ::
+
+    $ cd /sys/kernel/debug/shrinker/
+    $ cd sb-btrfs\:vda2-24/
+
+    $ cat count | head -n 5
+    1 212 0
+    21 97 0
+    55 802 5
+    2367 2 0
+    225 13 0
+
+    $ echo "55 0 200" > scan
+
+    $ cat count | head -n 5
+    1 212 0
+    21 96 0
+    55 752 5
+    2367 2 0
+    225 13 0
index 5c9aa17..f74f722 100644 (file)
@@ -565,9 +565,8 @@ See Documentation/admin-guide/mm/hugetlbpage.rst
 hugetlb_optimize_vmemmap
 ========================
 
-This knob is not available when memory_hotplug.memmap_on_memory (kernel parameter)
-is configured or the size of 'struct page' (a structure defined in
-include/linux/mm_types.h) is not power of two (an unusual system config could
+This knob is not available when the size of 'struct page' (a structure defined
+in include/linux/mm_types.h) is not power of two (an unusual system config could
 result in this).
 
 Enable (set to 1) or disable (set to 0) the feature of optimizing vmemmap pages
@@ -760,7 +759,7 @@ and don't use much of it.
 
 The default value is 0.
 
-See Documentation/vm/overcommit-accounting.rst and
+See Documentation/mm/overcommit-accounting.rst and
 mm/util.c::__vm_enough_memory() for more information.
 
 
index 726065a..dc95df4 100644 (file)
@@ -86,7 +86,7 @@ Memory management
 =================
 
 How to allocate and use memory in the kernel.  Note that there is a lot
-more memory-management documentation in Documentation/vm/index.rst.
+more memory-management documentation in Documentation/mm/index.rst.
 
 .. toctree::
    :maxdepth: 1
index 1c935f4..5483fd3 100644 (file)
@@ -174,7 +174,6 @@ mapping:
 
 - ``kmemleak_alloc_phys``
 - ``kmemleak_free_part_phys``
-- ``kmemleak_not_leak_phys``
 - ``kmemleak_ignore_phys``
 
 Dealing with false positives/negatives
index 1bc91fb..e7aafc8 100644 (file)
@@ -448,6 +448,7 @@ Memory Area, or VMA) there is a series of lines such as the following::
     MMUPageSize:           4 kB
     Rss:                 892 kB
     Pss:                 374 kB
+    Pss_Dirty:             0 kB
     Shared_Clean:        892 kB
     Shared_Dirty:          0 kB
     Private_Clean:         0 kB
@@ -479,7 +480,9 @@ dirty shared and private pages in the mapping.
 The "proportional set size" (PSS) of a process is the count of pages it has
 in memory, where each page is divided by the number of processes sharing it.
 So if a process has 1000 pages all to itself, and 1000 shared with one other
-process, its PSS will be 1500.
+process, its PSS will be 1500.  "Pss_Dirty" is the portion of PSS which
+consists of dirty pages.  ("Pss_Clean" is not included, but it can be
+calculated by subtracting "Pss_Dirty" from "Pss".)
 
 Note that even a page which is part of a MAP_SHARED mapping, but has only
 a single pte mapped, i.e.  is currently used by only one process, is accounted
@@ -514,8 +517,10 @@ replaced by copy-on-write) part of the underlying shmem object out on swap.
 "SwapPss" shows proportional swap share of this mapping. Unlike "Swap", this
 does not take into account swapped out page of underlying shmem objects.
 "Locked" indicates whether the mapping is locked in memory or not.
+
 "THPeligible" indicates whether the mapping is eligible for allocating THP
-pages - 1 if true, 0 otherwise. It just shows the current status.
+pages as well as the THP is PMD mappable or not - 1 if true, 0 otherwise.
+It just shows the current status.
 
 "VmFlags" field deserves a separate description. This member represents the
 kernel flags associated with the particular virtual memory area in two letter
@@ -1109,7 +1114,7 @@ CommitLimit
               yield a CommitLimit of 7.3G.
 
               For more details, see the memory overcommit documentation
-              in vm/overcommit-accounting.
+              in mm/overcommit-accounting.
 Committed_AS
               The amount of memory presently allocated on the system.
               The committed memory is a sum of all of the memory which
index 67036a0..4737c18 100644 (file)
@@ -128,7 +128,7 @@ needed).
    sound/index
    crypto/index
    filesystems/index
-   vm/index
+   mm/index
    bpf/index
    usb/index
    PCI/index
diff --git a/Documentation/mm/active_mm.rst b/Documentation/mm/active_mm.rst
new file mode 100644 (file)
index 0000000..6f8269c
--- /dev/null
@@ -0,0 +1,91 @@
+.. _active_mm:
+
+=========
+Active MM
+=========
+
+::
+
+ List:       linux-kernel
+ Subject:    Re: active_mm
+ From:       Linus Torvalds <torvalds () transmeta ! com>
+ Date:       1999-07-30 21:36:24
+
+ Cc'd to linux-kernel, because I don't write explanations all that often,
+ and when I do I feel better about more people reading them.
+
+ On Fri, 30 Jul 1999, David Mosberger wrote:
+ >
+ > Is there a brief description someplace on how "mm" vs. "active_mm" in
+ > the task_struct are supposed to be used?  (My apologies if this was
+ > discussed on the mailing lists---I just returned from vacation and
+ > wasn't able to follow linux-kernel for a while).
+
+ Basically, the new setup is:
+
+  - we have "real address spaces" and "anonymous address spaces". The
+    difference is that an anonymous address space doesn't care about the
+    user-level page tables at all, so when we do a context switch into an
+    anonymous address space we just leave the previous address space
+    active.
+
+    The obvious use for a "anonymous address space" is any thread that
+    doesn't need any user mappings - all kernel threads basically fall into
+    this category, but even "real" threads can temporarily say that for
+    some amount of time they are not going to be interested in user space,
+    and that the scheduler might as well try to avoid wasting time on
+    switching the VM state around. Currently only the old-style bdflush
+    sync does that.
+
+  - "tsk->mm" points to the "real address space". For an anonymous process,
+    tsk->mm will be NULL, for the logical reason that an anonymous process
+    really doesn't _have_ a real address space at all.
+
+  - however, we obviously need to keep track of which address space we
+    "stole" for such an anonymous user. For that, we have "tsk->active_mm",
+    which shows what the currently active address space is.
+
+    The rule is that for a process with a real address space (ie tsk->mm is
+    non-NULL) the active_mm obviously always has to be the same as the real
+    one.
+
+    For a anonymous process, tsk->mm == NULL, and tsk->active_mm is the
+    "borrowed" mm while the anonymous process is running. When the
+    anonymous process gets scheduled away, the borrowed address space is
+    returned and cleared.
+
+ To support all that, the "struct mm_struct" now has two counters: a
+ "mm_users" counter that is how many "real address space users" there are,
+ and a "mm_count" counter that is the number of "lazy" users (ie anonymous
+ users) plus one if there are any real users.
+
+ Usually there is at least one real user, but it could be that the real
+ user exited on another CPU while a lazy user was still active, so you do
+ actually get cases where you have a address space that is _only_ used by
+ lazy users. That is often a short-lived state, because once that thread
+ gets scheduled away in favour of a real thread, the "zombie" mm gets
+ released because "mm_count" becomes zero.
+
+ Also, a new rule is that _nobody_ ever has "init_mm" as a real MM any
+ more. "init_mm" should be considered just a "lazy context when no other
+ context is available", and in fact it is mainly used just at bootup when
+ no real VM has yet been created. So code that used to check
+
+       if (current->mm == &init_mm)
+
+ should generally just do
+
+       if (!current->mm)
+
+ instead (which makes more sense anyway - the test is basically one of "do
+ we have a user context", and is generally done by the page fault handler
+ and things like that).
+
+ Anyway, I put a pre-patch-2.3.13-1 on ftp.kernel.org just a moment ago,
+ because it slightly changes the interfaces to accommodate the alpha (who
+ would have thought it, but the alpha actually ends up having one of the
+ ugliest context switch codes - unlike the other architectures where the MM
+ and register state is separate, the alpha PALcode joins the two, and you
+ need to switch both together).
+
+ (From http://marc.info/?l=linux-kernel&m=93337278602211&w=2)
diff --git a/Documentation/mm/arch_pgtable_helpers.rst b/Documentation/mm/arch_pgtable_helpers.rst
new file mode 100644 (file)
index 0000000..cbaee9e
--- /dev/null
@@ -0,0 +1,260 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. _arch_page_table_helpers:
+
+===============================
+Architecture Page Table Helpers
+===============================
+
+Generic MM expects architectures (with MMU) to provide helpers to create, access
+and modify page table entries at various level for different memory functions.
+These page table helpers need to conform to a common semantics across platforms.
+Following tables describe the expected semantics which can also be tested during
+boot via CONFIG_DEBUG_VM_PGTABLE option. All future changes in here or the debug
+test need to be in sync.
+
+
+PTE Page Table Helpers
+======================
+
++---------------------------+--------------------------------------------------+
+| pte_same                  | Tests whether both PTE entries are the same      |
++---------------------------+--------------------------------------------------+
+| pte_bad                   | Tests a non-table mapped PTE                     |
++---------------------------+--------------------------------------------------+
+| pte_present               | Tests a valid mapped PTE                         |
++---------------------------+--------------------------------------------------+
+| pte_young                 | Tests a young PTE                                |
++---------------------------+--------------------------------------------------+
+| pte_dirty                 | Tests a dirty PTE                                |
++---------------------------+--------------------------------------------------+
+| pte_write                 | Tests a writable PTE                             |
++---------------------------+--------------------------------------------------+
+| pte_special               | Tests a special PTE                              |
++---------------------------+--------------------------------------------------+
+| pte_protnone              | Tests a PROT_NONE PTE                            |
++---------------------------+--------------------------------------------------+
+| pte_devmap                | Tests a ZONE_DEVICE mapped PTE                   |
++---------------------------+--------------------------------------------------+
+| pte_soft_dirty            | Tests a soft dirty PTE                           |
++---------------------------+--------------------------------------------------+
+| pte_swp_soft_dirty        | Tests a soft dirty swapped PTE                   |
++---------------------------+--------------------------------------------------+
+| pte_mkyoung               | Creates a young PTE                              |
++---------------------------+--------------------------------------------------+
+| pte_mkold                 | Creates an old PTE                               |
++---------------------------+--------------------------------------------------+
+| pte_mkdirty               | Creates a dirty PTE                              |
++---------------------------+--------------------------------------------------+
+| pte_mkclean               | Creates a clean PTE                              |
++---------------------------+--------------------------------------------------+
+| pte_mkwrite               | Creates a writable PTE                           |
++---------------------------+--------------------------------------------------+
+| pte_wrprotect             | Creates a write protected PTE                    |
++---------------------------+--------------------------------------------------+
+| pte_mkspecial             | Creates a special PTE                            |
++---------------------------+--------------------------------------------------+
+| pte_mkdevmap              | Creates a ZONE_DEVICE mapped PTE                 |
++---------------------------+--------------------------------------------------+
+| pte_mksoft_dirty          | Creates a soft dirty PTE                         |
++---------------------------+--------------------------------------------------+
+| pte_clear_soft_dirty      | Clears a soft dirty PTE                          |
++---------------------------+--------------------------------------------------+
+| pte_swp_mksoft_dirty      | Creates a soft dirty swapped PTE                 |
++---------------------------+--------------------------------------------------+
+| pte_swp_clear_soft_dirty  | Clears a soft dirty swapped PTE                  |
++---------------------------+--------------------------------------------------+
+| pte_mknotpresent          | Invalidates a mapped PTE                         |
++---------------------------+--------------------------------------------------+
+| ptep_clear                | Clears a PTE                                     |
++---------------------------+--------------------------------------------------+
+| ptep_get_and_clear        | Clears and returns PTE                           |
++---------------------------+--------------------------------------------------+
+| ptep_get_and_clear_full   | Clears and returns PTE (batched PTE unmap)       |
++---------------------------+--------------------------------------------------+
+| ptep_test_and_clear_young | Clears young from a PTE                          |
++---------------------------+--------------------------------------------------+
+| ptep_set_wrprotect        | Converts into a write protected PTE              |
++---------------------------+--------------------------------------------------+
+| ptep_set_access_flags     | Converts into a more permissive PTE              |
++---------------------------+--------------------------------------------------+
+
+
+PMD Page Table Helpers
+======================
+
++---------------------------+--------------------------------------------------+
+| pmd_same                  | Tests whether both PMD entries are the same      |
++---------------------------+--------------------------------------------------+
+| pmd_bad                   | Tests a non-table mapped PMD                     |
++---------------------------+--------------------------------------------------+
+| pmd_leaf                  | Tests a leaf mapped PMD                          |
++---------------------------+--------------------------------------------------+
+| pmd_huge                  | Tests a HugeTLB mapped PMD                       |
++---------------------------+--------------------------------------------------+
+| pmd_trans_huge            | Tests a Transparent Huge Page (THP) at PMD       |
++---------------------------+--------------------------------------------------+
+| pmd_present               | Tests a valid mapped PMD                         |
++---------------------------+--------------------------------------------------+
+| pmd_young                 | Tests a young PMD                                |
++---------------------------+--------------------------------------------------+
+| pmd_dirty                 | Tests a dirty PMD                                |
++---------------------------+--------------------------------------------------+
+| pmd_write                 | Tests a writable PMD                             |
++---------------------------+--------------------------------------------------+
+| pmd_special               | Tests a special PMD                              |
++---------------------------+--------------------------------------------------+
+| pmd_protnone              | Tests a PROT_NONE PMD                            |
++---------------------------+--------------------------------------------------+
+| pmd_devmap                | Tests a ZONE_DEVICE mapped PMD                   |
++---------------------------+--------------------------------------------------+
+| pmd_soft_dirty            | Tests a soft dirty PMD                           |
++---------------------------+--------------------------------------------------+
+| pmd_swp_soft_dirty        | Tests a soft dirty swapped PMD                   |
++---------------------------+--------------------------------------------------+
+| pmd_mkyoung               | Creates a young PMD                              |
++---------------------------+--------------------------------------------------+
+| pmd_mkold                 | Creates an old PMD                               |
++---------------------------+--------------------------------------------------+
+| pmd_mkdirty               | Creates a dirty PMD                              |
++---------------------------+--------------------------------------------------+
+| pmd_mkclean               | Creates a clean PMD                              |
++---------------------------+--------------------------------------------------+
+| pmd_mkwrite               | Creates a writable PMD                           |
++---------------------------+--------------------------------------------------+
+| pmd_wrprotect             | Creates a write protected PMD                    |
++---------------------------+--------------------------------------------------+
+| pmd_mkspecial             | Creates a special PMD                            |
++---------------------------+--------------------------------------------------+
+| pmd_mkdevmap              | Creates a ZONE_DEVICE mapped PMD                 |
++---------------------------+--------------------------------------------------+
+| pmd_mksoft_dirty          | Creates a soft dirty PMD                         |
++---------------------------+--------------------------------------------------+
+| pmd_clear_soft_dirty      | Clears a soft dirty PMD                          |
++---------------------------+--------------------------------------------------+
+| pmd_swp_mksoft_dirty      | Creates a soft dirty swapped PMD                 |
++---------------------------+--------------------------------------------------+
+| pmd_swp_clear_soft_dirty  | Clears a soft dirty swapped PMD                  |
++---------------------------+--------------------------------------------------+
+| pmd_mkinvalid             | Invalidates a mapped PMD [1]                     |
++---------------------------+--------------------------------------------------+
+| pmd_set_huge              | Creates a PMD huge mapping                       |
++---------------------------+--------------------------------------------------+
+| pmd_clear_huge            | Clears a PMD huge mapping                        |
++---------------------------+--------------------------------------------------+
+| pmdp_get_and_clear        | Clears a PMD                                     |
++---------------------------+--------------------------------------------------+
+| pmdp_get_and_clear_full   | Clears a PMD                                     |
++---------------------------+--------------------------------------------------+
+| pmdp_test_and_clear_young | Clears young from a PMD                          |
++---------------------------+--------------------------------------------------+
+| pmdp_set_wrprotect        | Converts into a write protected PMD              |
++---------------------------+--------------------------------------------------+
+| pmdp_set_access_flags     | Converts into a more permissive PMD              |
++---------------------------+--------------------------------------------------+
+
+
+PUD Page Table Helpers
+======================
+
++---------------------------+--------------------------------------------------+
+| pud_same                  | Tests whether both PUD entries are the same      |
++---------------------------+--------------------------------------------------+
+| pud_bad                   | Tests a non-table mapped PUD                     |
++---------------------------+--------------------------------------------------+
+| pud_leaf                  | Tests a leaf mapped PUD                          |
++---------------------------+--------------------------------------------------+
+| pud_huge                  | Tests a HugeTLB mapped PUD                       |
++---------------------------+--------------------------------------------------+
+| pud_trans_huge            | Tests a Transparent Huge Page (THP) at PUD       |
++---------------------------+--------------------------------------------------+
+| pud_present               | Tests a valid mapped PUD                         |
++---------------------------+--------------------------------------------------+
+| pud_young                 | Tests a young PUD                                |
++---------------------------+--------------------------------------------------+
+| pud_dirty                 | Tests a dirty PUD                                |
++---------------------------+--------------------------------------------------+
+| pud_write                 | Tests a writable PUD                             |
++---------------------------+--------------------------------------------------+
+| pud_devmap                | Tests a ZONE_DEVICE mapped PUD                   |
++---------------------------+--------------------------------------------------+
+| pud_mkyoung               | Creates a young PUD                              |
++---------------------------+--------------------------------------------------+
+| pud_mkold                 | Creates an old PUD                               |
++---------------------------+--------------------------------------------------+
+| pud_mkdirty               | Creates a dirty PUD                              |
++---------------------------+--------------------------------------------------+
+| pud_mkclean               | Creates a clean PUD                              |
++---------------------------+--------------------------------------------------+
+| pud_mkwrite               | Creates a writable PUD                           |
++---------------------------+--------------------------------------------------+
+| pud_wrprotect             | Creates a write protected PUD                    |
++---------------------------+--------------------------------------------------+
+| pud_mkdevmap              | Creates a ZONE_DEVICE mapped PUD                 |
++---------------------------+--------------------------------------------------+
+| pud_mkinvalid             | Invalidates a mapped PUD [1]                     |
++---------------------------+--------------------------------------------------+
+| pud_set_huge              | Creates a PUD huge mapping                       |
++---------------------------+--------------------------------------------------+
+| pud_clear_huge            | Clears a PUD huge mapping                        |
++---------------------------+--------------------------------------------------+
+| pudp_get_and_clear        | Clears a PUD                                     |
++---------------------------+--------------------------------------------------+
+| pudp_get_and_clear_full   | Clears a PUD                                     |
++---------------------------+--------------------------------------------------+
+| pudp_test_and_clear_young | Clears young from a PUD                          |
++---------------------------+--------------------------------------------------+
+| pudp_set_wrprotect        | Converts into a write protected PUD              |
++---------------------------+--------------------------------------------------+
+| pudp_set_access_flags     | Converts into a more permissive PUD              |
++---------------------------+--------------------------------------------------+
+
+
+HugeTLB Page Table Helpers
+==========================
+
++---------------------------+--------------------------------------------------+
+| pte_huge                  | Tests a HugeTLB                                  |
++---------------------------+--------------------------------------------------+
+| pte_mkhuge                | Creates a HugeTLB                                |
++---------------------------+--------------------------------------------------+
+| huge_pte_dirty            | Tests a dirty HugeTLB                            |
++---------------------------+--------------------------------------------------+
+| huge_pte_write            | Tests a writable HugeTLB                         |
++---------------------------+--------------------------------------------------+
+| huge_pte_mkdirty          | Creates a dirty HugeTLB                          |
++---------------------------+--------------------------------------------------+
+| huge_pte_mkwrite          | Creates a writable HugeTLB                       |
++---------------------------+--------------------------------------------------+
+| huge_pte_wrprotect        | Creates a write protected HugeTLB                |
++---------------------------+--------------------------------------------------+
+| huge_ptep_get_and_clear   | Clears a HugeTLB                                 |
++---------------------------+--------------------------------------------------+
+| huge_ptep_set_wrprotect   | Converts into a write protected HugeTLB          |
++---------------------------+--------------------------------------------------+
+| huge_ptep_set_access_flags  | Converts into a more permissive HugeTLB        |
++---------------------------+--------------------------------------------------+
+
+
+SWAP Page Table Helpers
+========================
+
++---------------------------+--------------------------------------------------+
+| __pte_to_swp_entry        | Creates a swapped entry (arch) from a mapped PTE |
++---------------------------+--------------------------------------------------+
+| __swp_to_pte_entry        | Creates a mapped PTE from a swapped entry (arch) |
++---------------------------+--------------------------------------------------+
+| __pmd_to_swp_entry        | Creates a swapped entry (arch) from a mapped PMD |
++---------------------------+--------------------------------------------------+
+| __swp_to_pmd_entry        | Creates a mapped PMD from a swapped entry (arch) |
++---------------------------+--------------------------------------------------+
+| is_migration_entry        | Tests a migration (read or write) swapped entry  |
++-------------------------------+----------------------------------------------+
+| is_writable_migration_entry   | Tests a write migration swapped entry        |
++-------------------------------+----------------------------------------------+
+| make_readable_migration_entry | Creates a read migration swapped entry       |
++-------------------------------+----------------------------------------------+
+| make_writable_migration_entry | Creates a write migration swapped entry      |
++-------------------------------+----------------------------------------------+
+
+[1] https://lore.kernel.org/linux-mm/20181017020930.GN30832@redhat.com/
diff --git a/Documentation/mm/balance.rst b/Documentation/mm/balance.rst
new file mode 100644 (file)
index 0000000..6a1fadf
--- /dev/null
@@ -0,0 +1,102 @@
+.. _balance:
+
+================
+Memory Balancing
+================
+
+Started Jan 2000 by Kanoj Sarcar <kanoj@sgi.com>
+
+Memory balancing is needed for !__GFP_ATOMIC and !__GFP_KSWAPD_RECLAIM as
+well as for non __GFP_IO allocations.
+
+The first reason why a caller may avoid reclaim is that the caller can not
+sleep due to holding a spinlock or is in interrupt context. The second may
+be that the caller is willing to fail the allocation without incurring the
+overhead of page reclaim. This may happen for opportunistic high-order
+allocation requests that have order-0 fallback options. In such cases,
+the caller may also wish to avoid waking kswapd.
+
+__GFP_IO allocation requests are made to prevent file system deadlocks.
+
+In the absence of non sleepable allocation requests, it seems detrimental
+to be doing balancing. Page reclamation can be kicked off lazily, that
+is, only when needed (aka zone free memory is 0), instead of making it
+a proactive process.
+
+That being said, the kernel should try to fulfill requests for direct
+mapped pages from the direct mapped pool, instead of falling back on
+the dma pool, so as to keep the dma pool filled for dma requests (atomic
+or not). A similar argument applies to highmem and direct mapped pages.
+OTOH, if there is a lot of free dma pages, it is preferable to satisfy
+regular memory requests by allocating one from the dma pool, instead
+of incurring the overhead of regular zone balancing.
+
+In 2.2, memory balancing/page reclamation would kick off only when the
+_total_ number of free pages fell below 1/64 th of total memory. With the
+right ratio of dma and regular memory, it is quite possible that balancing
+would not be done even when the dma zone was completely empty. 2.2 has
+been running production machines of varying memory sizes, and seems to be
+doing fine even with the presence of this problem. In 2.3, due to
+HIGHMEM, this problem is aggravated.
+
+In 2.3, zone balancing can be done in one of two ways: depending on the
+zone size (and possibly of the size of lower class zones), we can decide
+at init time how many free pages we should aim for while balancing any
+zone. The good part is, while balancing, we do not need to look at sizes
+of lower class zones, the bad part is, we might do too frequent balancing
+due to ignoring possibly lower usage in the lower class zones. Also,
+with a slight change in the allocation routine, it is possible to reduce
+the memclass() macro to be a simple equality.
+
+Another possible solution is that we balance only when the free memory
+of a zone _and_ all its lower class zones falls below 1/64th of the
+total memory in the zone and its lower class zones. This fixes the 2.2
+balancing problem, and stays as close to 2.2 behavior as possible. Also,
+the balancing algorithm works the same way on the various architectures,
+which have different numbers and types of zones. If we wanted to get
+fancy, we could assign different weights to free pages in different
+zones in the future.
+
+Note that if the size of the regular zone is huge compared to dma zone,
+it becomes less significant to consider the free dma pages while
+deciding whether to balance the regular zone. The first solution
+becomes more attractive then.
+
+The appended patch implements the second solution. It also "fixes" two
+problems: first, kswapd is woken up as in 2.2 on low memory conditions
+for non-sleepable allocations. Second, the HIGHMEM zone is also balanced,
+so as to give a fighting chance for replace_with_highmem() to get a
+HIGHMEM page, as well as to ensure that HIGHMEM allocations do not
+fall back into regular zone. This also makes sure that HIGHMEM pages
+are not leaked (for example, in situations where a HIGHMEM page is in
+the swapcache but is not being used by anyone)
+
+kswapd also needs to know about the zones it should balance. kswapd is
+primarily needed in a situation where balancing can not be done,
+probably because all allocation requests are coming from intr context
+and all process contexts are sleeping. For 2.3, kswapd does not really
+need to balance the highmem zone, since intr context does not request
+highmem pages. kswapd looks at the zone_wake_kswapd field in the zone
+structure to decide whether a zone needs balancing.
+
+Page stealing from process memory and shm is done if stealing the page would
+alleviate memory pressure on any zone in the page's node that has fallen below
+its watermark.
+
+watemark[WMARK_MIN/WMARK_LOW/WMARK_HIGH]/low_on_memory/zone_wake_kswapd: These
+are per-zone fields, used to determine when a zone needs to be balanced. When
+the number of pages falls below watermark[WMARK_MIN], the hysteric field
+low_on_memory gets set. This stays set till the number of free pages becomes
+watermark[WMARK_HIGH]. When low_on_memory is set, page allocation requests will
+try to free some pages in the zone (providing GFP_WAIT is set in the request).
+Orthogonal to this, is the decision to poke kswapd to free some zone pages.
+That decision is not hysteresis based, and is done when the number of free
+pages is below watermark[WMARK_LOW]; in which case zone_wake_kswapd is also set.
+
+
+(Good) Ideas that I have heard:
+
+1. Dynamic experience should influence balancing: number of failed requests
+   for a zone can be tracked and fed into the balancing scheme (jalvo@mbay.net)
+2. Implement a replace_with_highmem()-like replace_with_regular() to preserve
+   dma pages. (lkd@tantalophile.demon.co.uk)
diff --git a/Documentation/mm/bootmem.rst b/Documentation/mm/bootmem.rst
new file mode 100644 (file)
index 0000000..eb2b31e
--- /dev/null
@@ -0,0 +1,5 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========
+Boot Memory
+===========
diff --git a/Documentation/mm/damon/api.rst b/Documentation/mm/damon/api.rst
new file mode 100644 (file)
index 0000000..08f34df
--- /dev/null
@@ -0,0 +1,20 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=============
+API Reference
+=============
+
+Kernel space programs can use every feature of DAMON using below APIs.  All you
+need to do is including ``damon.h``, which is located in ``include/linux/`` of
+the source tree.
+
+Structures
+==========
+
+.. kernel-doc:: include/linux/damon.h
+
+
+Functions
+=========
+
+.. kernel-doc:: mm/damon/core.c
diff --git a/Documentation/mm/damon/design.rst b/Documentation/mm/damon/design.rst
new file mode 100644 (file)
index 0000000..0cff6fa
--- /dev/null
@@ -0,0 +1,176 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======
+Design
+======
+
+Configurable Layers
+===================
+
+DAMON provides data access monitoring functionality while making the accuracy
+and the overhead controllable.  The fundamental access monitorings require
+primitives that dependent on and optimized for the target address space.  On
+the other hand, the accuracy and overhead tradeoff mechanism, which is the core
+of DAMON, is in the pure logic space.  DAMON separates the two parts in
+different layers and defines its interface to allow various low level
+primitives implementations configurable with the core logic.  We call the low
+level primitives implementations monitoring operations.
+
+Due to this separated design and the configurable interface, users can extend
+DAMON for any address space by configuring the core logics with appropriate
+monitoring operations.  If appropriate one is not provided, users can implement
+the operations on their own.
+
+For example, physical memory, virtual memory, swap space, those for specific
+processes, NUMA nodes, files, and backing memory devices would be supportable.
+Also, if some architectures or devices support special optimized access check
+primitives, those will be easily configurable.
+
+
+Reference Implementations of Address Space Specific Monitoring Operations
+=========================================================================
+
+The monitoring operations are defined in two parts:
+
+1. Identification of the monitoring target address range for the address space.
+2. Access check of specific address range in the target space.
+
+DAMON currently provides the implementations of the operations for the physical
+and virtual address spaces. Below two subsections describe how those work.
+
+
+VMA-based Target Address Range Construction
+-------------------------------------------
+
+This is only for the virtual address space monitoring operations
+implementation.  That for the physical address space simply asks users to
+manually set the monitoring target address ranges.
+
+Only small parts in the super-huge virtual address space of the processes are
+mapped to the physical memory and accessed.  Thus, tracking the unmapped
+address regions is just wasteful.  However, because DAMON can deal with some
+level of noise using the adaptive regions adjustment mechanism, tracking every
+mapping is not strictly required but could even incur a high overhead in some
+cases.  That said, too huge unmapped areas inside the monitoring target should
+be removed to not take the time for the adaptive mechanism.
+
+For the reason, this implementation converts the complex mappings to three
+distinct regions that cover every mapped area of the address space.  The two
+gaps between the three regions are the two biggest unmapped areas in the given
+address space.  The two biggest unmapped areas would be the gap between the
+heap and the uppermost mmap()-ed region, and the gap between the lowermost
+mmap()-ed region and the stack in most of the cases.  Because these gaps are
+exceptionally huge in usual address spaces, excluding these will be sufficient
+to make a reasonable trade-off.  Below shows this in detail::
+
+    <heap>
+    <BIG UNMAPPED REGION 1>
+    <uppermost mmap()-ed region>
+    (small mmap()-ed regions and munmap()-ed regions)
+    <lowermost mmap()-ed region>
+    <BIG UNMAPPED REGION 2>
+    <stack>
+
+
+PTE Accessed-bit Based Access Check
+-----------------------------------
+
+Both of the implementations for physical and virtual address spaces use PTE
+Accessed-bit for basic access checks.  Only one difference is the way of
+finding the relevant PTE Accessed bit(s) from the address.  While the
+implementation for the virtual address walks the page table for the target task
+of the address, the implementation for the physical address walks every page
+table having a mapping to the address.  In this way, the implementations find
+and clear the bit(s) for next sampling target address and checks whether the
+bit(s) set again after one sampling period.  This could disturb other kernel
+subsystems using the Accessed bits, namely Idle page tracking and the reclaim
+logic.  DAMON does nothing to avoid disturbing Idle page tracking, so handling
+the interference is the responsibility of sysadmins.  However, it solves the
+conflict with the reclaim logic using ``PG_idle`` and ``PG_young`` page flags,
+as Idle page tracking does.
+
+
+Address Space Independent Core Mechanisms
+=========================================
+
+Below four sections describe each of the DAMON core mechanisms and the five
+monitoring attributes, ``sampling interval``, ``aggregation interval``,
+``update interval``, ``minimum number of regions``, and ``maximum number of
+regions``.
+
+
+Access Frequency Monitoring
+---------------------------
+
+The output of DAMON says what pages are how frequently accessed for a given
+duration.  The resolution of the access frequency is controlled by setting
+``sampling interval`` and ``aggregation interval``.  In detail, DAMON checks
+access to each page per ``sampling interval`` and aggregates the results.  In
+other words, counts the number of the accesses to each page.  After each
+``aggregation interval`` passes, DAMON calls callback functions that previously
+registered by users so that users can read the aggregated results and then
+clears the results.  This can be described in below simple pseudo-code::
+
+    while monitoring_on:
+        for page in monitoring_target:
+            if accessed(page):
+                nr_accesses[page] += 1
+        if time() % aggregation_interval == 0:
+            for callback in user_registered_callbacks:
+                callback(monitoring_target, nr_accesses)
+            for page in monitoring_target:
+                nr_accesses[page] = 0
+        sleep(sampling interval)
+
+The monitoring overhead of this mechanism will arbitrarily increase as the
+size of the target workload grows.
+
+
+Region Based Sampling
+---------------------
+
+To avoid the unbounded increase of the overhead, DAMON groups adjacent pages
+that assumed to have the same access frequencies into a region.  As long as the
+assumption (pages in a region have the same access frequencies) is kept, only
+one page in the region is required to be checked.  Thus, for each ``sampling
+interval``, DAMON randomly picks one page in each region, waits for one
+``sampling interval``, checks whether the page is accessed meanwhile, and
+increases the access frequency of the region if so.  Therefore, the monitoring
+overhead is controllable by setting the number of regions.  DAMON allows users
+to set the minimum and the maximum number of regions for the trade-off.
+
+This scheme, however, cannot preserve the quality of the output if the
+assumption is not guaranteed.
+
+
+Adaptive Regions Adjustment
+---------------------------
+
+Even somehow the initial monitoring target regions are well constructed to
+fulfill the assumption (pages in same region have similar access frequencies),
+the data access pattern can be dynamically changed.  This will result in low
+monitoring quality.  To keep the assumption as much as possible, DAMON
+adaptively merges and splits each region based on their access frequency.
+
+For each ``aggregation interval``, it compares the access frequencies of
+adjacent regions and merges those if the frequency difference is small.  Then,
+after it reports and clears the aggregated access frequency of each region, it
+splits each region into two or three regions if the total number of regions
+will not exceed the user-specified maximum number of regions after the split.
+
+In this way, DAMON provides its best-effort quality and minimal overhead while
+keeping the bounds users set for their trade-off.
+
+
+Dynamic Target Space Updates Handling
+-------------------------------------
+
+The monitoring target address range could dynamically changed.  For example,
+virtual memory could be dynamically mapped and unmapped.  Physical memory could
+be hot-plugged.
+
+As the changes could be quite frequent in some cases, DAMON allows the
+monitoring operations to check dynamic changes including memory mapping changes
+and applies it to monitoring operations-related data structures such as the
+abstracted monitoring target memory area only for each of a user-specified time
+interval (``update interval``).
diff --git a/Documentation/mm/damon/faq.rst b/Documentation/mm/damon/faq.rst
new file mode 100644 (file)
index 0000000..dde7e24
--- /dev/null
@@ -0,0 +1,50 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==========================
+Frequently Asked Questions
+==========================
+
+Why a new subsystem, instead of extending perf or other user space tools?
+=========================================================================
+
+First, because it needs to be lightweight as much as possible so that it can be
+used online, any unnecessary overhead such as kernel - user space context
+switching cost should be avoided.  Second, DAMON aims to be used by other
+programs including the kernel.  Therefore, having a dependency on specific
+tools like perf is not desirable.  These are the two biggest reasons why DAMON
+is implemented in the kernel space.
+
+
+Can 'idle pages tracking' or 'perf mem' substitute DAMON?
+=========================================================
+
+Idle page tracking is a low level primitive for access check of the physical
+address space.  'perf mem' is similar, though it can use sampling to minimize
+the overhead.  On the other hand, DAMON is a higher-level framework for the
+monitoring of various address spaces.  It is focused on memory management
+optimization and provides sophisticated accuracy/overhead handling mechanisms.
+Therefore, 'idle pages tracking' and 'perf mem' could provide a subset of
+DAMON's output, but cannot substitute DAMON.
+
+
+Does DAMON support virtual memory only?
+=======================================
+
+No.  The core of the DAMON is address space independent.  The address space
+specific monitoring operations including monitoring target regions
+constructions and actual access checks can be implemented and configured on the
+DAMON core by the users.  In this way, DAMON users can monitor any address
+space with any access check technique.
+
+Nonetheless, DAMON provides vma/rmap tracking and PTE Accessed bit check based
+implementations of the address space dependent functions for the virtual memory
+and the physical memory by default, for a reference and convenient use.
+
+
+Can I simply monitor page granularity?
+======================================
+
+Yes.  You can do so by setting the ``min_nr_regions`` attribute higher than the
+working set size divided by the page size.  Because the monitoring target
+regions size is forced to be ``>=page size``, the region split will make no
+effect.
diff --git a/Documentation/mm/damon/index.rst b/Documentation/mm/damon/index.rst
new file mode 100644 (file)
index 0000000..48c0bbf
--- /dev/null
@@ -0,0 +1,29 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==========================
+DAMON: Data Access MONitor
+==========================
+
+DAMON is a data access monitoring framework subsystem for the Linux kernel.
+The core mechanisms of DAMON (refer to :doc:`design` for the detail) make it
+
+ - *accurate* (the monitoring output is useful enough for DRAM level memory
+   management; It might not appropriate for CPU Cache levels, though),
+ - *light-weight* (the monitoring overhead is low enough to be applied online),
+   and
+ - *scalable* (the upper-bound of the overhead is in constant range regardless
+   of the size of target workloads).
+
+Using this framework, therefore, the kernel's memory management mechanisms can
+make advanced decisions.  Experimental memory management optimization works
+that incurring high data accesses monitoring overhead could implemented again.
+In user space, meanwhile, users who have some special workloads can write
+personalized applications for better understanding and optimizations of their
+workloads and systems.
+
+.. toctree::
+   :maxdepth: 2
+
+   faq
+   design
+   api
diff --git a/Documentation/mm/free_page_reporting.rst b/Documentation/mm/free_page_reporting.rst
new file mode 100644 (file)
index 0000000..8c05e62
--- /dev/null
@@ -0,0 +1,40 @@
+.. _free_page_reporting:
+
+=====================
+Free Page Reporting
+=====================
+
+Free page reporting is an API by which a device can register to receive
+lists of pages that are currently unused by the system. This is useful in
+the case of virtualization where a guest is then able to use this data to
+notify the hypervisor that it is no longer using certain pages in memory.
+
+For the driver, typically a balloon driver, to use of this functionality
+it will allocate and initialize a page_reporting_dev_info structure. The
+field within the structure it will populate is the "report" function
+pointer used to process the scatterlist. It must also guarantee that it can
+handle at least PAGE_REPORTING_CAPACITY worth of scatterlist entries per
+call to the function. A call to page_reporting_register will register the
+page reporting interface with the reporting framework assuming no other
+page reporting devices are already registered.
+
+Once registered the page reporting API will begin reporting batches of
+pages to the driver. The API will start reporting pages 2 seconds after
+the interface is registered and will continue to do so 2 seconds after any
+page of a sufficiently high order is freed.
+
+Pages reported will be stored in the scatterlist passed to the reporting
+function with the final entry having the end bit set in entry nent - 1.
+While pages are being processed by the report function they will not be
+accessible to the allocator. Once the report function has been completed
+the pages will be returned to the free area from which they were obtained.
+
+Prior to removing a driver that is making use of free page reporting it
+is necessary to call page_reporting_unregister to have the
+page_reporting_dev_info structure that is currently in use by free page
+reporting removed. Doing this will prevent further reports from being
+issued via the interface. If another driver or the same driver is
+registered it is possible for it to resume where the previous driver had
+left off in terms of reporting free pages.
+
+Alexander Duyck, Dec 04, 2019
diff --git a/Documentation/mm/frontswap.rst b/Documentation/mm/frontswap.rst
new file mode 100644 (file)
index 0000000..feecc5e
--- /dev/null
@@ -0,0 +1,266 @@
+.. _frontswap:
+
+=========
+Frontswap
+=========
+
+Frontswap provides a "transcendent memory" interface for swap pages.
+In some environments, dramatic performance savings may be obtained because
+swapped pages are saved in RAM (or a RAM-like device) instead of a swap disk.
+
+.. _Transcendent memory in a nutshell: https://lwn.net/Articles/454795/
+
+Frontswap is so named because it can be thought of as the opposite of
+a "backing" store for a swap device.  The storage is assumed to be
+a synchronous concurrency-safe page-oriented "pseudo-RAM device" conforming
+to the requirements of transcendent memory (such as Xen's "tmem", or
+in-kernel compressed memory, aka "zcache", or future RAM-like devices);
+this pseudo-RAM device is not directly accessible or addressable by the
+kernel and is of unknown and possibly time-varying size.  The driver
+links itself to frontswap by calling frontswap_register_ops to set the
+frontswap_ops funcs appropriately and the functions it provides must
+conform to certain policies as follows:
+
+An "init" prepares the device to receive frontswap pages associated
+with the specified swap device number (aka "type").  A "store" will
+copy the page to transcendent memory and associate it with the type and
+offset associated with the page. A "load" will copy the page, if found,
+from transcendent memory into kernel memory, but will NOT remove the page
+from transcendent memory.  An "invalidate_page" will remove the page
+from transcendent memory and an "invalidate_area" will remove ALL pages
+associated with the swap type (e.g., like swapoff) and notify the "device"
+to refuse further stores with that swap type.
+
+Once a page is successfully stored, a matching load on the page will normally
+succeed.  So when the kernel finds itself in a situation where it needs
+to swap out a page, it first attempts to use frontswap.  If the store returns
+success, the data has been successfully saved to transcendent memory and
+a disk write and, if the data is later read back, a disk read are avoided.
+If a store returns failure, transcendent memory has rejected the data, and the
+page can be written to swap as usual.
+
+Note that if a page is stored and the page already exists in transcendent memory
+(a "duplicate" store), either the store succeeds and the data is overwritten,
+or the store fails AND the page is invalidated.  This ensures stale data may
+never be obtained from frontswap.
+
+If properly configured, monitoring of frontswap is done via debugfs in
+the `/sys/kernel/debug/frontswap` directory.  The effectiveness of
+frontswap can be measured (across all swap devices) with:
+
+``failed_stores``
+       how many store attempts have failed
+
+``loads``
+       how many loads were attempted (all should succeed)
+
+``succ_stores``
+       how many store attempts have succeeded
+
+``invalidates``
+       how many invalidates were attempted
+
+A backend implementation may provide additional metrics.
+
+FAQ
+===
+
+* Where's the value?
+
+When a workload starts swapping, performance falls through the floor.
+Frontswap significantly increases performance in many such workloads by
+providing a clean, dynamic interface to read and write swap pages to
+"transcendent memory" that is otherwise not directly addressable to the kernel.
+This interface is ideal when data is transformed to a different form
+and size (such as with compression) or secretly moved (as might be
+useful for write-balancing for some RAM-like devices).  Swap pages (and
+evicted page-cache pages) are a great use for this kind of slower-than-RAM-
+but-much-faster-than-disk "pseudo-RAM device".
+
+Frontswap with a fairly small impact on the kernel,
+provides a huge amount of flexibility for more dynamic, flexible RAM
+utilization in various system configurations:
+
+In the single kernel case, aka "zcache", pages are compressed and
+stored in local memory, thus increasing the total anonymous pages
+that can be safely kept in RAM.  Zcache essentially trades off CPU
+cycles used in compression/decompression for better memory utilization.
+Benchmarks have shown little or no impact when memory pressure is
+low while providing a significant performance improvement (25%+)
+on some workloads under high memory pressure.
+
+"RAMster" builds on zcache by adding "peer-to-peer" transcendent memory
+support for clustered systems.  Frontswap pages are locally compressed
+as in zcache, but then "remotified" to another system's RAM.  This
+allows RAM to be dynamically load-balanced back-and-forth as needed,
+i.e. when system A is overcommitted, it can swap to system B, and
+vice versa.  RAMster can also be configured as a memory server so
+many servers in a cluster can swap, dynamically as needed, to a single
+server configured with a large amount of RAM... without pre-configuring
+how much of the RAM is available for each of the clients!
+
+In the virtual case, the whole point of virtualization is to statistically
+multiplex physical resources across the varying demands of multiple
+virtual machines.  This is really hard to do with RAM and efforts to do
+it well with no kernel changes have essentially failed (except in some
+well-publicized special-case workloads).
+Specifically, the Xen Transcendent Memory backend allows otherwise
+"fallow" hypervisor-owned RAM to not only be "time-shared" between multiple
+virtual machines, but the pages can be compressed and deduplicated to
+optimize RAM utilization.  And when guest OS's are induced to surrender
+underutilized RAM (e.g. with "selfballooning"), sudden unexpected
+memory pressure may result in swapping; frontswap allows those pages
+to be swapped to and from hypervisor RAM (if overall host system memory
+conditions allow), thus mitigating the potentially awful performance impact
+of unplanned swapping.
+
+A KVM implementation is underway and has been RFC'ed to lkml.  And,
+using frontswap, investigation is also underway on the use of NVM as
+a memory extension technology.
+
+* Sure there may be performance advantages in some situations, but
+  what's the space/time overhead of frontswap?
+
+If CONFIG_FRONTSWAP is disabled, every frontswap hook compiles into
+nothingness and the only overhead is a few extra bytes per swapon'ed
+swap device.  If CONFIG_FRONTSWAP is enabled but no frontswap "backend"
+registers, there is one extra global variable compared to zero for
+every swap page read or written.  If CONFIG_FRONTSWAP is enabled
+AND a frontswap backend registers AND the backend fails every "store"
+request (i.e. provides no memory despite claiming it might),
+CPU overhead is still negligible -- and since every frontswap fail
+precedes a swap page write-to-disk, the system is highly likely
+to be I/O bound and using a small fraction of a percent of a CPU
+will be irrelevant anyway.
+
+As for space, if CONFIG_FRONTSWAP is enabled AND a frontswap backend
+registers, one bit is allocated for every swap page for every swap
+device that is swapon'd.  This is added to the EIGHT bits (which
+was sixteen until about 2.6.34) that the kernel already allocates
+for every swap page for every swap device that is swapon'd.  (Hugh
+Dickins has observed that frontswap could probably steal one of
+the existing eight bits, but let's worry about that minor optimization
+later.)  For very large swap disks (which are rare) on a standard
+4K pagesize, this is 1MB per 32GB swap.
+
+When swap pages are stored in transcendent memory instead of written
+out to disk, there is a side effect that this may create more memory
+pressure that can potentially outweigh the other advantages.  A
+backend, such as zcache, must implement policies to carefully (but
+dynamically) manage memory limits to ensure this doesn't happen.
+
+* OK, how about a quick overview of what this frontswap patch does
+  in terms that a kernel hacker can grok?
+
+Let's assume that a frontswap "backend" has registered during
+kernel initialization; this registration indicates that this
+frontswap backend has access to some "memory" that is not directly
+accessible by the kernel.  Exactly how much memory it provides is
+entirely dynamic and random.
+
+Whenever a swap-device is swapon'd frontswap_init() is called,
+passing the swap device number (aka "type") as a parameter.
+This notifies frontswap to expect attempts to "store" swap pages
+associated with that number.
+
+Whenever the swap subsystem is readying a page to write to a swap
+device (c.f swap_writepage()), frontswap_store is called.  Frontswap
+consults with the frontswap backend and if the backend says it does NOT
+have room, frontswap_store returns -1 and the kernel swaps the page
+to the swap device as normal.  Note that the response from the frontswap
+backend is unpredictable to the kernel; it may choose to never accept a
+page, it could accept every ninth page, or it might accept every
+page.  But if the backend does accept a page, the data from the page
+has already been copied and associated with the type and offset,
+and the backend guarantees the persistence of the data.  In this case,
+frontswap sets a bit in the "frontswap_map" for the swap device
+corresponding to the page offset on the swap device to which it would
+otherwise have written the data.
+
+When the swap subsystem needs to swap-in a page (swap_readpage()),
+it first calls frontswap_load() which checks the frontswap_map to
+see if the page was earlier accepted by the frontswap backend.  If
+it was, the page of data is filled from the frontswap backend and
+the swap-in is complete.  If not, the normal swap-in code is
+executed to obtain the page of data from the real swap device.
+
+So every time the frontswap backend accepts a page, a swap device read
+and (potentially) a swap device write are replaced by a "frontswap backend
+store" and (possibly) a "frontswap backend loads", which are presumably much
+faster.
+
+* Can't frontswap be configured as a "special" swap device that is
+  just higher priority than any real swap device (e.g. like zswap,
+  or maybe swap-over-nbd/NFS)?
+
+No.  First, the existing swap subsystem doesn't allow for any kind of
+swap hierarchy.  Perhaps it could be rewritten to accommodate a hierarchy,
+but this would require fairly drastic changes.  Even if it were
+rewritten, the existing swap subsystem uses the block I/O layer which
+assumes a swap device is fixed size and any page in it is linearly
+addressable.  Frontswap barely touches the existing swap subsystem,
+and works around the constraints of the block I/O subsystem to provide
+a great deal of flexibility and dynamicity.
+
+For example, the acceptance of any swap page by the frontswap backend is
+entirely unpredictable. This is critical to the definition of frontswap
+backends because it grants completely dynamic discretion to the
+backend.  In zcache, one cannot know a priori how compressible a page is.
+"Poorly" compressible pages can be rejected, and "poorly" can itself be
+defined dynamically depending on current memory constraints.
+
+Further, frontswap is entirely synchronous whereas a real swap
+device is, by definition, asynchronous and uses block I/O.  The
+block I/O layer is not only unnecessary, but may perform "optimizations"
+that are inappropriate for a RAM-oriented device including delaying
+the write of some pages for a significant amount of time.  Synchrony is
+required to ensure the dynamicity of the backend and to avoid thorny race
+conditions that would unnecessarily and greatly complicate frontswap
+and/or the block I/O subsystem.  That said, only the initial "store"
+and "load" operations need be synchronous.  A separate asynchronous thread
+is free to manipulate the pages stored by frontswap.  For example,
+the "remotification" thread in RAMster uses standard asynchronous
+kernel sockets to move compressed frontswap pages to a remote machine.
+Similarly, a KVM guest-side implementation could do in-guest compression
+and use "batched" hypercalls.
+
+In a virtualized environment, the dynamicity allows the hypervisor
+(or host OS) to do "intelligent overcommit".  For example, it can
+choose to accept pages only until host-swapping might be imminent,
+then force guests to do their own swapping.
+
+There is a downside to the transcendent memory specifications for
+frontswap:  Since any "store" might fail, there must always be a real
+slot on a real swap device to swap the page.  Thus frontswap must be
+implemented as a "shadow" to every swapon'd device with the potential
+capability of holding every page that the swap device might have held
+and the possibility that it might hold no pages at all.  This means
+that frontswap cannot contain more pages than the total of swapon'd
+swap devices.  For example, if NO swap device is configured on some
+installation, frontswap is useless.  Swapless portable devices
+can still use frontswap but a backend for such devices must configure
+some kind of "ghost" swap device and ensure that it is never used.
+
+* Why this weird definition about "duplicate stores"?  If a page
+  has been previously successfully stored, can't it always be
+  successfully overwritten?
+
+Nearly always it can, but no, sometimes it cannot.  Consider an example
+where data is compressed and the original 4K page has been compressed
+to 1K.  Now an attempt is made to overwrite the page with data that
+is non-compressible and so would take the entire 4K.  But the backend
+has no more space.  In this case, the store must be rejected.  Whenever
+frontswap rejects a store that would overwrite, it also must invalidate
+the old data and ensure that it is no longer accessible.  Since the
+swap subsystem then writes the new data to the read swap device,
+this is the correct course of action to ensure coherency.
+
+* Why does the frontswap patch create the new include file swapfile.h?
+
+The frontswap code depends on some swap-subsystem-internal data
+structures that have, over the years, moved back and forth between
+static and global.  This seemed a reasonable compromise:  Define
+them as global but declare them in a new include file that isn't
+included by the large number of source files that include swap.h.
+
+Dan Magenheimer, last updated April 9, 2012
diff --git a/Documentation/mm/highmem.rst b/Documentation/mm/highmem.rst
new file mode 100644 (file)
index 0000000..c9887f2
--- /dev/null
@@ -0,0 +1,167 @@
+.. _highmem:
+
+====================
+High Memory Handling
+====================
+
+By: Peter Zijlstra <a.p.zijlstra@chello.nl>
+
+.. contents:: :local:
+
+What Is High Memory?
+====================
+
+High memory (highmem) is used when the size of physical memory approaches or
+exceeds the maximum size of virtual memory.  At that point it becomes
+impossible for the kernel to keep all of the available physical memory mapped
+at all times.  This means the kernel needs to start using temporary mappings of
+the pieces of physical memory that it wants to access.
+
+The part of (physical) memory not covered by a permanent mapping is what we
+refer to as 'highmem'.  There are various architecture dependent constraints on
+where exactly that border lies.
+
+In the i386 arch, for example, we choose to map the kernel into every process's
+VM space so that we don't have to pay the full TLB invalidation costs for
+kernel entry/exit.  This means the available virtual memory space (4GiB on
+i386) has to be divided between user and kernel space.
+
+The traditional split for architectures using this approach is 3:1, 3GiB for
+userspace and the top 1GiB for kernel space::
+
+               +--------+ 0xffffffff
+               | Kernel |
+               +--------+ 0xc0000000
+               |        |
+               | User   |
+               |        |
+               +--------+ 0x00000000
+
+This means that the kernel can at most map 1GiB of physical memory at any one
+time, but because we need virtual address space for other things - including
+temporary maps to access the rest of the physical memory - the actual direct
+map will typically be less (usually around ~896MiB).
+
+Other architectures that have mm context tagged TLBs can have separate kernel
+and user maps.  Some hardware (like some ARMs), however, have limited virtual
+space when they use mm context tags.
+
+
+Temporary Virtual Mappings
+==========================
+
+The kernel contains several ways of creating temporary mappings. The following
+list shows them in order of preference of use.
+
+* kmap_local_page().  This function is used to require short term mappings.
+  It can be invoked from any context (including interrupts) but the mappings
+  can only be used in the context which acquired them.
+
+  This function should be preferred, where feasible, over all the others.
+
+  These mappings are thread-local and CPU-local, meaning that the mapping
+  can only be accessed from within this thread and the thread is bound the
+  CPU while the mapping is active. Even if the thread is preempted (since
+  preemption is never disabled by the function) the CPU can not be
+  unplugged from the system via CPU-hotplug until the mapping is disposed.
+
+  It's valid to take pagefaults in a local kmap region, unless the context
+  in which the local mapping is acquired does not allow it for other reasons.
+
+  kmap_local_page() always returns a valid virtual address and it is assumed
+  that kunmap_local() will never fail.
+
+  Nesting kmap_local_page() and kmap_atomic() mappings is allowed to a certain
+  extent (up to KMAP_TYPE_NR) but their invocations have to be strictly ordered
+  because the map implementation is stack based. See kmap_local_page() kdocs
+  (included in the "Functions" section) for details on how to manage nested
+  mappings.
+
+* kmap_atomic().  This permits a very short duration mapping of a single
+  page.  Since the mapping is restricted to the CPU that issued it, it
+  performs well, but the issuing task is therefore required to stay on that
+  CPU until it has finished, lest some other task displace its mappings.
+
+  kmap_atomic() may also be used by interrupt contexts, since it does not
+  sleep and the callers too may not sleep until after kunmap_atomic() is
+  called.
+
+  Each call of kmap_atomic() in the kernel creates a non-preemptible section
+  and disable pagefaults. This could be a source of unwanted latency. Therefore
+  users should prefer kmap_local_page() instead of kmap_atomic().
+
+  It is assumed that k[un]map_atomic() won't fail.
+
+* kmap().  This should be used to make short duration mapping of a single
+  page with no restrictions on preemption or migration. It comes with an
+  overhead as mapping space is restricted and protected by a global lock
+  for synchronization. When mapping is no longer needed, the address that
+  the page was mapped to must be released with kunmap().
+
+  Mapping changes must be propagated across all the CPUs. kmap() also
+  requires global TLB invalidation when the kmap's pool wraps and it might
+  block when the mapping space is fully utilized until a slot becomes
+  available. Therefore, kmap() is only callable from preemptible context.
+
+  All the above work is necessary if a mapping must last for a relatively
+  long time but the bulk of high-memory mappings in the kernel are
+  short-lived and only used in one place. This means that the cost of
+  kmap() is mostly wasted in such cases. kmap() was not intended for long
+  term mappings but it has morphed in that direction and its use is
+  strongly discouraged in newer code and the set of the preceding functions
+  should be preferred.
+
+  On 64-bit systems, calls to kmap_local_page(), kmap_atomic() and kmap() have
+  no real work to do because a 64-bit address space is more than sufficient to
+  address all the physical memory whose pages are permanently mapped.
+
+* vmap().  This can be used to make a long duration mapping of multiple
+  physical pages into a contiguous virtual space.  It needs global
+  synchronization to unmap.
+
+
+Cost of Temporary Mappings
+==========================
+
+The cost of creating temporary mappings can be quite high.  The arch has to
+manipulate the kernel's page tables, the data TLB and/or the MMU's registers.
+
+If CONFIG_HIGHMEM is not set, then the kernel will try and create a mapping
+simply with a bit of arithmetic that will convert the page struct address into
+a pointer to the page contents rather than juggling mappings about.  In such a
+case, the unmap operation may be a null operation.
+
+If CONFIG_MMU is not set, then there can be no temporary mappings and no
+highmem.  In such a case, the arithmetic approach will also be used.
+
+
+i386 PAE
+========
+
+The i386 arch, under some circumstances, will permit you to stick up to 64GiB
+of RAM into your 32-bit machine.  This has a number of consequences:
+
+* Linux needs a page-frame structure for each page in the system and the
+  pageframes need to live in the permanent mapping, which means:
+
+* you can have 896M/sizeof(struct page) page-frames at most; with struct
+  page being 32-bytes that would end up being something in the order of 112G
+  worth of pages; the kernel, however, needs to store more than just
+  page-frames in that memory...
+
+* PAE makes your page tables larger - which slows the system down as more
+  data has to be accessed to traverse in TLB fills and the like.  One
+  advantage is that PAE has more PTE bits and can provide advanced features
+  like NX and PAT.
+
+The general recommendation is that you don't use more than 8GiB on a 32-bit
+machine - although more might work for you and your workload, you're pretty
+much on your own - don't expect kernel developers to really care much if things
+come apart.
+
+
+Functions
+=========
+
+.. kernel-doc:: include/linux/highmem.h
+.. kernel-doc:: include/linux/highmem-internal.h
diff --git a/Documentation/mm/hmm.rst b/Documentation/mm/hmm.rst
new file mode 100644 (file)
index 0000000..f2a59ed
--- /dev/null
@@ -0,0 +1,452 @@
+.. _hmm:
+
+=====================================
+Heterogeneous Memory Management (HMM)
+=====================================
+
+Provide infrastructure and helpers to integrate non-conventional memory (device
+memory like GPU on board memory) into regular kernel path, with the cornerstone
+of this being specialized struct page for such memory (see sections 5 to 7 of
+this document).
+
+HMM also provides optional helpers for SVM (Share Virtual Memory), i.e.,
+allowing a device to transparently access program addresses coherently with
+the CPU meaning that any valid pointer on the CPU is also a valid pointer
+for the device. This is becoming mandatory to simplify the use of advanced
+heterogeneous computing where GPU, DSP, or FPGA are used to perform various
+computations on behalf of a process.
+
+This document is divided as follows: in the first section I expose the problems
+related to using device specific memory allocators. In the second section, I
+expose the hardware limitations that are inherent to many platforms. The third
+section gives an overview of the HMM design. The fourth section explains how
+CPU page-table mirroring works and the purpose of HMM in this context. The
+fifth section deals with how device memory is represented inside the kernel.
+Finally, the last section presents a new migration helper that allows
+leveraging the device DMA engine.
+
+.. contents:: :local:
+
+Problems of using a device specific memory allocator
+====================================================
+
+Devices with a large amount of on board memory (several gigabytes) like GPUs
+have historically managed their memory through dedicated driver specific APIs.
+This creates a disconnect between memory allocated and managed by a device
+driver and regular application memory (private anonymous, shared memory, or
+regular file backed memory). From here on I will refer to this aspect as split
+address space. I use shared address space to refer to the opposite situation:
+i.e., one in which any application memory region can be used by a device
+transparently.
+
+Split address space happens because devices can only access memory allocated
+through a device specific API. This implies that all memory objects in a program
+are not equal from the device point of view which complicates large programs
+that rely on a wide set of libraries.
+
+Concretely, this means that code that wants to leverage devices like GPUs needs
+to copy objects between generically allocated memory (malloc, mmap private, mmap
+share) and memory allocated through the device driver API (this still ends up
+with an mmap but of the device file).
+
+For flat data sets (array, grid, image, ...) this isn't too hard to achieve but
+for complex data sets (list, tree, ...) it's hard to get right. Duplicating a
+complex data set needs to re-map all the pointer relations between each of its
+elements. This is error prone and programs get harder to debug because of the
+duplicate data set and addresses.
+
+Split address space also means that libraries cannot transparently use data
+they are getting from the core program or another library and thus each library
+might have to duplicate its input data set using the device specific memory
+allocator. Large projects suffer from this and waste resources because of the
+various memory copies.
+
+Duplicating each library API to accept as input or output memory allocated by
+each device specific allocator is not a viable option. It would lead to a
+combinatorial explosion in the library entry points.
+
+Finally, with the advance of high level language constructs (in C++ but in
+other languages too) it is now possible for the compiler to leverage GPUs and
+other devices without programmer knowledge. Some compiler identified patterns
+are only do-able with a shared address space. It is also more reasonable to use
+a shared address space for all other patterns.
+
+
+I/O bus, device memory characteristics
+======================================
+
+I/O buses cripple shared address spaces due to a few limitations. Most I/O
+buses only allow basic memory access from device to main memory; even cache
+coherency is often optional. Access to device memory from a CPU is even more
+limited. More often than not, it is not cache coherent.
+
+If we only consider the PCIE bus, then a device can access main memory (often
+through an IOMMU) and be cache coherent with the CPUs. However, it only allows
+a limited set of atomic operations from the device on main memory. This is worse
+in the other direction: the CPU can only access a limited range of the device
+memory and cannot perform atomic operations on it. Thus device memory cannot
+be considered the same as regular memory from the kernel point of view.
+
+Another crippling factor is the limited bandwidth (~32GBytes/s with PCIE 4.0
+and 16 lanes). This is 33 times less than the fastest GPU memory (1 TBytes/s).
+The final limitation is latency. Access to main memory from the device has an
+order of magnitude higher latency than when the device accesses its own memory.
+
+Some platforms are developing new I/O buses or additions/modifications to PCIE
+to address some of these limitations (OpenCAPI, CCIX). They mainly allow
+two-way cache coherency between CPU and device and allow all atomic operations the
+architecture supports. Sadly, not all platforms are following this trend and
+some major architectures are left without hardware solutions to these problems.
+
+So for shared address space to make sense, not only must we allow devices to
+access any memory but we must also permit any memory to be migrated to device
+memory while the device is using it (blocking CPU access while it happens).
+
+
+Shared address space and migration
+==================================
+
+HMM intends to provide two main features. The first one is to share the address
+space by duplicating the CPU page table in the device page table so the same
+address points to the same physical memory for any valid main memory address in
+the process address space.
+
+To achieve this, HMM offers a set of helpers to populate the device page table
+while keeping track of CPU page table updates. Device page table updates are
+not as easy as CPU page table updates. To update the device page table, you must
+allocate a buffer (or use a pool of pre-allocated buffers) and write GPU
+specific commands in it to perform the update (unmap, cache invalidations, and
+flush, ...). This cannot be done through common code for all devices. Hence
+why HMM provides helpers to factor out everything that can be while leaving the
+hardware specific details to the device driver.
+
+The second mechanism HMM provides is a new kind of ZONE_DEVICE memory that
+allows allocating a struct page for each page of device memory. Those pages
+are special because the CPU cannot map them. However, they allow migrating
+main memory to device memory using existing migration mechanisms and everything
+looks like a page that is swapped out to disk from the CPU point of view. Using a
+struct page gives the easiest and cleanest integration with existing mm
+mechanisms. Here again, HMM only provides helpers, first to hotplug new ZONE_DEVICE
+memory for the device memory and second to perform migration. Policy decisions
+of what and when to migrate is left to the device driver.
+
+Note that any CPU access to a device page triggers a page fault and a migration
+back to main memory. For example, when a page backing a given CPU address A is
+migrated from a main memory page to a device page, then any CPU access to
+address A triggers a page fault and initiates a migration back to main memory.
+
+With these two features, HMM not only allows a device to mirror process address
+space and keeps both CPU and device page tables synchronized, but also
+leverages device memory by migrating the part of the data set that is actively being
+used by the device.
+
+
+Address space mirroring implementation and API
+==============================================
+
+Address space mirroring's main objective is to allow duplication of a range of
+CPU page table into a device page table; HMM helps keep both synchronized. A
+device driver that wants to mirror a process address space must start with the
+registration of a mmu_interval_notifier::
+
+ int mmu_interval_notifier_insert(struct mmu_interval_notifier *interval_sub,
+                                 struct mm_struct *mm, unsigned long start,
+                                 unsigned long length,
+                                 const struct mmu_interval_notifier_ops *ops);
+
+During the ops->invalidate() callback the device driver must perform the
+update action to the range (mark range read only, or fully unmap, etc.). The
+device must complete the update before the driver callback returns.
+
+When the device driver wants to populate a range of virtual addresses, it can
+use::
+
+  int hmm_range_fault(struct hmm_range *range);
+
+It will trigger a page fault on missing or read-only entries if write access is
+requested (see below). Page faults use the generic mm page fault code path just
+like a CPU page fault.
+
+Both functions copy CPU page table entries into their pfns array argument. Each
+entry in that array corresponds to an address in the virtual range. HMM
+provides a set of flags to help the driver identify special CPU page table
+entries.
+
+Locking within the sync_cpu_device_pagetables() callback is the most important
+aspect the driver must respect in order to keep things properly synchronized.
+The usage pattern is::
+
+ int driver_populate_range(...)
+ {
+      struct hmm_range range;
+      ...
+
+      range.notifier = &interval_sub;
+      range.start = ...;
+      range.end = ...;
+      range.hmm_pfns = ...;
+
+      if (!mmget_not_zero(interval_sub->notifier.mm))
+          return -EFAULT;
+
+ again:
+      range.notifier_seq = mmu_interval_read_begin(&interval_sub);
+      mmap_read_lock(mm);
+      ret = hmm_range_fault(&range);
+      if (ret) {
+          mmap_read_unlock(mm);
+          if (ret == -EBUSY)
+                 goto again;
+          return ret;
+      }
+      mmap_read_unlock(mm);
+
+      take_lock(driver->update);
+      if (mmu_interval_read_retry(&ni, range.notifier_seq) {
+          release_lock(driver->update);
+          goto again;
+      }
+
+      /* Use pfns array content to update device page table,
+       * under the update lock */
+
+      release_lock(driver->update);
+      return 0;
+ }
+
+The driver->update lock is the same lock that the driver takes inside its
+invalidate() callback. That lock must be held before calling
+mmu_interval_read_retry() to avoid any race with a concurrent CPU page table
+update.
+
+Leverage default_flags and pfn_flags_mask
+=========================================
+
+The hmm_range struct has 2 fields, default_flags and pfn_flags_mask, that specify
+fault or snapshot policy for the whole range instead of having to set them
+for each entry in the pfns array.
+
+For instance if the device driver wants pages for a range with at least read
+permission, it sets::
+
+    range->default_flags = HMM_PFN_REQ_FAULT;
+    range->pfn_flags_mask = 0;
+
+and calls hmm_range_fault() as described above. This will fill fault all pages
+in the range with at least read permission.
+
+Now let's say the driver wants to do the same except for one page in the range for
+which it wants to have write permission. Now driver set::
+
+    range->default_flags = HMM_PFN_REQ_FAULT;
+    range->pfn_flags_mask = HMM_PFN_REQ_WRITE;
+    range->pfns[index_of_write] = HMM_PFN_REQ_WRITE;
+
+With this, HMM will fault in all pages with at least read (i.e., valid) and for the
+address == range->start + (index_of_write << PAGE_SHIFT) it will fault with
+write permission i.e., if the CPU pte does not have write permission set then HMM
+will call handle_mm_fault().
+
+After hmm_range_fault completes the flag bits are set to the current state of
+the page tables, ie HMM_PFN_VALID | HMM_PFN_WRITE will be set if the page is
+writable.
+
+
+Represent and manage device memory from core kernel point of view
+=================================================================
+
+Several different designs were tried to support device memory. The first one
+used a device specific data structure to keep information about migrated memory
+and HMM hooked itself in various places of mm code to handle any access to
+addresses that were backed by device memory. It turns out that this ended up
+replicating most of the fields of struct page and also needed many kernel code
+paths to be updated to understand this new kind of memory.
+
+Most kernel code paths never try to access the memory behind a page
+but only care about struct page contents. Because of this, HMM switched to
+directly using struct page for device memory which left most kernel code paths
+unaware of the difference. We only need to make sure that no one ever tries to
+map those pages from the CPU side.
+
+Migration to and from device memory
+===================================
+
+Because the CPU cannot access device memory directly, the device driver must
+use hardware DMA or device specific load/store instructions to migrate data.
+The migrate_vma_setup(), migrate_vma_pages(), and migrate_vma_finalize()
+functions are designed to make drivers easier to write and to centralize common
+code across drivers.
+
+Before migrating pages to device private memory, special device private
+``struct page`` need to be created. These will be used as special "swap"
+page table entries so that a CPU process will fault if it tries to access
+a page that has been migrated to device private memory.
+
+These can be allocated and freed with::
+
+    struct resource *res;
+    struct dev_pagemap pagemap;
+
+    res = request_free_mem_region(&iomem_resource, /* number of bytes */,
+                                  "name of driver resource");
+    pagemap.type = MEMORY_DEVICE_PRIVATE;
+    pagemap.range.start = res->start;
+    pagemap.range.end = res->end;
+    pagemap.nr_range = 1;
+    pagemap.ops = &device_devmem_ops;
+    memremap_pages(&pagemap, numa_node_id());
+
+    memunmap_pages(&pagemap);
+    release_mem_region(pagemap.range.start, range_len(&pagemap.range));
+
+There are also devm_request_free_mem_region(), devm_memremap_pages(),
+devm_memunmap_pages(), and devm_release_mem_region() when the resources can
+be tied to a ``struct device``.
+
+The overall migration steps are similar to migrating NUMA pages within system
+memory (see :ref:`Page migration <page_migration>`) but the steps are split
+between device driver specific code and shared common code:
+
+1. ``mmap_read_lock()``
+
+   The device driver has to pass a ``struct vm_area_struct`` to
+   migrate_vma_setup() so the mmap_read_lock() or mmap_write_lock() needs to
+   be held for the duration of the migration.
+
+2. ``migrate_vma_setup(struct migrate_vma *args)``
+
+   The device driver initializes the ``struct migrate_vma`` fields and passes
+   the pointer to migrate_vma_setup(). The ``args->flags`` field is used to
+   filter which source pages should be migrated. For example, setting
+   ``MIGRATE_VMA_SELECT_SYSTEM`` will only migrate system memory and
+   ``MIGRATE_VMA_SELECT_DEVICE_PRIVATE`` will only migrate pages residing in
+   device private memory. If the latter flag is set, the ``args->pgmap_owner``
+   field is used to identify device private pages owned by the driver. This
+   avoids trying to migrate device private pages residing in other devices.
+   Currently only anonymous private VMA ranges can be migrated to or from
+   system memory and device private memory.
+
+   One of the first steps migrate_vma_setup() does is to invalidate other
+   device's MMUs with the ``mmu_notifier_invalidate_range_start(()`` and
+   ``mmu_notifier_invalidate_range_end()`` calls around the page table
+   walks to fill in the ``args->src`` array with PFNs to be migrated.
+   The ``invalidate_range_start()`` callback is passed a
+   ``struct mmu_notifier_range`` with the ``event`` field set to
+   ``MMU_NOTIFY_MIGRATE`` and the ``owner`` field set to
+   the ``args->pgmap_owner`` field passed to migrate_vma_setup(). This is
+   allows the device driver to skip the invalidation callback and only
+   invalidate device private MMU mappings that are actually migrating.
+   This is explained more in the next section.
+
+   While walking the page tables, a ``pte_none()`` or ``is_zero_pfn()``
+   entry results in a valid "zero" PFN stored in the ``args->src`` array.
+   This lets the driver allocate device private memory and clear it instead
+   of copying a page of zeros. Valid PTE entries to system memory or
+   device private struct pages will be locked with ``lock_page()``, isolated
+   from the LRU (if system memory since device private pages are not on
+   the LRU), unmapped from the process, and a special migration PTE is
+   inserted in place of the original PTE.
+   migrate_vma_setup() also clears the ``args->dst`` array.
+
+3. The device driver allocates destination pages and copies source pages to
+   destination pages.
+
+   The driver checks each ``src`` entry to see if the ``MIGRATE_PFN_MIGRATE``
+   bit is set and skips entries that are not migrating. The device driver
+   can also choose to skip migrating a page by not filling in the ``dst``
+   array for that page.
+
+   The driver then allocates either a device private struct page or a
+   system memory page, locks the page with ``lock_page()``, and fills in the
+   ``dst`` array entry with::
+
+     dst[i] = migrate_pfn(page_to_pfn(dpage));
+
+   Now that the driver knows that this page is being migrated, it can
+   invalidate device private MMU mappings and copy device private memory
+   to system memory or another device private page. The core Linux kernel
+   handles CPU page table invalidations so the device driver only has to
+   invalidate its own MMU mappings.
+
+   The driver can use ``migrate_pfn_to_page(src[i])`` to get the
+   ``struct page`` of the source and either copy the source page to the
+   destination or clear the destination device private memory if the pointer
+   is ``NULL`` meaning the source page was not populated in system memory.
+
+4. ``migrate_vma_pages()``
+
+   This step is where the migration is actually "committed".
+
+   If the source page was a ``pte_none()`` or ``is_zero_pfn()`` page, this
+   is where the newly allocated page is inserted into the CPU's page table.
+   This can fail if a CPU thread faults on the same page. However, the page
+   table is locked and only one of the new pages will be inserted.
+   The device driver will see that the ``MIGRATE_PFN_MIGRATE`` bit is cleared
+   if it loses the race.
+
+   If the source page was locked, isolated, etc. the source ``struct page``
+   information is now copied to destination ``struct page`` finalizing the
+   migration on the CPU side.
+
+5. Device driver updates device MMU page tables for pages still migrating,
+   rolling back pages not migrating.
+
+   If the ``src`` entry still has ``MIGRATE_PFN_MIGRATE`` bit set, the device
+   driver can update the device MMU and set the write enable bit if the
+   ``MIGRATE_PFN_WRITE`` bit is set.
+
+6. ``migrate_vma_finalize()``
+
+   This step replaces the special migration page table entry with the new
+   page's page table entry and releases the reference to the source and
+   destination ``struct page``.
+
+7. ``mmap_read_unlock()``
+
+   The lock can now be released.
+
+Exclusive access memory
+=======================
+
+Some devices have features such as atomic PTE bits that can be used to implement
+atomic access to system memory. To support atomic operations to a shared virtual
+memory page such a device needs access to that page which is exclusive of any
+userspace access from the CPU. The ``make_device_exclusive_range()`` function
+can be used to make a memory range inaccessible from userspace.
+
+This replaces all mappings for pages in the given range with special swap
+entries. Any attempt to access the swap entry results in a fault which is
+resovled by replacing the entry with the original mapping. A driver gets
+notified that the mapping has been changed by MMU notifiers, after which point
+it will no longer have exclusive access to the page. Exclusive access is
+guranteed to last until the driver drops the page lock and page reference, at
+which point any CPU faults on the page may proceed as described.
+
+Memory cgroup (memcg) and rss accounting
+========================================
+
+For now, device memory is accounted as any regular page in rss counters (either
+anonymous if device page is used for anonymous, file if device page is used for
+file backed page, or shmem if device page is used for shared memory). This is a
+deliberate choice to keep existing applications, that might start using device
+memory without knowing about it, running unimpacted.
+
+A drawback is that the OOM killer might kill an application using a lot of
+device memory and not a lot of regular system memory and thus not freeing much
+system memory. We want to gather more real world experience on how applications
+and system react under memory pressure in the presence of device memory before
+deciding to account device memory differently.
+
+
+Same decision was made for memory cgroup. Device memory pages are accounted
+against same memory cgroup a regular page would be accounted to. This does
+simplify migration to and from device memory. This also means that migration
+back from device memory to regular memory cannot fail because it would
+go above memory cgroup limit. We might revisit this choice latter on once we
+get more experience in how device memory is used and its impact on memory
+resource control.
+
+
+Note that device memory can never be pinned by a device driver nor through GUP
+and thus such memory is always free upon process exit. Or when last reference
+is dropped in case of shared memory or file backed memory.
diff --git a/Documentation/mm/hugetlbfs_reserv.rst b/Documentation/mm/hugetlbfs_reserv.rst
new file mode 100644 (file)
index 0000000..f143954
--- /dev/null
@@ -0,0 +1,596 @@
+.. _hugetlbfs_reserve:
+
+=====================
+Hugetlbfs Reservation
+=====================
+
+Overview
+========
+
+Huge pages as described at :ref:`hugetlbpage` are typically
+preallocated for application use.  These huge pages are instantiated in a
+task's address space at page fault time if the VMA indicates huge pages are
+to be used.  If no huge page exists at page fault time, the task is sent
+a SIGBUS and often dies an unhappy death.  Shortly after huge page support
+was added, it was determined that it would be better to detect a shortage
+of huge pages at mmap() time.  The idea is that if there were not enough
+huge pages to cover the mapping, the mmap() would fail.  This was first
+done with a simple check in the code at mmap() time to determine if there
+were enough free huge pages to cover the mapping.  Like most things in the
+kernel, the code has evolved over time.  However, the basic idea was to
+'reserve' huge pages at mmap() time to ensure that huge pages would be
+available for page faults in that mapping.  The description below attempts to
+describe how huge page reserve processing is done in the v4.10 kernel.
+
+
+Audience
+========
+This description is primarily targeted at kernel developers who are modifying
+hugetlbfs code.
+
+
+The Data Structures
+===================
+
+resv_huge_pages
+       This is a global (per-hstate) count of reserved huge pages.  Reserved
+       huge pages are only available to the task which reserved them.
+       Therefore, the number of huge pages generally available is computed
+       as (``free_huge_pages - resv_huge_pages``).
+Reserve Map
+       A reserve map is described by the structure::
+
+               struct resv_map {
+                       struct kref refs;
+                       spinlock_t lock;
+                       struct list_head regions;
+                       long adds_in_progress;
+                       struct list_head region_cache;
+                       long region_cache_count;
+               };
+
+       There is one reserve map for each huge page mapping in the system.
+       The regions list within the resv_map describes the regions within
+       the mapping.  A region is described as::
+
+               struct file_region {
+                       struct list_head link;
+                       long from;
+                       long to;
+               };
+
+       The 'from' and 'to' fields of the file region structure are huge page
+       indices into the mapping.  Depending on the type of mapping, a
+       region in the reserv_map may indicate reservations exist for the
+       range, or reservations do not exist.
+Flags for MAP_PRIVATE Reservations
+       These are stored in the bottom bits of the reservation map pointer.
+
+       ``#define HPAGE_RESV_OWNER    (1UL << 0)``
+               Indicates this task is the owner of the reservations
+               associated with the mapping.
+       ``#define HPAGE_RESV_UNMAPPED (1UL << 1)``
+               Indicates task originally mapping this range (and creating
+               reserves) has unmapped a page from this task (the child)
+               due to a failed COW.
+Page Flags
+       The PagePrivate page flag is used to indicate that a huge page
+       reservation must be restored when the huge page is freed.  More
+       details will be discussed in the "Freeing huge pages" section.
+
+
+Reservation Map Location (Private or Shared)
+============================================
+
+A huge page mapping or segment is either private or shared.  If private,
+it is typically only available to a single address space (task).  If shared,
+it can be mapped into multiple address spaces (tasks).  The location and
+semantics of the reservation map is significantly different for the two types
+of mappings.  Location differences are:
+
+- For private mappings, the reservation map hangs off the VMA structure.
+  Specifically, vma->vm_private_data.  This reserve map is created at the
+  time the mapping (mmap(MAP_PRIVATE)) is created.
+- For shared mappings, the reservation map hangs off the inode.  Specifically,
+  inode->i_mapping->private_data.  Since shared mappings are always backed
+  by files in the hugetlbfs filesystem, the hugetlbfs code ensures each inode
+  contains a reservation map.  As a result, the reservation map is allocated
+  when the inode is created.
+
+
+Creating Reservations
+=====================
+Reservations are created when a huge page backed shared memory segment is
+created (shmget(SHM_HUGETLB)) or a mapping is created via mmap(MAP_HUGETLB).
+These operations result in a call to the routine hugetlb_reserve_pages()::
+
+       int hugetlb_reserve_pages(struct inode *inode,
+                                 long from, long to,
+                                 struct vm_area_struct *vma,
+                                 vm_flags_t vm_flags)
+
+The first thing hugetlb_reserve_pages() does is check if the NORESERVE
+flag was specified in either the shmget() or mmap() call.  If NORESERVE
+was specified, then this routine returns immediately as no reservations
+are desired.
+
+The arguments 'from' and 'to' are huge page indices into the mapping or
+underlying file.  For shmget(), 'from' is always 0 and 'to' corresponds to
+the length of the segment/mapping.  For mmap(), the offset argument could
+be used to specify the offset into the underlying file.  In such a case,
+the 'from' and 'to' arguments have been adjusted by this offset.
+
+One of the big differences between PRIVATE and SHARED mappings is the way
+in which reservations are represented in the reservation map.
+
+- For shared mappings, an entry in the reservation map indicates a reservation
+  exists or did exist for the corresponding page.  As reservations are
+  consumed, the reservation map is not modified.
+- For private mappings, the lack of an entry in the reservation map indicates
+  a reservation exists for the corresponding page.  As reservations are
+  consumed, entries are added to the reservation map.  Therefore, the
+  reservation map can also be used to determine which reservations have
+  been consumed.
+
+For private mappings, hugetlb_reserve_pages() creates the reservation map and
+hangs it off the VMA structure.  In addition, the HPAGE_RESV_OWNER flag is set
+to indicate this VMA owns the reservations.
+
+The reservation map is consulted to determine how many huge page reservations
+are needed for the current mapping/segment.  For private mappings, this is
+always the value (to - from).  However, for shared mappings it is possible that
+some reservations may already exist within the range (to - from).  See the
+section :ref:`Reservation Map Modifications <resv_map_modifications>`
+for details on how this is accomplished.
+
+The mapping may be associated with a subpool.  If so, the subpool is consulted
+to ensure there is sufficient space for the mapping.  It is possible that the
+subpool has set aside reservations that can be used for the mapping.  See the
+section :ref:`Subpool Reservations <sub_pool_resv>` for more details.
+
+After consulting the reservation map and subpool, the number of needed new
+reservations is known.  The routine hugetlb_acct_memory() is called to check
+for and take the requested number of reservations.  hugetlb_acct_memory()
+calls into routines that potentially allocate and adjust surplus page counts.
+However, within those routines the code is simply checking to ensure there
+are enough free huge pages to accommodate the reservation.  If there are,
+the global reservation count resv_huge_pages is adjusted something like the
+following::
+
+       if (resv_needed <= (resv_huge_pages - free_huge_pages))
+               resv_huge_pages += resv_needed;
+
+Note that the global lock hugetlb_lock is held when checking and adjusting
+these counters.
+
+If there were enough free huge pages and the global count resv_huge_pages
+was adjusted, then the reservation map associated with the mapping is
+modified to reflect the reservations.  In the case of a shared mapping, a
+file_region will exist that includes the range 'from' - 'to'.  For private
+mappings, no modifications are made to the reservation map as lack of an
+entry indicates a reservation exists.
+
+If hugetlb_reserve_pages() was successful, the global reservation count and
+reservation map associated with the mapping will be modified as required to
+ensure reservations exist for the range 'from' - 'to'.
+
+.. _consume_resv:
+
+Consuming Reservations/Allocating a Huge Page
+=============================================
+
+Reservations are consumed when huge pages associated with the reservations
+are allocated and instantiated in the corresponding mapping.  The allocation
+is performed within the routine alloc_huge_page()::
+
+       struct page *alloc_huge_page(struct vm_area_struct *vma,
+                                    unsigned long addr, int avoid_reserve)
+
+alloc_huge_page is passed a VMA pointer and a virtual address, so it can
+consult the reservation map to determine if a reservation exists.  In addition,
+alloc_huge_page takes the argument avoid_reserve which indicates reserves
+should not be used even if it appears they have been set aside for the
+specified address.  The avoid_reserve argument is most often used in the case
+of Copy on Write and Page Migration where additional copies of an existing
+page are being allocated.
+
+The helper routine vma_needs_reservation() is called to determine if a
+reservation exists for the address within the mapping(vma).  See the section
+:ref:`Reservation Map Helper Routines <resv_map_helpers>` for detailed
+information on what this routine does.
+The value returned from vma_needs_reservation() is generally
+0 or 1.  0 if a reservation exists for the address, 1 if no reservation exists.
+If a reservation does not exist, and there is a subpool associated with the
+mapping the subpool is consulted to determine if it contains reservations.
+If the subpool contains reservations, one can be used for this allocation.
+However, in every case the avoid_reserve argument overrides the use of
+a reservation for the allocation.  After determining whether a reservation
+exists and can be used for the allocation, the routine dequeue_huge_page_vma()
+is called.  This routine takes two arguments related to reservations:
+
+- avoid_reserve, this is the same value/argument passed to alloc_huge_page()
+- chg, even though this argument is of type long only the values 0 or 1 are
+  passed to dequeue_huge_page_vma.  If the value is 0, it indicates a
+  reservation exists (see the section "Memory Policy and Reservations" for
+  possible issues).  If the value is 1, it indicates a reservation does not
+  exist and the page must be taken from the global free pool if possible.
+
+The free lists associated with the memory policy of the VMA are searched for
+a free page.  If a page is found, the value free_huge_pages is decremented
+when the page is removed from the free list.  If there was a reservation
+associated with the page, the following adjustments are made::
+
+       SetPagePrivate(page);   /* Indicates allocating this page consumed
+                                * a reservation, and if an error is
+                                * encountered such that the page must be
+                                * freed, the reservation will be restored. */
+       resv_huge_pages--;      /* Decrement the global reservation count */
+
+Note, if no huge page can be found that satisfies the VMA's memory policy
+an attempt will be made to allocate one using the buddy allocator.  This
+brings up the issue of surplus huge pages and overcommit which is beyond
+the scope reservations.  Even if a surplus page is allocated, the same
+reservation based adjustments as above will be made: SetPagePrivate(page) and
+resv_huge_pages--.
+
+After obtaining a new huge page, (page)->private is set to the value of
+the subpool associated with the page if it exists.  This will be used for
+subpool accounting when the page is freed.
+
+The routine vma_commit_reservation() is then called to adjust the reserve
+map based on the consumption of the reservation.  In general, this involves
+ensuring the page is represented within a file_region structure of the region
+map.  For shared mappings where the reservation was present, an entry
+in the reserve map already existed so no change is made.  However, if there
+was no reservation in a shared mapping or this was a private mapping a new
+entry must be created.
+
+It is possible that the reserve map could have been changed between the call
+to vma_needs_reservation() at the beginning of alloc_huge_page() and the
+call to vma_commit_reservation() after the page was allocated.  This would
+be possible if hugetlb_reserve_pages was called for the same page in a shared
+mapping.  In such cases, the reservation count and subpool free page count
+will be off by one.  This rare condition can be identified by comparing the
+return value from vma_needs_reservation and vma_commit_reservation.  If such
+a race is detected, the subpool and global reserve counts are adjusted to
+compensate.  See the section
+:ref:`Reservation Map Helper Routines <resv_map_helpers>` for more
+information on these routines.
+
+
+Instantiate Huge Pages
+======================
+
+After huge page allocation, the page is typically added to the page tables
+of the allocating task.  Before this, pages in a shared mapping are added
+to the page cache and pages in private mappings are added to an anonymous
+reverse mapping.  In both cases, the PagePrivate flag is cleared.  Therefore,
+when a huge page that has been instantiated is freed no adjustment is made
+to the global reservation count (resv_huge_pages).
+
+
+Freeing Huge Pages
+==================
+
+Huge page freeing is performed by the routine free_huge_page().  This routine
+is the destructor for hugetlbfs compound pages.  As a result, it is only
+passed a pointer to the page struct.  When a huge page is freed, reservation
+accounting may need to be performed.  This would be the case if the page was
+associated with a subpool that contained reserves, or the page is being freed
+on an error path where a global reserve count must be restored.
+
+The page->private field points to any subpool associated with the page.
+If the PagePrivate flag is set, it indicates the global reserve count should
+be adjusted (see the section
+:ref:`Consuming Reservations/Allocating a Huge Page <consume_resv>`
+for information on how these are set).
+
+The routine first calls hugepage_subpool_put_pages() for the page.  If this
+routine returns a value of 0 (which does not equal the value passed 1) it
+indicates reserves are associated with the subpool, and this newly free page
+must be used to keep the number of subpool reserves above the minimum size.
+Therefore, the global resv_huge_pages counter is incremented in this case.
+
+If the PagePrivate flag was set in the page, the global resv_huge_pages counter
+will always be incremented.
+
+.. _sub_pool_resv:
+
+Subpool Reservations
+====================
+
+There is a struct hstate associated with each huge page size.  The hstate
+tracks all huge pages of the specified size.  A subpool represents a subset
+of pages within a hstate that is associated with a mounted hugetlbfs
+filesystem.
+
+When a hugetlbfs filesystem is mounted a min_size option can be specified
+which indicates the minimum number of huge pages required by the filesystem.
+If this option is specified, the number of huge pages corresponding to
+min_size are reserved for use by the filesystem.  This number is tracked in
+the min_hpages field of a struct hugepage_subpool.  At mount time,
+hugetlb_acct_memory(min_hpages) is called to reserve the specified number of
+huge pages.  If they can not be reserved, the mount fails.
+
+The routines hugepage_subpool_get/put_pages() are called when pages are
+obtained from or released back to a subpool.  They perform all subpool
+accounting, and track any reservations associated with the subpool.
+hugepage_subpool_get/put_pages are passed the number of huge pages by which
+to adjust the subpool 'used page' count (down for get, up for put).  Normally,
+they return the same value that was passed or an error if not enough pages
+exist in the subpool.
+
+However, if reserves are associated with the subpool a return value less
+than the passed value may be returned.  This return value indicates the
+number of additional global pool adjustments which must be made.  For example,
+suppose a subpool contains 3 reserved huge pages and someone asks for 5.
+The 3 reserved pages associated with the subpool can be used to satisfy part
+of the request.  But, 2 pages must be obtained from the global pools.  To
+relay this information to the caller, the value 2 is returned.  The caller
+is then responsible for attempting to obtain the additional two pages from
+the global pools.
+
+
+COW and Reservations
+====================
+
+Since shared mappings all point to and use the same underlying pages, the
+biggest reservation concern for COW is private mappings.  In this case,
+two tasks can be pointing at the same previously allocated page.  One task
+attempts to write to the page, so a new page must be allocated so that each
+task points to its own page.
+
+When the page was originally allocated, the reservation for that page was
+consumed.  When an attempt to allocate a new page is made as a result of
+COW, it is possible that no free huge pages are free and the allocation
+will fail.
+
+When the private mapping was originally created, the owner of the mapping
+was noted by setting the HPAGE_RESV_OWNER bit in the pointer to the reservation
+map of the owner.  Since the owner created the mapping, the owner owns all
+the reservations associated with the mapping.  Therefore, when a write fault
+occurs and there is no page available, different action is taken for the owner
+and non-owner of the reservation.
+
+In the case where the faulting task is not the owner, the fault will fail and
+the task will typically receive a SIGBUS.
+
+If the owner is the faulting task, we want it to succeed since it owned the
+original reservation.  To accomplish this, the page is unmapped from the
+non-owning task.  In this way, the only reference is from the owning task.
+In addition, the HPAGE_RESV_UNMAPPED bit is set in the reservation map pointer
+of the non-owning task.  The non-owning task may receive a SIGBUS if it later
+faults on a non-present page.  But, the original owner of the
+mapping/reservation will behave as expected.
+
+
+.. _resv_map_modifications:
+
+Reservation Map Modifications
+=============================
+
+The following low level routines are used to make modifications to a
+reservation map.  Typically, these routines are not called directly.  Rather,
+a reservation map helper routine is called which calls one of these low level
+routines.  These low level routines are fairly well documented in the source
+code (mm/hugetlb.c).  These routines are::
+
+       long region_chg(struct resv_map *resv, long f, long t);
+       long region_add(struct resv_map *resv, long f, long t);
+       void region_abort(struct resv_map *resv, long f, long t);
+       long region_count(struct resv_map *resv, long f, long t);
+
+Operations on the reservation map typically involve two operations:
+
+1) region_chg() is called to examine the reserve map and determine how
+   many pages in the specified range [f, t) are NOT currently represented.
+
+   The calling code performs global checks and allocations to determine if
+   there are enough huge pages for the operation to succeed.
+
+2)
+  a) If the operation can succeed, region_add() is called to actually modify
+     the reservation map for the same range [f, t) previously passed to
+     region_chg().
+  b) If the operation can not succeed, region_abort is called for the same
+     range [f, t) to abort the operation.
+
+Note that this is a two step process where region_add() and region_abort()
+are guaranteed to succeed after a prior call to region_chg() for the same
+range.  region_chg() is responsible for pre-allocating any data structures
+necessary to ensure the subsequent operations (specifically region_add()))
+will succeed.
+
+As mentioned above, region_chg() determines the number of pages in the range
+which are NOT currently represented in the map.  This number is returned to
+the caller.  region_add() returns the number of pages in the range added to
+the map.  In most cases, the return value of region_add() is the same as the
+return value of region_chg().  However, in the case of shared mappings it is
+possible for changes to the reservation map to be made between the calls to
+region_chg() and region_add().  In this case, the return value of region_add()
+will not match the return value of region_chg().  It is likely that in such
+cases global counts and subpool accounting will be incorrect and in need of
+adjustment.  It is the responsibility of the caller to check for this condition
+and make the appropriate adjustments.
+
+The routine region_del() is called to remove regions from a reservation map.
+It is typically called in the following situations:
+
+- When a file in the hugetlbfs filesystem is being removed, the inode will
+  be released and the reservation map freed.  Before freeing the reservation
+  map, all the individual file_region structures must be freed.  In this case
+  region_del is passed the range [0, LONG_MAX).
+- When a hugetlbfs file is being truncated.  In this case, all allocated pages
+  after the new file size must be freed.  In addition, any file_region entries
+  in the reservation map past the new end of file must be deleted.  In this
+  case, region_del is passed the range [new_end_of_file, LONG_MAX).
+- When a hole is being punched in a hugetlbfs file.  In this case, huge pages
+  are removed from the middle of the file one at a time.  As the pages are
+  removed, region_del() is called to remove the corresponding entry from the
+  reservation map.  In this case, region_del is passed the range
+  [page_idx, page_idx + 1).
+
+In every case, region_del() will return the number of pages removed from the
+reservation map.  In VERY rare cases, region_del() can fail.  This can only
+happen in the hole punch case where it has to split an existing file_region
+entry and can not allocate a new structure.  In this error case, region_del()
+will return -ENOMEM.  The problem here is that the reservation map will
+indicate that there is a reservation for the page.  However, the subpool and
+global reservation counts will not reflect the reservation.  To handle this
+situation, the routine hugetlb_fix_reserve_counts() is called to adjust the
+counters so that they correspond with the reservation map entry that could
+not be deleted.
+
+region_count() is called when unmapping a private huge page mapping.  In
+private mappings, the lack of a entry in the reservation map indicates that
+a reservation exists.  Therefore, by counting the number of entries in the
+reservation map we know how many reservations were consumed and how many are
+outstanding (outstanding = (end - start) - region_count(resv, start, end)).
+Since the mapping is going away, the subpool and global reservation counts
+are decremented by the number of outstanding reservations.
+
+.. _resv_map_helpers:
+
+Reservation Map Helper Routines
+===============================
+
+Several helper routines exist to query and modify the reservation maps.
+These routines are only interested with reservations for a specific huge
+page, so they just pass in an address instead of a range.  In addition,
+they pass in the associated VMA.  From the VMA, the type of mapping (private
+or shared) and the location of the reservation map (inode or VMA) can be
+determined.  These routines simply call the underlying routines described
+in the section "Reservation Map Modifications".  However, they do take into
+account the 'opposite' meaning of reservation map entries for private and
+shared mappings and hide this detail from the caller::
+
+       long vma_needs_reservation(struct hstate *h,
+                                  struct vm_area_struct *vma,
+                                  unsigned long addr)
+
+This routine calls region_chg() for the specified page.  If no reservation
+exists, 1 is returned.  If a reservation exists, 0 is returned::
+
+       long vma_commit_reservation(struct hstate *h,
+                                   struct vm_area_struct *vma,
+                                   unsigned long addr)
+
+This calls region_add() for the specified page.  As in the case of region_chg
+and region_add, this routine is to be called after a previous call to
+vma_needs_reservation.  It will add a reservation entry for the page.  It
+returns 1 if the reservation was added and 0 if not.  The return value should
+be compared with the return value of the previous call to
+vma_needs_reservation.  An unexpected difference indicates the reservation
+map was modified between calls::
+
+       void vma_end_reservation(struct hstate *h,
+                                struct vm_area_struct *vma,
+                                unsigned long addr)
+
+This calls region_abort() for the specified page.  As in the case of region_chg
+and region_abort, this routine is to be called after a previous call to
+vma_needs_reservation.  It will abort/end the in progress reservation add
+operation::
+
+       long vma_add_reservation(struct hstate *h,
+                                struct vm_area_struct *vma,
+                                unsigned long addr)
+
+This is a special wrapper routine to help facilitate reservation cleanup
+on error paths.  It is only called from the routine restore_reserve_on_error().
+This routine is used in conjunction with vma_needs_reservation in an attempt
+to add a reservation to the reservation map.  It takes into account the
+different reservation map semantics for private and shared mappings.  Hence,
+region_add is called for shared mappings (as an entry present in the map
+indicates a reservation), and region_del is called for private mappings (as
+the absence of an entry in the map indicates a reservation).  See the section
+"Reservation cleanup in error paths" for more information on what needs to
+be done on error paths.
+
+
+Reservation Cleanup in Error Paths
+==================================
+
+As mentioned in the section
+:ref:`Reservation Map Helper Routines <resv_map_helpers>`, reservation
+map modifications are performed in two steps.  First vma_needs_reservation
+is called before a page is allocated.  If the allocation is successful,
+then vma_commit_reservation is called.  If not, vma_end_reservation is called.
+Global and subpool reservation counts are adjusted based on success or failure
+of the operation and all is well.
+
+Additionally, after a huge page is instantiated the PagePrivate flag is
+cleared so that accounting when the page is ultimately freed is correct.
+
+However, there are several instances where errors are encountered after a huge
+page is allocated but before it is instantiated.  In this case, the page
+allocation has consumed the reservation and made the appropriate subpool,
+reservation map and global count adjustments.  If the page is freed at this
+time (before instantiation and clearing of PagePrivate), then free_huge_page
+will increment the global reservation count.  However, the reservation map
+indicates the reservation was consumed.  This resulting inconsistent state
+will cause the 'leak' of a reserved huge page.  The global reserve count will
+be  higher than it should and prevent allocation of a pre-allocated page.
+
+The routine restore_reserve_on_error() attempts to handle this situation.  It
+is fairly well documented.  The intention of this routine is to restore
+the reservation map to the way it was before the page allocation.   In this
+way, the state of the reservation map will correspond to the global reservation
+count after the page is freed.
+
+The routine restore_reserve_on_error itself may encounter errors while
+attempting to restore the reservation map entry.  In this case, it will
+simply clear the PagePrivate flag of the page.  In this way, the global
+reserve count will not be incremented when the page is freed.  However, the
+reservation map will continue to look as though the reservation was consumed.
+A page can still be allocated for the address, but it will not use a reserved
+page as originally intended.
+
+There is some code (most notably userfaultfd) which can not call
+restore_reserve_on_error.  In this case, it simply modifies the PagePrivate
+so that a reservation will not be leaked when the huge page is freed.
+
+
+Reservations and Memory Policy
+==============================
+Per-node huge page lists existed in struct hstate when git was first used
+to manage Linux code.  The concept of reservations was added some time later.
+When reservations were added, no attempt was made to take memory policy
+into account.  While cpusets are not exactly the same as memory policy, this
+comment in hugetlb_acct_memory sums up the interaction between reservations
+and cpusets/memory policy::
+
+       /*
+        * When cpuset is configured, it breaks the strict hugetlb page
+        * reservation as the accounting is done on a global variable. Such
+        * reservation is completely rubbish in the presence of cpuset because
+        * the reservation is not checked against page availability for the
+        * current cpuset. Application can still potentially OOM'ed by kernel
+        * with lack of free htlb page in cpuset that the task is in.
+        * Attempt to enforce strict accounting with cpuset is almost
+        * impossible (or too ugly) because cpuset is too fluid that
+        * task or memory node can be dynamically moved between cpusets.
+        *
+        * The change of semantics for shared hugetlb mapping with cpuset is
+        * undesirable. However, in order to preserve some of the semantics,
+        * we fall back to check against current free page availability as
+        * a best attempt and hopefully to minimize the impact of changing
+        * semantics that cpuset has.
+        */
+
+Huge page reservations were added to prevent unexpected page allocation
+failures (OOM) at page fault time.  However, if an application makes use
+of cpusets or memory policy there is no guarantee that huge pages will be
+available on the required nodes.  This is true even if there are a sufficient
+number of global reservations.
+
+Hugetlbfs regression testing
+============================
+
+The most complete set of hugetlb tests are in the libhugetlbfs repository.
+If you modify any hugetlb related code, use the libhugetlbfs test suite
+to check for regressions.  In addition, if you add any new hugetlb
+functionality, please add appropriate tests to libhugetlbfs.
+
+--
+Mike Kravetz, 7 April 2017
diff --git a/Documentation/mm/hwpoison.rst b/Documentation/mm/hwpoison.rst
new file mode 100644 (file)
index 0000000..b9d5253
--- /dev/null
@@ -0,0 +1,184 @@
+.. hwpoison:
+
+========
+hwpoison
+========
+
+What is hwpoison?
+=================
+
+Upcoming Intel CPUs have support for recovering from some memory errors
+(``MCA recovery``). This requires the OS to declare a page "poisoned",
+kill the processes associated with it and avoid using it in the future.
+
+This patchkit implements the necessary infrastructure in the VM.
+
+To quote the overview comment::
+
+       High level machine check handler. Handles pages reported by the
+       hardware as being corrupted usually due to a 2bit ECC memory or cache
+       failure.
+
+       This focusses on pages detected as corrupted in the background.
+       When the current CPU tries to consume corruption the currently
+       running process can just be killed directly instead. This implies
+       that if the error cannot be handled for some reason it's safe to
+       just ignore it because no corruption has been consumed yet. Instead
+       when that happens another machine check will happen.
+
+       Handles page cache pages in various states. The tricky part
+       here is that we can access any page asynchronous to other VM
+       users, because memory failures could happen anytime and anywhere,
+       possibly violating some of their assumptions. This is why this code
+       has to be extremely careful. Generally it tries to use normal locking
+       rules, as in get the standard locks, even if that means the
+       error handling takes potentially a long time.
+
+       Some of the operations here are somewhat inefficient and have non
+       linear algorithmic complexity, because the data structures have not
+       been optimized for this case. This is in particular the case
+       for the mapping from a vma to a process. Since this case is expected
+       to be rare we hope we can get away with this.
+
+The code consists of a the high level handler in mm/memory-failure.c,
+a new page poison bit and various checks in the VM to handle poisoned
+pages.
+
+The main target right now is KVM guests, but it works for all kinds
+of applications. KVM support requires a recent qemu-kvm release.
+
+For the KVM use there was need for a new signal type so that
+KVM can inject the machine check into the guest with the proper
+address. This in theory allows other applications to handle
+memory failures too. The expection is that near all applications
+won't do that, but some very specialized ones might.
+
+Failure recovery modes
+======================
+
+There are two (actually three) modes memory failure recovery can be in:
+
+vm.memory_failure_recovery sysctl set to zero:
+       All memory failures cause a panic. Do not attempt recovery.
+
+early kill
+       (can be controlled globally and per process)
+       Send SIGBUS to the application as soon as the error is detected
+       This allows applications who can process memory errors in a gentle
+       way (e.g. drop affected object)
+       This is the mode used by KVM qemu.
+
+late kill
+       Send SIGBUS when the application runs into the corrupted page.
+       This is best for memory error unaware applications and default
+       Note some pages are always handled as late kill.
+
+User control
+============
+
+vm.memory_failure_recovery
+       See sysctl.txt
+
+vm.memory_failure_early_kill
+       Enable early kill mode globally
+
+PR_MCE_KILL
+       Set early/late kill mode/revert to system default
+
+       arg1: PR_MCE_KILL_CLEAR:
+               Revert to system default
+       arg1: PR_MCE_KILL_SET:
+               arg2 defines thread specific mode
+
+               PR_MCE_KILL_EARLY:
+                       Early kill
+               PR_MCE_KILL_LATE:
+                       Late kill
+               PR_MCE_KILL_DEFAULT
+                       Use system global default
+
+       Note that if you want to have a dedicated thread which handles
+       the SIGBUS(BUS_MCEERR_AO) on behalf of the process, you should
+       call prctl(PR_MCE_KILL_EARLY) on the designated thread. Otherwise,
+       the SIGBUS is sent to the main thread.
+
+PR_MCE_KILL_GET
+       return current mode
+
+Testing
+=======
+
+* madvise(MADV_HWPOISON, ....) (as root) - Poison a page in the
+  process for testing
+
+* hwpoison-inject module through debugfs ``/sys/kernel/debug/hwpoison/``
+
+  corrupt-pfn
+       Inject hwpoison fault at PFN echoed into this file. This does
+       some early filtering to avoid corrupted unintended pages in test suites.
+
+  unpoison-pfn
+       Software-unpoison page at PFN echoed into this file. This way
+       a page can be reused again.  This only works for Linux
+       injected failures, not for real memory failures. Once any hardware
+       memory failure happens, this feature is disabled.
+
+  Note these injection interfaces are not stable and might change between
+  kernel versions
+
+  corrupt-filter-dev-major, corrupt-filter-dev-minor
+       Only handle memory failures to pages associated with the file
+       system defined by block device major/minor.  -1U is the
+       wildcard value.  This should be only used for testing with
+       artificial injection.
+
+  corrupt-filter-memcg
+       Limit injection to pages owned by memgroup. Specified by inode
+       number of the memcg.
+
+       Example::
+
+               mkdir /sys/fs/cgroup/mem/hwpoison
+
+               usemem -m 100 -s 1000 &
+               echo `jobs -p` > /sys/fs/cgroup/mem/hwpoison/tasks
+
+               memcg_ino=$(ls -id /sys/fs/cgroup/mem/hwpoison | cut -f1 -d' ')
+               echo $memcg_ino > /debug/hwpoison/corrupt-filter-memcg
+
+               page-types -p `pidof init`   --hwpoison  # shall do nothing
+               page-types -p `pidof usemem` --hwpoison  # poison its pages
+
+  corrupt-filter-flags-mask, corrupt-filter-flags-value
+       When specified, only poison pages if ((page_flags & mask) ==
+       value).  This allows stress testing of many kinds of
+       pages. The page_flags are the same as in /proc/kpageflags. The
+       flag bits are defined in include/linux/kernel-page-flags.h and
+       documented in Documentation/admin-guide/mm/pagemap.rst
+
+* Architecture specific MCE injector
+
+  x86 has mce-inject, mce-test
+
+  Some portable hwpoison test programs in mce-test, see below.
+
+References
+==========
+
+http://halobates.de/mce-lc09-2.pdf
+       Overview presentation from LinuxCon 09
+
+git://git.kernel.org/pub/scm/utils/cpu/mce/mce-test.git
+       Test suite (hwpoison specific portable tests in tsrc)
+
+git://git.kernel.org/pub/scm/utils/cpu/mce/mce-inject.git
+       x86 specific injector
+
+
+Limitations
+===========
+- Not all page types are supported and never will. Most kernel internal
+  objects cannot be recovered, only LRU pages for now.
+
+---
+Andi Kleen, Oct 2009
diff --git a/Documentation/mm/index.rst b/Documentation/mm/index.rst
new file mode 100644 (file)
index 0000000..575ccd4
--- /dev/null
@@ -0,0 +1,68 @@
+=====================================
+Linux Memory Management Documentation
+=====================================
+
+Memory Management Guide
+=======================
+
+This is a guide to understanding the memory management subsystem
+of Linux.  If you are looking for advice on simply allocating memory,
+see the :ref:`memory_allocation`.  For controlling and tuning guides,
+see the :doc:`admin guide <../admin-guide/mm/index>`.
+
+.. toctree::
+   :maxdepth: 1
+
+   physical_memory
+   page_tables
+   process_addrs
+   bootmem
+   page_allocation
+   vmalloc
+   slab
+   highmem
+   page_reclaim
+   swap
+   page_cache
+   shmfs
+   oom
+
+Legacy Documentation
+====================
+
+This is a collection of older documents about the Linux memory management
+(MM) subsystem internals with different level of details ranging from
+notes and mailing list responses for elaborating descriptions of data
+structures and algorithms.  It should all be integrated nicely into the
+above structured documentation, or deleted if it has served its purpose.
+
+.. toctree::
+   :maxdepth: 1
+
+   active_mm
+   arch_pgtable_helpers
+   balance
+   damon/index
+   free_page_reporting
+   frontswap
+   hmm
+   hwpoison
+   hugetlbfs_reserv
+   ksm
+   memory-model
+   mmu_notifier
+   numa
+   overcommit-accounting
+   page_migration
+   page_frags
+   page_owner
+   page_table_check
+   remap_file_pages
+   slub
+   split_page_table_lock
+   transhuge
+   unevictable-lru
+   vmalloced-kernel-stacks
+   vmemmap_dedup
+   z3fold
+   zsmalloc
diff --git a/Documentation/mm/ksm.rst b/Documentation/mm/ksm.rst
new file mode 100644 (file)
index 0000000..9e37add
--- /dev/null
@@ -0,0 +1,87 @@
+.. _ksm:
+
+=======================
+Kernel Samepage Merging
+=======================
+
+KSM is a memory-saving de-duplication feature, enabled by CONFIG_KSM=y,
+added to the Linux kernel in 2.6.32.  See ``mm/ksm.c`` for its implementation,
+and http://lwn.net/Articles/306704/ and https://lwn.net/Articles/330589/
+
+The userspace interface of KSM is described in :ref:`Documentation/admin-guide/mm/ksm.rst <admin_guide_ksm>`
+
+Design
+======
+
+Overview
+--------
+
+.. kernel-doc:: mm/ksm.c
+   :DOC: Overview
+
+Reverse mapping
+---------------
+KSM maintains reverse mapping information for KSM pages in the stable
+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 rmap_item and the ``page->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".
+
+Every "chain" and all "dups" linked into a "chain" enforce the
+invariant that they represent the same write protected memory content,
+even if each "dup" will be pointed by a different KSM page copy of
+that content.
+
+This way the stable tree lookup computational complexity is unaffected
+if compared to an unlimited list of reverse mappings. It is still
+enforced that there cannot be KSM page content duplicates in the
+stable tree itself.
+
+The deduplication limit enforced by ``max_page_sharing`` is required
+to avoid the virtual memory rmap lists to grow too large. The rmap
+walk has O(N) complexity where N is the number of rmap_items
+(i.e. virtual mappings) that are sharing the page, which is in turn
+capped by ``max_page_sharing``. So this effectively spreads the linear
+O(N) computational complexity from rmap walk context over different
+KSM pages. The ksmd walk over the stable_node "chains" is also O(N),
+but N is the number of stable_node "dups", not the number of
+rmap_items, so it has not a significant impact on ksmd performance. In
+practice the best stable_node "dup" candidate will be kept and found
+at the head of the "dups" list.
+
+High values of ``max_page_sharing`` result in faster memory merging
+(because there will be fewer stable_node dups queued into the
+stable_node chain->hlist to check for pruning) and higher
+deduplication factor at the expense of slower worst case for rmap
+walks for any KSM page which can happen during swapping, compaction,
+NUMA balancing and page migration.
+
+The ``stable_node_dups/stable_node_chains`` ratio is also affected by the
+``max_page_sharing`` tunable, and an high ratio may indicate fragmentation
+in the stable_node dups, which could be solved by introducing
+fragmentation algorithms in ksmd which would refile rmap_items from
+one stable_node dup to another stable_node dup, in order to free up
+stable_node "dups" with few rmap_items in them, but that may increase
+the ksmd CPU usage and possibly slowdown the readonly computations on
+the KSM pages of the applications.
+
+The whole list of stable_node "dups" linked in the stable_node
+"chains" is scanned periodically in order to prune stale stable_nodes.
+The frequency of such scans is defined by
+``stable_node_chains_prune_millisecs`` sysfs tunable.
+
+Reference
+---------
+.. kernel-doc:: mm/ksm.c
+   :functions: mm_slot ksm_scan stable_node rmap_item
+
+--
+Izik Eidus,
+Hugh Dickins, 17 Nov 2009
diff --git a/Documentation/mm/memory-model.rst b/Documentation/mm/memory-model.rst
new file mode 100644 (file)
index 0000000..3779e56
--- /dev/null
@@ -0,0 +1,177 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. _physical_memory_model:
+
+=====================
+Physical Memory Model
+=====================
+
+Physical memory in a system may be addressed in different ways. The
+simplest case is when the physical memory starts at address 0 and
+spans a contiguous range up to the maximal address. It could be,
+however, that this range contains small holes that are not accessible
+for the CPU. Then there could be several contiguous ranges at
+completely distinct addresses. And, don't forget about NUMA, where
+different memory banks are attached to different CPUs.
+
+Linux abstracts this diversity using one of the two memory models:
+FLATMEM and SPARSEMEM. Each architecture defines what
+memory models it supports, what the default memory model is and
+whether it is possible to manually override that default.
+
+All the memory models track the status of physical page frames using
+struct page arranged in one or more arrays.
+
+Regardless of the selected memory model, there exists one-to-one
+mapping between the physical page frame number (PFN) and the
+corresponding `struct page`.
+
+Each memory model defines :c:func:`pfn_to_page` and :c:func:`page_to_pfn`
+helpers that allow the conversion from PFN to `struct page` and vice
+versa.
+
+FLATMEM
+=======
+
+The simplest memory model is FLATMEM. This model is suitable for
+non-NUMA systems with contiguous, or mostly contiguous, physical
+memory.
+
+In the FLATMEM memory model, there is a global `mem_map` array that
+maps the entire physical memory. For most architectures, the holes
+have entries in the `mem_map` array. The `struct page` objects
+corresponding to the holes are never fully initialized.
+
+To allocate the `mem_map` array, architecture specific setup code should
+call :c:func:`free_area_init` function. Yet, the mappings array is not
+usable until the call to :c:func:`memblock_free_all` that hands all the
+memory to the page allocator.
+
+An architecture may free parts of the `mem_map` array that do not cover the
+actual physical pages. In such case, the architecture specific
+:c:func:`pfn_valid` implementation should take the holes in the
+`mem_map` into account.
+
+With FLATMEM, the conversion between a PFN and the `struct page` is
+straightforward: `PFN - ARCH_PFN_OFFSET` is an index to the
+`mem_map` array.
+
+The `ARCH_PFN_OFFSET` defines the first page frame number for
+systems with physical memory starting at address different from 0.
+
+SPARSEMEM
+=========
+
+SPARSEMEM is the most versatile memory model available in Linux and it
+is the only memory model that supports several advanced features such
+as hot-plug and hot-remove of the physical memory, alternative memory
+maps for non-volatile memory devices and deferred initialization of
+the memory map for larger systems.
+
+The SPARSEMEM model presents the physical memory as a collection of
+sections. A section is represented with struct mem_section
+that contains `section_mem_map` that is, logically, a pointer to an
+array of struct pages. However, it is stored with some other magic
+that aids the sections management. The section size and maximal number
+of section is specified using `SECTION_SIZE_BITS` and
+`MAX_PHYSMEM_BITS` constants defined by each architecture that
+supports SPARSEMEM. While `MAX_PHYSMEM_BITS` is an actual width of a
+physical address that an architecture supports, the
+`SECTION_SIZE_BITS` is an arbitrary value.
+
+The maximal number of sections is denoted `NR_MEM_SECTIONS` and
+defined as
+
+.. math::
+
+   NR\_MEM\_SECTIONS = 2 ^ {(MAX\_PHYSMEM\_BITS - SECTION\_SIZE\_BITS)}
+
+The `mem_section` objects are arranged in a two-dimensional array
+called `mem_sections`. The size and placement of this array depend
+on `CONFIG_SPARSEMEM_EXTREME` and the maximal possible number of
+sections:
+
+* When `CONFIG_SPARSEMEM_EXTREME` is disabled, the `mem_sections`
+  array is static and has `NR_MEM_SECTIONS` rows. Each row holds a
+  single `mem_section` object.
+* When `CONFIG_SPARSEMEM_EXTREME` is enabled, the `mem_sections`
+  array is dynamically allocated. Each row contains PAGE_SIZE worth of
+  `mem_section` objects and the number of rows is calculated to fit
+  all the memory sections.
+
+The architecture setup code should call sparse_init() to
+initialize the memory sections and the memory maps.
+
+With SPARSEMEM there are two possible ways to convert a PFN to the
+corresponding `struct page` - a "classic sparse" and "sparse
+vmemmap". The selection is made at build time and it is determined by
+the value of `CONFIG_SPARSEMEM_VMEMMAP`.
+
+The classic sparse encodes the section number of a page in page->flags
+and uses high bits of a PFN to access the section that maps that page
+frame. Inside a section, the PFN is the index to the array of pages.
+
+The sparse vmemmap uses a virtually mapped memory map to optimize
+pfn_to_page and page_to_pfn operations. There is a global `struct
+page *vmemmap` pointer that points to a virtually contiguous array of
+`struct page` objects. A PFN is an index to that array and the
+offset of the `struct page` from `vmemmap` is the PFN of that
+page.
+
+To use vmemmap, an architecture has to reserve a range of virtual
+addresses that will map the physical pages containing the memory
+map and make sure that `vmemmap` points to that range. In addition,
+the architecture should implement :c:func:`vmemmap_populate` method
+that will allocate the physical memory and create page tables for the
+virtual memory map. If an architecture does not have any special
+requirements for the vmemmap mappings, it can use default
+:c:func:`vmemmap_populate_basepages` provided by the generic memory
+management.
+
+The virtually mapped memory map allows storing `struct page` objects
+for persistent memory devices in pre-allocated storage on those
+devices. This storage is represented with struct vmem_altmap
+that is eventually passed to vmemmap_populate() through a long chain
+of function calls. The vmemmap_populate() implementation may use the
+`vmem_altmap` along with :c:func:`vmemmap_alloc_block_buf` helper to
+allocate memory map on the persistent memory device.
+
+ZONE_DEVICE
+===========
+The `ZONE_DEVICE` facility builds upon `SPARSEMEM_VMEMMAP` to offer
+`struct page` `mem_map` services for device driver identified physical
+address ranges. The "device" aspect of `ZONE_DEVICE` relates to the fact
+that the page objects for these address ranges are never marked online,
+and that a reference must be taken against the device, not just the page
+to keep the memory pinned for active use. `ZONE_DEVICE`, via
+:c:func:`devm_memremap_pages`, performs just enough memory hotplug to
+turn on :c:func:`pfn_to_page`, :c:func:`page_to_pfn`, and
+:c:func:`get_user_pages` service for the given range of pfns. Since the
+page reference count never drops below 1 the page is never tracked as
+free memory and the page's `struct list_head lru` space is repurposed
+for back referencing to the host device / driver that mapped the memory.
+
+While `SPARSEMEM` presents memory as a collection of sections,
+optionally collected into memory blocks, `ZONE_DEVICE` users have a need
+for smaller granularity of populating the `mem_map`. Given that
+`ZONE_DEVICE` memory is never marked online it is subsequently never
+subject to its memory ranges being exposed through the sysfs memory
+hotplug api on memory block boundaries. The implementation relies on
+this lack of user-api constraint to allow sub-section sized memory
+ranges to be specified to :c:func:`arch_add_memory`, the top-half of
+memory hotplug. Sub-section support allows for 2MB as the cross-arch
+common alignment granularity for :c:func:`devm_memremap_pages`.
+
+The users of `ZONE_DEVICE` are:
+
+* pmem: Map platform persistent memory to be used as a direct-I/O target
+  via DAX mappings.
+
+* hmm: Extend `ZONE_DEVICE` with `->page_fault()` and `->page_free()`
+  event callbacks to allow a device-driver to coordinate memory management
+  events related to device-memory, typically GPU memory. See
+  Documentation/mm/hmm.rst.
+
+* p2pdma: Create `struct page` objects to allow peer devices in a
+  PCI/-E topology to coordinate direct-DMA operations between themselves,
+  i.e. bypass host memory.
diff --git a/Documentation/mm/mmu_notifier.rst b/Documentation/mm/mmu_notifier.rst
new file mode 100644 (file)
index 0000000..df5d777
--- /dev/null
@@ -0,0 +1,99 @@
+.. _mmu_notifier:
+
+When do you need to notify inside page table lock ?
+===================================================
+
+When clearing a pte/pmd we are given a choice to notify the event through
+(notify version of \*_clear_flush call mmu_notifier_invalidate_range) under
+the page table lock. But that notification is not necessary in all cases.
+
+For secondary TLB (non CPU TLB) like IOMMU TLB or device TLB (when device use
+thing like ATS/PASID to get the IOMMU to walk the CPU page table to access a
+process virtual address space). There is only 2 cases when you need to notify
+those secondary TLB while holding page table lock when clearing a pte/pmd:
+
+  A) page backing address is free before mmu_notifier_invalidate_range_end()
+  B) a page table entry is updated to point to a new page (COW, write fault
+     on zero page, __replace_page(), ...)
+
+Case A is obvious you do not want to take the risk for the device to write to
+a page that might now be used by some completely different task.
+
+Case B is more subtle. For correctness it requires the following sequence to
+happen:
+
+  - take page table lock
+  - clear page table entry and notify ([pmd/pte]p_huge_clear_flush_notify())
+  - set page table entry to point to new page
+
+If clearing the page table entry is not followed by a notify before setting
+the new pte/pmd value then you can break memory model like C11 or C++11 for
+the device.
+
+Consider the following scenario (device use a feature similar to ATS/PASID):
+
+Two address addrA and addrB such that \|addrA - addrB\| >= PAGE_SIZE we assume
+they are write protected for COW (other case of B apply too).
+
+::
+
+ [Time N] --------------------------------------------------------------------
+ CPU-thread-0  {try to write to addrA}
+ CPU-thread-1  {try to write to addrB}
+ CPU-thread-2  {}
+ CPU-thread-3  {}
+ DEV-thread-0  {read addrA and populate device TLB}
+ DEV-thread-2  {read addrB and populate device TLB}
+ [Time N+1] ------------------------------------------------------------------
+ CPU-thread-0  {COW_step0: {mmu_notifier_invalidate_range_start(addrA)}}
+ CPU-thread-1  {COW_step0: {mmu_notifier_invalidate_range_start(addrB)}}
+ CPU-thread-2  {}
+ CPU-thread-3  {}
+ DEV-thread-0  {}
+ DEV-thread-2  {}
+ [Time N+2] ------------------------------------------------------------------
+ CPU-thread-0  {COW_step1: {update page table to point to new page for addrA}}
+ CPU-thread-1  {COW_step1: {update page table to point to new page for addrB}}
+ CPU-thread-2  {}
+ CPU-thread-3  {}
+ DEV-thread-0  {}
+ DEV-thread-2  {}
+ [Time N+3] ------------------------------------------------------------------
+ CPU-thread-0  {preempted}
+ CPU-thread-1  {preempted}
+ CPU-thread-2  {write to addrA which is a write to new page}
+ CPU-thread-3  {}
+ DEV-thread-0  {}
+ DEV-thread-2  {}
+ [Time N+3] ------------------------------------------------------------------
+ CPU-thread-0  {preempted}
+ CPU-thread-1  {preempted}
+ CPU-thread-2  {}
+ CPU-thread-3  {write to addrB which is a write to new page}
+ DEV-thread-0  {}
+ DEV-thread-2  {}
+ [Time N+4] ------------------------------------------------------------------
+ CPU-thread-0  {preempted}
+ CPU-thread-1  {COW_step3: {mmu_notifier_invalidate_range_end(addrB)}}
+ CPU-thread-2  {}
+ CPU-thread-3  {}
+ DEV-thread-0  {}
+ DEV-thread-2  {}
+ [Time N+5] ------------------------------------------------------------------
+ CPU-thread-0  {preempted}
+ CPU-thread-1  {}
+ CPU-thread-2  {}
+ CPU-thread-3  {}
+ DEV-thread-0  {read addrA from old page}
+ DEV-thread-2  {read addrB from new page}
+
+So here because at time N+2 the clear page table entry was not pair with a
+notification to invalidate the secondary TLB, the device see the new value for
+addrB before seeing the new value for addrA. This break total memory ordering
+for the device.
+
+When changing a pte to write protect or to point to a new write protected page
+with same content (KSM) it is fine to delay the mmu_notifier_invalidate_range
+call to mmu_notifier_invalidate_range_end() outside the page table lock. This
+is true even if the thread doing the page table update is preempted right after
+releasing page table lock but before call mmu_notifier_invalidate_range_end().
diff --git a/Documentation/mm/numa.rst b/Documentation/mm/numa.rst
new file mode 100644 (file)
index 0000000..99fdeca
--- /dev/null
@@ -0,0 +1,150 @@
+.. _numa:
+
+Started Nov 1999 by Kanoj Sarcar <kanoj@sgi.com>
+
+=============
+What is NUMA?
+=============
+
+This question can be answered from a couple of perspectives:  the
+hardware view and the Linux software view.
+
+From the hardware perspective, a NUMA system is a computer platform that
+comprises multiple components or assemblies each of which may contain 0
+or more CPUs, local memory, and/or IO buses.  For brevity and to
+disambiguate the hardware view of these physical components/assemblies
+from the software abstraction thereof, we'll call the components/assemblies
+'cells' in this document.
+
+Each of the 'cells' may be viewed as an SMP [symmetric multi-processor] subset
+of the system--although some components necessary for a stand-alone SMP system
+may not be populated on any given cell.   The cells of the NUMA system are
+connected together with some sort of system interconnect--e.g., a crossbar or
+point-to-point link are common types of NUMA system interconnects.  Both of
+these types of interconnects can be aggregated to create NUMA platforms with
+cells at multiple distances from other cells.
+
+For Linux, the NUMA platforms of interest are primarily what is known as Cache
+Coherent NUMA or ccNUMA systems.   With ccNUMA systems, all memory is visible
+to and accessible from any CPU attached to any cell and cache coherency
+is handled in hardware by the processor caches and/or the system interconnect.
+
+Memory access time and effective memory bandwidth varies depending on how far
+away the cell containing the CPU or IO bus making the memory access is from the
+cell containing the target memory.  For example, access to memory by CPUs
+attached to the same cell will experience faster access times and higher
+bandwidths than accesses to memory on other, remote cells.  NUMA platforms
+can have cells at multiple remote distances from any given cell.
+
+Platform vendors don't build NUMA systems just to make software developers'
+lives interesting.  Rather, this architecture is a means to provide scalable
+memory bandwidth.  However, to achieve scalable memory bandwidth, system and
+application software must arrange for a large majority of the memory references
+[cache misses] to be to "local" memory--memory on the same cell, if any--or
+to the closest cell with memory.
+
+This leads to the Linux software view of a NUMA system:
+
+Linux divides the system's hardware resources into multiple software
+abstractions called "nodes".  Linux maps the nodes onto the physical cells
+of the hardware platform, abstracting away some of the details for some
+architectures.  As with physical cells, software nodes may contain 0 or more
+CPUs, memory and/or IO buses.  And, again, memory accesses to memory on
+"closer" nodes--nodes that map to closer cells--will generally experience
+faster access times and higher effective bandwidth than accesses to more
+remote cells.
+
+For some architectures, such as x86, Linux will "hide" any node representing a
+physical cell that has no memory attached, and reassign any CPUs attached to
+that cell to a node representing a cell that does have memory.  Thus, on
+these architectures, one cannot assume that all CPUs that Linux associates with
+a given node will see the same local memory access times and bandwidth.
+
+In addition, for some architectures, again x86 is an example, Linux supports
+the emulation of additional nodes.  For NUMA emulation, linux will carve up
+the existing nodes--or the system memory for non-NUMA platforms--into multiple
+nodes.  Each emulated node will manage a fraction of the underlying cells'
+physical memory.  NUMA emluation is useful for testing NUMA kernel and
+application features on non-NUMA platforms, and as a sort of memory resource
+management mechanism when used together with cpusets.
+[see Documentation/admin-guide/cgroup-v1/cpusets.rst]
+
+For each node with memory, Linux constructs an independent memory management
+subsystem, complete with its own free page lists, in-use page lists, usage
+statistics and locks to mediate access.  In addition, Linux constructs for
+each memory zone [one or more of DMA, DMA32, NORMAL, HIGH_MEMORY, MOVABLE],
+an ordered "zonelist".  A zonelist specifies the zones/nodes to visit when a
+selected zone/node cannot satisfy the allocation request.  This situation,
+when a zone has no available memory to satisfy a request, is called
+"overflow" or "fallback".
+
+Because some nodes contain multiple zones containing different types of
+memory, Linux must decide whether to order the zonelists such that allocations
+fall back to the same zone type on a different node, or to a different zone
+type on the same node.  This is an important consideration because some zones,
+such as DMA or DMA32, represent relatively scarce resources.  Linux chooses
+a default Node ordered zonelist. This means it tries to fallback to other zones
+from the same node before using remote nodes which are ordered by NUMA distance.
+
+By default, Linux will attempt to satisfy memory allocation requests from the
+node to which the CPU that executes the request is assigned.  Specifically,
+Linux will attempt to allocate from the first node in the appropriate zonelist
+for the node where the request originates.  This is called "local allocation."
+If the "local" node cannot satisfy the request, the kernel will examine other
+nodes' zones in the selected zonelist looking for the first zone in the list
+that can satisfy the request.
+
+Local allocation will tend to keep subsequent access to the allocated memory
+"local" to the underlying physical resources and off the system interconnect--
+as long as the task on whose behalf the kernel allocated some memory does not
+later migrate away from that memory.  The Linux scheduler is aware of the
+NUMA topology of the platform--embodied in the "scheduling domains" data
+structures [see Documentation/scheduler/sched-domains.rst]--and the scheduler
+attempts to minimize task migration to distant scheduling domains.  However,
+the scheduler does not take a task's NUMA footprint into account directly.
+Thus, under sufficient imbalance, tasks can migrate between nodes, remote
+from their initial node and kernel data structures.
+
+System administrators and application designers can restrict a task's migration
+to improve NUMA locality using various CPU affinity command line interfaces,
+such as taskset(1) and numactl(1), and program interfaces such as
+sched_setaffinity(2).  Further, one can modify the kernel's default local
+allocation behavior using Linux NUMA memory policy. [see
+:ref:`Documentation/admin-guide/mm/numa_memory_policy.rst <numa_memory_policy>`].
+
+System administrators can restrict the CPUs and nodes' memories that a non-
+privileged user can specify in the scheduling or NUMA commands and functions
+using control groups and CPUsets.  [see Documentation/admin-guide/cgroup-v1/cpusets.rst]
+
+On architectures that do not hide memoryless nodes, Linux will include only
+zones [nodes] with memory in the zonelists.  This means that for a memoryless
+node the "local memory node"--the node of the first zone in CPU's node's
+zonelist--will not be the node itself.  Rather, it will be the node that the
+kernel selected as the nearest node with memory when it built the zonelists.
+So, default, local allocations will succeed with the kernel supplying the
+closest available memory.  This is a consequence of the same mechanism that
+allows such allocations to fallback to other nearby nodes when a node that
+does contain memory overflows.
+
+Some kernel allocations do not want or cannot tolerate this allocation fallback
+behavior.  Rather they want to be sure they get memory from the specified node
+or get notified that the node has no free memory.  This is usually the case when
+a subsystem allocates per CPU memory resources, for example.
+
+A typical model for making such an allocation is to obtain the node id of the
+node to which the "current CPU" is attached using one of the kernel's
+numa_node_id() or CPU_to_node() functions and then request memory from only
+the node id returned.  When such an allocation fails, the requesting subsystem
+may revert to its own fallback path.  The slab kernel memory allocator is an
+example of this.  Or, the subsystem may choose to disable or not to enable
+itself on allocation failure.  The kernel profiling subsystem is an example of
+this.
+
+If the architecture supports--does not hide--memoryless nodes, then CPUs
+attached to memoryless nodes would always incur the fallback path overhead
+or some subsystems would fail to initialize if they attempted to allocated
+memory exclusively from a node without memory.  To support such
+architectures transparently, kernel subsystems can use the numa_mem_id()
+or cpu_to_mem() function to locate the "local memory node" for the calling or
+specified CPU.  Again, this is the same node from which default, local page
+allocations will be attempted.
diff --git a/Documentation/mm/oom.rst b/Documentation/mm/oom.rst
new file mode 100644 (file)
index 0000000..18e9e40
--- /dev/null
@@ -0,0 +1,5 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================
+Out Of Memory Handling
+======================
diff --git a/Documentation/mm/overcommit-accounting.rst b/Documentation/mm/overcommit-accounting.rst
new file mode 100644 (file)
index 0000000..a4895d6
--- /dev/null
@@ -0,0 +1,86 @@
+=====================
+Overcommit Accounting
+=====================
+
+The Linux kernel supports the following overcommit handling modes
+
+0
+       Heuristic overcommit handling. Obvious overcommits of address
+       space are refused. Used for a typical system. It ensures a
+       seriously wild allocation fails while allowing overcommit to
+       reduce swap usage.  root is allowed to allocate slightly more
+       memory in this mode. This is the default.
+
+1
+       Always overcommit. Appropriate for some scientific
+       applications. Classic example is code using sparse arrays and
+       just relying on the virtual memory consisting almost entirely
+       of zero pages.
+
+2
+       Don't overcommit. The total address space commit for the
+       system is not permitted to exceed swap + a configurable amount
+       (default is 50%) of physical RAM.  Depending on the amount you
+       use, in most situations this means a process will not be
+       killed while accessing pages but will receive errors on memory
+       allocation as appropriate.
+
+       Useful for applications that want to guarantee their memory
+       allocations will be available in the future without having to
+       initialize every page.
+
+The overcommit policy is set via the sysctl ``vm.overcommit_memory``.
+
+The overcommit amount can be set via ``vm.overcommit_ratio`` (percentage)
+or ``vm.overcommit_kbytes`` (absolute value). These only have an effect
+when ``vm.overcommit_memory`` is set to 2.
+
+The current overcommit limit and amount committed are viewable in
+``/proc/meminfo`` as CommitLimit and Committed_AS respectively.
+
+Gotchas
+=======
+
+The C language stack growth does an implicit mremap. If you want absolute
+guarantees and run close to the edge you MUST mmap your stack for the
+largest size you think you will need. For typical stack usage this does
+not matter much but it's a corner case if you really really care
+
+In mode 2 the MAP_NORESERVE flag is ignored.
+
+
+How It Works
+============
+
+The overcommit is based on the following rules
+
+For a file backed map
+       | SHARED or READ-only   -       0 cost (the file is the map not swap)
+       | PRIVATE WRITABLE      -       size of mapping per instance
+
+For an anonymous or ``/dev/zero`` map
+       | SHARED                        -       size of mapping
+       | PRIVATE READ-only     -       0 cost (but of little use)
+       | PRIVATE WRITABLE      -       size of mapping per instance
+
+Additional accounting
+       | Pages made writable copies by mmap
+       | shmfs memory drawn from the same pool
+
+Status
+======
+
+*      We account mmap memory mappings
+*      We account mprotect changes in commit
+*      We account mremap changes in size
+*      We account brk
+*      We account munmap
+*      We report the commit status in /proc
+*      Account and check on fork
+*      Review stack handling/building on exec
+*      SHMfs accounting
+*      Implement actual limit enforcement
+
+To Do
+=====
+*      Account ptrace pages (this is hard)
diff --git a/Documentation/mm/page_allocation.rst b/Documentation/mm/page_allocation.rst
new file mode 100644 (file)
index 0000000..d9b4495
--- /dev/null
@@ -0,0 +1,5 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============
+Page Allocation
+===============
diff --git a/Documentation/mm/page_cache.rst b/Documentation/mm/page_cache.rst
new file mode 100644 (file)
index 0000000..75eba7c
--- /dev/null
@@ -0,0 +1,5 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==========
+Page Cache
+==========
diff --git a/Documentation/mm/page_frags.rst b/Documentation/mm/page_frags.rst
new file mode 100644 (file)
index 0000000..7d6f938
--- /dev/null
@@ -0,0 +1,45 @@
+.. _page_frags:
+
+==============
+Page fragments
+==============
+
+A page fragment is an arbitrary-length arbitrary-offset area of memory
+which resides within a 0 or higher order compound page.  Multiple
+fragments within that page are individually refcounted, in the page's
+reference counter.
+
+The page_frag functions, page_frag_alloc and page_frag_free, provide a
+simple allocation framework for page fragments.  This is used by the
+network stack and network device drivers to provide a backing region of
+memory for use as either an sk_buff->head, or to be used in the "frags"
+portion of skb_shared_info.
+
+In order to make use of the page fragment APIs a backing page fragment
+cache is needed.  This provides a central point for the fragment allocation
+and tracks allows multiple calls to make use of a cached page.  The
+advantage to doing this is that multiple calls to get_page can be avoided
+which can be expensive at allocation time.  However due to the nature of
+this caching it is required that any calls to the cache be protected by
+either a per-cpu limitation, or a per-cpu limitation and forcing interrupts
+to be disabled when executing the fragment allocation.
+
+The network stack uses two separate caches per CPU to handle fragment
+allocation.  The netdev_alloc_cache is used by callers making use of the
+netdev_alloc_frag and __netdev_alloc_skb calls.  The napi_alloc_cache is
+used by callers of the __napi_alloc_frag and __napi_alloc_skb calls.  The
+main difference between these two calls is the context in which they may be
+called.  The "netdev" prefixed functions are usable in any context as these
+functions will disable interrupts, while the "napi" prefixed functions are
+only usable within the softirq context.
+
+Many network device drivers use a similar methodology for allocating page
+fragments, but the page fragments are cached at the ring or descriptor
+level.  In order to enable these cases it is necessary to provide a generic
+way of tearing down a page cache.  For this reason __page_frag_cache_drain
+was implemented.  It allows for freeing multiple references from a single
+page via a single call.  The advantage to doing this is that it allows for
+cleaning up the multiple references that were added to a page in order to
+avoid calling get_page per allocation.
+
+Alexander Duyck, Nov 29, 2016.
diff --git a/Documentation/mm/page_migration.rst b/Documentation/mm/page_migration.rst
new file mode 100644 (file)
index 0000000..11493ba
--- /dev/null
@@ -0,0 +1,195 @@
+.. _page_migration:
+
+==============
+Page migration
+==============
+
+Page migration allows moving the physical location of pages between
+nodes in a NUMA system while the process is running. This means that the
+virtual addresses that the process sees do not change. However, the
+system rearranges the physical location of those pages.
+
+Also see :ref:`Heterogeneous Memory Management (HMM) <hmm>`
+for migrating pages to or from device private memory.
+
+The main intent of page migration is to reduce the latency of memory accesses
+by moving pages near to the processor where the process accessing that memory
+is running.
+
+Page migration allows a process to manually relocate the node on which its
+pages are located through the MF_MOVE and MF_MOVE_ALL options while setting
+a new memory policy via mbind(). The pages of a process can also be relocated
+from another process using the sys_migrate_pages() function call. The
+migrate_pages() function call takes two sets of nodes and moves pages of a
+process that are located on the from nodes to the destination nodes.
+Page migration functions are provided by the numactl package by Andi Kleen
+(a version later than 0.9.3 is required. Get it from
+https://github.com/numactl/numactl.git). numactl provides libnuma
+which provides an interface similar to other NUMA functionality for page
+migration.  cat ``/proc/<pid>/numa_maps`` allows an easy review of where the
+pages of a process are located. See also the numa_maps documentation in the
+proc(5) man page.
+
+Manual migration is useful if for example the scheduler has relocated
+a process to a processor on a distant node. A batch scheduler or an
+administrator may detect the situation and move the pages of the process
+nearer to the new processor. The kernel itself only provides
+manual page migration support. Automatic page migration may be implemented
+through user space processes that move pages. A special function call
+"move_pages" allows the moving of individual pages within a process.
+For example, A NUMA profiler may obtain a log showing frequent off-node
+accesses and may use the result to move pages to more advantageous
+locations.
+
+Larger installations usually partition the system using cpusets into
+sections of nodes. Paul Jackson has equipped cpusets with the ability to
+move pages when a task is moved to another cpuset (See
+:ref:`CPUSETS <cpusets>`).
+Cpusets allow the automation of process locality. If a task is moved to
+a new cpuset then also all its pages are moved with it so that the
+performance of the process does not sink dramatically. Also the pages
+of processes in a cpuset are moved if the allowed memory nodes of a
+cpuset are changed.
+
+Page migration allows the preservation of the relative location of pages
+within a group of nodes for all migration techniques which will preserve a
+particular memory allocation pattern generated even after migrating a
+process. This is necessary in order to preserve the memory latencies.
+Processes will run with similar performance after migration.
+
+Page migration occurs in several steps. First a high level
+description for those trying to use migrate_pages() from the kernel
+(for userspace usage see the Andi Kleen's numactl package mentioned above)
+and then a low level description of how the low level details work.
+
+In kernel use of migrate_pages()
+================================
+
+1. Remove pages from the LRU.
+
+   Lists of pages to be migrated are generated by scanning over
+   pages and moving them into lists. This is done by
+   calling isolate_lru_page().
+   Calling isolate_lru_page() increases the references to the page
+   so that it cannot vanish while the page migration occurs.
+   It also prevents the swapper or other scans from encountering
+   the page.
+
+2. We need to have a function of type new_page_t that can be
+   passed to migrate_pages(). This function should figure out
+   how to allocate the correct new page given the old page.
+
+3. The migrate_pages() function is called which attempts
+   to do the migration. It will call the function to allocate
+   the new page for each page that is considered for
+   moving.
+
+How migrate_pages() works
+=========================
+
+migrate_pages() does several passes over its list of pages. A page is moved
+if all references to a page are removable at the time. The page has
+already been removed from the LRU via isolate_lru_page() and the refcount
+is increased so that the page cannot be freed while page migration occurs.
+
+Steps:
+
+1. Lock the page to be migrated.
+
+2. Ensure that writeback is complete.
+
+3. Lock the new page that we want to move to. It is locked so that accesses to
+   this (not yet up-to-date) page immediately block while the move is in progress.
+
+4. All the page table references to the page are converted to migration
+   entries. This decreases the mapcount of a page. If the resulting
+   mapcount is not zero then we do not migrate the page. All user space
+   processes that attempt to access the page will now wait on the page lock
+   or wait for the migration page table entry to be removed.
+
+5. The i_pages lock is taken. This will cause all processes trying
+   to access the page via the mapping to block on the spinlock.
+
+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.
+
+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.
+
+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
+    the new page is referenced by the address space.
+
+11. The i_pages lock is dropped. With that lookups in the mapping
+    become possible again. Processes will move from spinning on the lock
+    to sleeping on the locked new page.
+
+12. The page contents are copied to the new page.
+
+13. The remaining page flags are copied to the new page.
+
+14. The old page flags are cleared to indicate that the page does
+    not provide any information anymore.
+
+15. Queued up writeback on the new page is triggered.
+
+16. If migration entries were inserted into the page table, then replace them
+    with real ptes. Doing so will enable access for user space processes not
+    already waiting for the page lock.
+
+17. The page locks are dropped from the old and new page.
+    Processes waiting on the page lock will redo their page faults
+    and will reach the new page.
+
+18. The new page is moved to the LRU and can be scanned by the swapper,
+    etc. again.
+
+Non-LRU page migration
+======================
+
+Although migration originally aimed for reducing the latency of memory
+accesses for NUMA, compaction also uses migration to create high-order
+pages.  For compaction purposes, it is also useful to be able to move
+non-LRU pages, such as zsmalloc and virtio-balloon pages.
+
+If a driver wants to make its pages movable, it should define a struct
+movable_operations.  It then needs to call __SetPageMovable() on each
+page that it may be able to move.  This uses the ``page->mapping`` field,
+so this field is not available for the driver to use for other purposes.
+
+Monitoring Migration
+=====================
+
+The following events (counters) can be used to monitor page migration.
+
+1. PGMIGRATE_SUCCESS: Normal page migration success. Each count means that a
+   page was migrated. If the page was a non-THP and non-hugetlb page, then
+   this counter is increased by one. If the page was a THP or hugetlb, then
+   this counter is increased by the number of THP or hugetlb subpages.
+   For example, migration of a single 2MB THP that has 4KB-size base pages
+   (subpages) will cause this counter to increase by 512.
+
+2. PGMIGRATE_FAIL: Normal page migration failure. Same counting rules as for
+   PGMIGRATE_SUCCESS, above: this will be increased by the number of subpages,
+   if it was a THP or hugetlb.
+
+3. THP_MIGRATION_SUCCESS: A THP was migrated without being split.
+
+4. THP_MIGRATION_FAIL: A THP could not be migrated nor it could be split.
+
+5. THP_MIGRATION_SPLIT: A THP was migrated, but not as such: first, the THP had
+   to be split. After splitting, a migration retry was used for it's sub-pages.
+
+THP_MIGRATION_* events also update the appropriate PGMIGRATE_SUCCESS or
+PGMIGRATE_FAIL events. For example, a THP migration failure will cause both
+THP_MIGRATION_FAIL and PGMIGRATE_FAIL to increase.
+
+Christoph Lameter, May 8, 2006.
+Minchan Kim, Mar 28, 2016.
+
+.. kernel-doc:: include/linux/migrate.h
diff --git a/Documentation/mm/page_owner.rst b/Documentation/mm/page_owner.rst
new file mode 100644 (file)
index 0000000..f5c954a
--- /dev/null
@@ -0,0 +1,196 @@
+.. _page_owner:
+
+==================================================
+page owner: Tracking about who allocated each page
+==================================================
+
+Introduction
+============
+
+page owner is for the tracking about who allocated each page.
+It can be used to debug memory leak or to find a memory hogger.
+When allocation happens, information about allocation such as call stack
+and order of pages is stored into certain storage for each page.
+When we need to know about status of all pages, we can get and analyze
+this information.
+
+Although we already have tracepoint for tracing page allocation/free,
+using it for analyzing who allocate each page is rather complex. We need
+to enlarge the trace buffer for preventing overlapping until userspace
+program launched. And, launched program continually dump out the trace
+buffer for later analysis and it would change system behaviour with more
+possibility rather than just keeping it in memory, so bad for debugging.
+
+page owner can also be used for various purposes. For example, accurate
+fragmentation statistics can be obtained through gfp flag information of
+each page. It is already implemented and activated if page owner is
+enabled. Other usages are more than welcome.
+
+page owner is disabled by default. So, if you'd like to use it, you need
+to add "page_owner=on" to your boot cmdline. If the kernel is built
+with page owner and page owner is disabled in runtime due to not enabling
+boot option, runtime overhead is marginal. If disabled in runtime, it
+doesn't require memory to store owner information, so there is no runtime
+memory overhead. And, page owner inserts just two unlikely branches into
+the page allocator hotpath and if not enabled, then allocation is done
+like as the kernel without page owner. These two unlikely branches should
+not affect to allocation performance, especially if the static keys jump
+label patching functionality is available. Following is the kernel's code
+size change due to this facility.
+
+- Without page owner::
+
+   text    data     bss     dec     hex filename
+   48392   2333     644   51369    c8a9 mm/page_alloc.o
+
+- With page owner::
+
+   text    data     bss     dec     hex filename
+   48800   2445     644   51889    cab1 mm/page_alloc.o
+   6662     108      29    6799    1a8f mm/page_owner.o
+   1025       8       8    1041     411 mm/page_ext.o
+
+Although, roughly, 8 KB code is added in total, page_alloc.o increase by
+520 bytes and less than half of it is in hotpath. Building the kernel with
+page owner and turning it on if needed would be great option to debug
+kernel memory problem.
+
+There is one notice that is caused by implementation detail. page owner
+stores information into the memory from struct page extension. This memory
+is initialized some time later than that page allocator starts in sparse
+memory system, so, until initialization, many pages can be allocated and
+they would have no owner information. To fix it up, these early allocated
+pages are investigated and marked as allocated in initialization phase.
+Although it doesn't mean that they have the right owner information,
+at least, we can tell whether the page is allocated or not,
+more accurately. On 2GB memory x86-64 VM box, 13343 early allocated pages
+are catched and marked, although they are mostly allocated from struct
+page extension feature. Anyway, after that, no page is left in
+un-tracking state.
+
+Usage
+=====
+
+1) Build user-space helper::
+
+       cd tools/vm
+       make page_owner_sort
+
+2) Enable page owner: add "page_owner=on" to boot cmdline.
+
+3) Do the job that you want to debug.
+
+4) Analyze information from page owner::
+
+       cat /sys/kernel/debug/page_owner > page_owner_full.txt
+       ./page_owner_sort page_owner_full.txt sorted_page_owner.txt
+
+   The general output of ``page_owner_full.txt`` is as follows::
+
+       Page allocated via order XXX, ...
+       PFN XXX ...
+       // Detailed stack
+
+       Page allocated via order XXX, ...
+       PFN XXX ...
+       // Detailed stack
+
+   The ``page_owner_sort`` tool ignores ``PFN`` rows, puts the remaining rows
+   in buf, uses regexp to extract the page order value, counts the times
+   and pages of buf, and finally sorts them according to the parameter(s).
+
+   See the result about who allocated each page
+   in the ``sorted_page_owner.txt``. General output::
+
+       XXX times, XXX pages:
+       Page allocated via order XXX, ...
+       // Detailed stack
+
+   By default, ``page_owner_sort`` is sorted according to the times of buf.
+   If you want to sort by the page nums of buf, use the ``-m`` parameter.
+   The detailed parameters are:
+
+   fundamental function::
+
+       Sort:
+               -a              Sort by memory allocation time.
+               -m              Sort by total memory.
+               -p              Sort by pid.
+               -P              Sort by tgid.
+               -n              Sort by task command name.
+               -r              Sort by memory release time.
+               -s              Sort by stack trace.
+               -t              Sort by times (default).
+               --sort <order>  Specify sorting order.  Sorting syntax is [+|-]key[,[+|-]key[,...]].
+                               Choose a key from the **STANDARD FORMAT SPECIFIERS** section. The "+" is
+                               optional since default direction is increasing numerical or lexicographic
+                               order. Mixed use of abbreviated and complete-form of keys is allowed.
+
+               Examples:
+                               ./page_owner_sort <input> <output> --sort=n,+pid,-tgid
+                               ./page_owner_sort <input> <output> --sort=at
+
+   additional function::
+
+       Cull:
+               --cull <rules>
+                               Specify culling rules.Culling syntax is key[,key[,...]].Choose a
+                               multi-letter key from the **STANDARD FORMAT SPECIFIERS** section.
+
+               <rules> is a single argument in the form of a comma-separated list,
+               which offers a way to specify individual culling rules.  The recognized
+               keywords are described in the **STANDARD FORMAT SPECIFIERS** section below.
+               <rules> can be specified by the sequence of keys k1,k2, ..., as described in
+               the STANDARD SORT KEYS section below. Mixed use of abbreviated and
+               complete-form of keys is allowed.
+
+               Examples:
+                               ./page_owner_sort <input> <output> --cull=stacktrace
+                               ./page_owner_sort <input> <output> --cull=st,pid,name
+                               ./page_owner_sort <input> <output> --cull=n,f
+
+       Filter:
+               -f              Filter out the information of blocks whose memory has been released.
+
+       Select:
+               --pid <pidlist>         Select by pid. This selects the blocks whose process ID
+                                       numbers appear in <pidlist>.
+               --tgid <tgidlist>       Select by tgid. This selects the blocks whose thread
+                                       group ID numbers appear in <tgidlist>.
+               --name <cmdlist>        Select by task command name. This selects the blocks whose
+                                       task command name appear in <cmdlist>.
+
+               <pidlist>, <tgidlist>, <cmdlist> are single arguments in the form of a comma-separated list,
+               which offers a way to specify individual selecting rules.
+
+
+               Examples:
+                               ./page_owner_sort <input> <output> --pid=1
+                               ./page_owner_sort <input> <output> --tgid=1,2,3
+                               ./page_owner_sort <input> <output> --name name1,name2
+
+STANDARD FORMAT SPECIFIERS
+==========================
+::
+
+  For --sort option:
+
+       KEY             LONG            DESCRIPTION
+       p               pid             process ID
+       tg              tgid            thread group ID
+       n               name            task command name
+       st              stacktrace      stack trace of the page allocation
+       T               txt             full text of block
+       ft              free_ts         timestamp of the page when it was released
+       at              alloc_ts        timestamp of the page when it was allocated
+       ator            allocator       memory allocator for pages
+
+  For --curl option:
+
+       KEY             LONG            DESCRIPTION
+       p               pid             process ID
+       tg              tgid            thread group ID
+       n               name            task command name
+       f               free            whether the page has been released or not
+       st              stacktrace      stack trace of the page allocation
+       ator            allocator       memory allocator for pages
diff --git a/Documentation/mm/page_reclaim.rst b/Documentation/mm/page_reclaim.rst
new file mode 100644 (file)
index 0000000..50a30b7
--- /dev/null
@@ -0,0 +1,5 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============
+Page Reclaim
+============
diff --git a/Documentation/mm/page_table_check.rst b/Documentation/mm/page_table_check.rst
new file mode 100644 (file)
index 0000000..1a09472
--- /dev/null
@@ -0,0 +1,56 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. _page_table_check:
+
+================
+Page Table Check
+================
+
+Introduction
+============
+
+Page table check allows to harden the kernel by ensuring that some types of
+the memory corruptions are prevented.
+
+Page table check performs extra verifications at the time when new pages become
+accessible from the userspace by getting their page table entries (PTEs PMDs
+etc.) added into the table.
+
+In case of detected corruption, the kernel is crashed. There is a small
+performance and memory overhead associated with the page table check. Therefore,
+it is disabled by default, but can be optionally enabled on systems where the
+extra hardening outweighs the performance costs. Also, because page table check
+is synchronous, it can help with debugging double map memory corruption issues,
+by crashing kernel at the time wrong mapping occurs instead of later which is
+often the case with memory corruptions bugs.
+
+Double mapping detection logic
+==============================
+
++-------------------+-------------------+-------------------+------------------+
+| Current Mapping   | New mapping       | Permissions       | Rule             |
++===================+===================+===================+==================+
+| Anonymous         | Anonymous         | Read              | Allow            |
++-------------------+-------------------+-------------------+------------------+
+| Anonymous         | Anonymous         | Read / Write      | Prohibit         |
++-------------------+-------------------+-------------------+------------------+
+| Anonymous         | Named             | Any               | Prohibit         |
++-------------------+-------------------+-------------------+------------------+
+| Named             | Anonymous         | Any               | Prohibit         |
++-------------------+-------------------+-------------------+------------------+
+| Named             | Named             | Any               | Allow            |
++-------------------+-------------------+-------------------+------------------+
+
+Enabling Page Table Check
+=========================
+
+Build kernel with:
+
+- PAGE_TABLE_CHECK=y
+  Note, it can only be enabled on platforms where ARCH_SUPPORTS_PAGE_TABLE_CHECK
+  is available.
+
+- Boot with 'page_table_check=on' kernel parameter.
+
+Optionally, build kernel with PAGE_TABLE_CHECK_ENFORCED in order to have page
+table support without extra kernel parameter.
diff --git a/Documentation/mm/page_tables.rst b/Documentation/mm/page_tables.rst
new file mode 100644 (file)
index 0000000..9693957
--- /dev/null
@@ -0,0 +1,5 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========
+Page Tables
+===========
diff --git a/Documentation/mm/physical_memory.rst b/Documentation/mm/physical_memory.rst
new file mode 100644 (file)
index 0000000..2ab7b8c
--- /dev/null
@@ -0,0 +1,5 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============
+Physical Memory
+===============
diff --git a/Documentation/mm/process_addrs.rst b/Documentation/mm/process_addrs.rst
new file mode 100644 (file)
index 0000000..e8618fb
--- /dev/null
@@ -0,0 +1,5 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=================
+Process Addresses
+=================
diff --git a/Documentation/mm/remap_file_pages.rst b/Documentation/mm/remap_file_pages.rst
new file mode 100644 (file)
index 0000000..7bef671
--- /dev/null
@@ -0,0 +1,33 @@
+.. _remap_file_pages:
+
+==============================
+remap_file_pages() system call
+==============================
+
+The remap_file_pages() system call is used to create a nonlinear mapping,
+that is, a mapping in which the pages of the file are mapped into a
+nonsequential order in memory. The advantage of using remap_file_pages()
+over using repeated calls to mmap(2) is that the former approach does not
+require the kernel to create additional VMA (Virtual Memory Area) data
+structures.
+
+Supporting of nonlinear mapping requires significant amount of non-trivial
+code in kernel virtual memory subsystem including hot paths. Also to get
+nonlinear mapping work kernel need a way to distinguish normal page table
+entries from entries with file offset (pte_file). Kernel reserves flag in
+PTE for this purpose. PTE flags are scarce resource especially on some CPU
+architectures. It would be nice to free up the flag for other usage.
+
+Fortunately, there are not many users of remap_file_pages() in the wild.
+It's only known that one enterprise RDBMS implementation uses the syscall
+on 32-bit systems to map files bigger than can linearly fit into 32-bit
+virtual address space. This use-case is not critical anymore since 64-bit
+systems are widely available.
+
+The syscall is deprecated and replaced it with an emulation now. The
+emulation creates new VMAs instead of nonlinear mappings. It's going to
+work slower for rare users of remap_file_pages() but ABI is preserved.
+
+One side effect of emulation (apart from performance) is that user can hit
+vm.max_map_count limit more easily due to additional VMAs. See comment for
+DEFAULT_MAX_MAP_COUNT for more details on the limit.
diff --git a/Documentation/mm/shmfs.rst b/Documentation/mm/shmfs.rst
new file mode 100644 (file)
index 0000000..8b01ebb
--- /dev/null
@@ -0,0 +1,5 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+========================
+Shared Memory Filesystem
+========================
diff --git a/Documentation/mm/slab.rst b/Documentation/mm/slab.rst
new file mode 100644 (file)
index 0000000..87d5a5b
--- /dev/null
@@ -0,0 +1,5 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============
+Slab Allocation
+===============
diff --git a/Documentation/mm/slub.rst b/Documentation/mm/slub.rst
new file mode 100644 (file)
index 0000000..43063ad
--- /dev/null
@@ -0,0 +1,452 @@
+.. _slub:
+
+==========================
+Short users guide for SLUB
+==========================
+
+The basic philosophy of SLUB is very different from SLAB. SLAB
+requires rebuilding the kernel to activate debug options for all
+slab caches. SLUB always includes full debugging but it is off by default.
+SLUB can enable debugging only for selected slabs in order to avoid
+an impact on overall system performance which may make a bug more
+difficult to find.
+
+In order to switch debugging on one can add an option ``slub_debug``
+to the kernel command line. That will enable full debugging for
+all slabs.
+
+Typically one would then use the ``slabinfo`` command to get statistical
+data and perform operation on the slabs. By default ``slabinfo`` only lists
+slabs that have data in them. See "slabinfo -h" for more options when
+running the command. ``slabinfo`` can be compiled with
+::
+
+       gcc -o slabinfo tools/vm/slabinfo.c
+
+Some of the modes of operation of ``slabinfo`` require that slub debugging
+be enabled on the command line. F.e. no tracking information will be
+available without debugging on and validation can only partially
+be performed if debugging was not switched on.
+
+Some more sophisticated uses of slub_debug:
+-------------------------------------------
+
+Parameters may be given to ``slub_debug``. If none is specified then full
+debugging is enabled. Format:
+
+slub_debug=<Debug-Options>
+       Enable options for all slabs
+
+slub_debug=<Debug-Options>,<slab name1>,<slab name2>,...
+       Enable options only for select slabs (no spaces
+       after a comma)
+
+Multiple blocks of options for all slabs or selected slabs can be given, with
+blocks of options delimited by ';'. The last of "all slabs" blocks is applied
+to all slabs except those that match one of the "select slabs" block. Options
+of the first "select slabs" blocks that matches the slab's name are applied.
+
+Possible debug options are::
+
+       F               Sanity checks on (enables SLAB_DEBUG_CONSISTENCY_CHECKS
+                       Sorry SLAB legacy issues)
+       Z               Red zoning
+       P               Poisoning (object and padding)
+       U               User tracking (free and alloc)
+       T               Trace (please only use on single slabs)
+       A               Enable failslab filter mark for the cache
+       O               Switch debugging off for caches that would have
+                       caused higher minimum slab orders
+       -               Switch all debugging off (useful if the kernel is
+                       configured with CONFIG_SLUB_DEBUG_ON)
+
+F.e. in order to boot just with sanity checks and red zoning one would specify::
+
+       slub_debug=FZ
+
+Trying to find an issue in the dentry cache? Try::
+
+       slub_debug=,dentry
+
+to only enable debugging on the dentry cache.  You may use an asterisk at the
+end of the slab name, in order to cover all slabs with the same prefix.  For
+example, here's how you can poison the dentry cache as well as all kmalloc
+slabs::
+
+       slub_debug=P,kmalloc-*,dentry
+
+Red zoning and tracking may realign the slab.  We can just apply sanity checks
+to the dentry cache with::
+
+       slub_debug=F,dentry
+
+Debugging options may require the minimum possible slab order to increase as
+a result of storing the metadata (for example, caches with PAGE_SIZE object
+sizes).  This has a higher liklihood of resulting in slab allocation errors
+in low memory situations or if there's high fragmentation of memory.  To
+switch off debugging for such caches by default, use::
+
+       slub_debug=O
+
+You can apply different options to different list of slab names, using blocks
+of options. This will enable red zoning for dentry and user tracking for
+kmalloc. All other slabs will not get any debugging enabled::
+
+       slub_debug=Z,dentry;U,kmalloc-*
+
+You can also enable options (e.g. sanity checks and poisoning) for all caches
+except some that are deemed too performance critical and don't need to be
+debugged by specifying global debug options followed by a list of slab names
+with "-" as options::
+
+       slub_debug=FZ;-,zs_handle,zspage
+
+The state of each debug option for a slab can be found in the respective files
+under::
+
+       /sys/kernel/slab/<slab name>/
+
+If the file contains 1, the option is enabled, 0 means disabled. The debug
+options from the ``slub_debug`` parameter translate to the following files::
+
+       F       sanity_checks
+       Z       red_zone
+       P       poison
+       U       store_user
+       T       trace
+       A       failslab
+
+Careful with tracing: It may spew out lots of information and never stop if
+used on the wrong slab.
+
+Slab merging
+============
+
+If no debug options are specified then SLUB may merge similar slabs together
+in order to reduce overhead and increase cache hotness of objects.
+``slabinfo -a`` displays which slabs were merged together.
+
+Slab validation
+===============
+
+SLUB can validate all object if the kernel was booted with slub_debug. In
+order to do so you must have the ``slabinfo`` tool. Then you can do
+::
+
+       slabinfo -v
+
+which will test all objects. Output will be generated to the syslog.
+
+This also works in a more limited way if boot was without slab debug.
+In that case ``slabinfo -v`` simply tests all reachable objects. Usually
+these are in the cpu slabs and the partial slabs. Full slabs are not
+tracked by SLUB in a non debug situation.
+
+Getting more performance
+========================
+
+To some degree SLUB's performance is limited by the need to take the
+list_lock once in a while to deal with partial slabs. That overhead is
+governed by the order of the allocation for each slab. The allocations
+can be influenced by kernel parameters:
+
+.. slub_min_objects=x          (default 4)
+.. slub_min_order=x            (default 0)
+.. slub_max_order=x            (default 3 (PAGE_ALLOC_COSTLY_ORDER))
+
+``slub_min_objects``
+       allows to specify how many objects must at least fit into one
+       slab in order for the allocation order to be acceptable.  In
+       general slub will be able to perform this number of
+       allocations on a slab without consulting centralized resources
+       (list_lock) where contention may occur.
+
+``slub_min_order``
+       specifies a minimum order of slabs. A similar effect like
+       ``slub_min_objects``.
+
+``slub_max_order``
+       specified the order at which ``slub_min_objects`` should no
+       longer be checked. This is useful to avoid SLUB trying to
+       generate super large order pages to fit ``slub_min_objects``
+       of a slab cache with large object sizes into one high order
+       page. Setting command line parameter
+       ``debug_guardpage_minorder=N`` (N > 0), forces setting
+       ``slub_max_order`` to 0, what cause minimum possible order of
+       slabs allocation.
+
+SLUB Debug output
+=================
+
+Here is a sample of slub debug output::
+
+ ====================================================================
+ BUG kmalloc-8: Right Redzone overwritten
+ --------------------------------------------------------------------
+
+ INFO: 0xc90f6d28-0xc90f6d2b. First byte 0x00 instead of 0xcc
+ INFO: Slab 0xc528c530 flags=0x400000c3 inuse=61 fp=0xc90f6d58
+ INFO: Object 0xc90f6d20 @offset=3360 fp=0xc90f6d58
+ INFO: Allocated in get_modalias+0x61/0xf5 age=53 cpu=1 pid=554
+
+ Bytes b4 (0xc90f6d10): 00 00 00 00 00 00 00 00 5a 5a 5a 5a 5a 5a 5a 5a ........ZZZZZZZZ
+ Object   (0xc90f6d20): 31 30 31 39 2e 30 30 35                         1019.005
+ Redzone  (0xc90f6d28): 00 cc cc cc                                     .
+ Padding  (0xc90f6d50): 5a 5a 5a 5a 5a 5a 5a 5a                         ZZZZZZZZ
+
+   [<c010523d>] dump_trace+0x63/0x1eb
+   [<c01053df>] show_trace_log_lvl+0x1a/0x2f
+   [<c010601d>] show_trace+0x12/0x14
+   [<c0106035>] dump_stack+0x16/0x18
+   [<c017e0fa>] object_err+0x143/0x14b
+   [<c017e2cc>] check_object+0x66/0x234
+   [<c017eb43>] __slab_free+0x239/0x384
+   [<c017f446>] kfree+0xa6/0xc6
+   [<c02e2335>] get_modalias+0xb9/0xf5
+   [<c02e23b7>] dmi_dev_uevent+0x27/0x3c
+   [<c027866a>] dev_uevent+0x1ad/0x1da
+   [<c0205024>] kobject_uevent_env+0x20a/0x45b
+   [<c020527f>] kobject_uevent+0xa/0xf
+   [<c02779f1>] store_uevent+0x4f/0x58
+   [<c027758e>] dev_attr_store+0x29/0x2f
+   [<c01bec4f>] sysfs_write_file+0x16e/0x19c
+   [<c0183ba7>] vfs_write+0xd1/0x15a
+   [<c01841d7>] sys_write+0x3d/0x72
+   [<c0104112>] sysenter_past_esp+0x5f/0x99
+   [<b7f7b410>] 0xb7f7b410
+   =======================
+
+ FIX kmalloc-8: Restoring Redzone 0xc90f6d28-0xc90f6d2b=0xcc
+
+If SLUB encounters a corrupted object (full detection requires the kernel
+to be booted with slub_debug) then the following output will be dumped
+into the syslog:
+
+1. Description of the problem encountered
+
+   This will be a message in the system log starting with::
+
+     ===============================================
+     BUG <slab cache affected>: <What went wrong>
+     -----------------------------------------------
+
+     INFO: <corruption start>-<corruption_end> <more info>
+     INFO: Slab <address> <slab information>
+     INFO: Object <address> <object information>
+     INFO: Allocated in <kernel function> age=<jiffies since alloc> cpu=<allocated by
+       cpu> pid=<pid of the process>
+     INFO: Freed in <kernel function> age=<jiffies since free> cpu=<freed by cpu>
+       pid=<pid of the process>
+
+   (Object allocation / free information is only available if SLAB_STORE_USER is
+   set for the slab. slub_debug sets that option)
+
+2. The object contents if an object was involved.
+
+   Various types of lines can follow the BUG SLUB line:
+
+   Bytes b4 <address> : <bytes>
+       Shows a few bytes before the object where the problem was detected.
+       Can be useful if the corruption does not stop with the start of the
+       object.
+
+   Object <address> : <bytes>
+       The bytes of the object. If the object is inactive then the bytes
+       typically contain poison values. Any non-poison value shows a
+       corruption by a write after free.
+
+   Redzone <address> : <bytes>
+       The Redzone following the object. The Redzone is used to detect
+       writes after the object. All bytes should always have the same
+       value. If there is any deviation then it is due to a write after
+       the object boundary.
+
+       (Redzone information is only available if SLAB_RED_ZONE is set.
+       slub_debug sets that option)
+
+   Padding <address> : <bytes>
+       Unused data to fill up the space in order to get the next object
+       properly aligned. In the debug case we make sure that there are
+       at least 4 bytes of padding. This allows the detection of writes
+       before the object.
+
+3. A stackdump
+
+   The stackdump describes the location where the error was detected. The cause
+   of the corruption is may be more likely found by looking at the function that
+   allocated or freed the object.
+
+4. Report on how the problem was dealt with in order to ensure the continued
+   operation of the system.
+
+   These are messages in the system log beginning with::
+
+       FIX <slab cache affected>: <corrective action taken>
+
+   In the above sample SLUB found that the Redzone of an active object has
+   been overwritten. Here a string of 8 characters was written into a slab that
+   has the length of 8 characters. However, a 8 character string needs a
+   terminating 0. That zero has overwritten the first byte of the Redzone field.
+   After reporting the details of the issue encountered the FIX SLUB message
+   tells us that SLUB has restored the Redzone to its proper value and then
+   system operations continue.
+
+Emergency operations
+====================
+
+Minimal debugging (sanity checks alone) can be enabled by booting with::
+
+       slub_debug=F
+
+This will be generally be enough to enable the resiliency features of slub
+which will keep the system running even if a bad kernel component will
+keep corrupting objects. This may be important for production systems.
+Performance will be impacted by the sanity checks and there will be a
+continual stream of error messages to the syslog but no additional memory
+will be used (unlike full debugging).
+
+No guarantees. The kernel component still needs to be fixed. Performance
+may be optimized further by locating the slab that experiences corruption
+and enabling debugging only for that cache
+
+I.e.::
+
+       slub_debug=F,dentry
+
+If the corruption occurs by writing after the end of the object then it
+may be advisable to enable a Redzone to avoid corrupting the beginning
+of other objects::
+
+       slub_debug=FZ,dentry
+
+Extended slabinfo mode and plotting
+===================================
+
+The ``slabinfo`` tool has a special 'extended' ('-X') mode that includes:
+ - Slabcache Totals
+ - Slabs sorted by size (up to -N <num> slabs, default 1)
+ - Slabs sorted by loss (up to -N <num> slabs, default 1)
+
+Additionally, in this mode ``slabinfo`` does not dynamically scale
+sizes (G/M/K) and reports everything in bytes (this functionality is
+also available to other slabinfo modes via '-B' option) which makes
+reporting more precise and accurate. Moreover, in some sense the `-X'
+mode also simplifies the analysis of slabs' behaviour, because its
+output can be plotted using the ``slabinfo-gnuplot.sh`` script. So it
+pushes the analysis from looking through the numbers (tons of numbers)
+to something easier -- visual analysis.
+
+To generate plots:
+
+a) collect slabinfo extended records, for example::
+
+       while [ 1 ]; do slabinfo -X >> FOO_STATS; sleep 1; done
+
+b) pass stats file(-s) to ``slabinfo-gnuplot.sh`` script::
+
+       slabinfo-gnuplot.sh FOO_STATS [FOO_STATS2 .. FOO_STATSN]
+
+   The ``slabinfo-gnuplot.sh`` script will pre-processes the collected records
+   and generates 3 png files (and 3 pre-processing cache files) per STATS
+   file:
+   - Slabcache Totals: FOO_STATS-totals.png
+   - Slabs sorted by size: FOO_STATS-slabs-by-size.png
+   - Slabs sorted by loss: FOO_STATS-slabs-by-loss.png
+
+Another use case, when ``slabinfo-gnuplot.sh`` can be useful, is when you
+need to compare slabs' behaviour "prior to" and "after" some code
+modification.  To help you out there, ``slabinfo-gnuplot.sh`` script
+can 'merge' the `Slabcache Totals` sections from different
+measurements. To visually compare N plots:
+
+a) Collect as many STATS1, STATS2, .. STATSN files as you need::
+
+       while [ 1 ]; do slabinfo -X >> STATS<X>; sleep 1; done
+
+b) Pre-process those STATS files::
+
+       slabinfo-gnuplot.sh STATS1 STATS2 .. STATSN
+
+c) Execute ``slabinfo-gnuplot.sh`` in '-t' mode, passing all of the
+   generated pre-processed \*-totals::
+
+       slabinfo-gnuplot.sh -t STATS1-totals STATS2-totals .. STATSN-totals
+
+   This will produce a single plot (png file).
+
+   Plots, expectedly, can be large so some fluctuations or small spikes
+   can go unnoticed. To deal with that, ``slabinfo-gnuplot.sh`` has two
+   options to 'zoom-in'/'zoom-out':
+
+   a) ``-s %d,%d`` -- overwrites the default image width and height
+   b) ``-r %d,%d`` -- specifies a range of samples to use (for example,
+      in ``slabinfo -X >> FOO_STATS; sleep 1;`` case, using a ``-r
+      40,60`` range will plot only samples collected between 40th and
+      60th seconds).
+
+
+DebugFS files for SLUB
+======================
+
+For more information about current state of SLUB caches with the user tracking
+debug option enabled, debugfs files are available, typically under
+/sys/kernel/debug/slab/<cache>/ (created only for caches with enabled user
+tracking). There are 2 types of these files with the following debug
+information:
+
+1. alloc_traces::
+
+    Prints information about unique allocation traces of the currently
+    allocated objects. The output is sorted by frequency of each trace.
+
+    Information in the output:
+    Number of objects, allocating function, minimal/average/maximal jiffies since alloc,
+    pid range of the allocating processes, cpu mask of allocating cpus, and stack trace.
+
+    Example:::
+
+    1085 populate_error_injection_list+0x97/0x110 age=166678/166680/166682 pid=1 cpus=1::
+       __slab_alloc+0x6d/0x90
+       kmem_cache_alloc_trace+0x2eb/0x300
+       populate_error_injection_list+0x97/0x110
+       init_error_injection+0x1b/0x71
+       do_one_initcall+0x5f/0x2d0
+       kernel_init_freeable+0x26f/0x2d7
+       kernel_init+0xe/0x118
+       ret_from_fork+0x22/0x30
+
+
+2. free_traces::
+
+    Prints information about unique freeing traces of the currently allocated
+    objects. The freeing traces thus come from the previous life-cycle of the
+    objects and are reported as not available for objects allocated for the first
+    time. The output is sorted by frequency of each trace.
+
+    Information in the output:
+    Number of objects, freeing function, minimal/average/maximal jiffies since free,
+    pid range of the freeing processes, cpu mask of freeing cpus, and stack trace.
+
+    Example:::
+
+    1980 <not-available> age=4294912290 pid=0 cpus=0
+    51 acpi_ut_update_ref_count+0x6a6/0x782 age=236886/237027/237772 pid=1 cpus=1
+       kfree+0x2db/0x420
+       acpi_ut_update_ref_count+0x6a6/0x782
+       acpi_ut_update_object_reference+0x1ad/0x234
+       acpi_ut_remove_reference+0x7d/0x84
+       acpi_rs_get_prt_method_data+0x97/0xd6
+       acpi_get_irq_routing_table+0x82/0xc4
+       acpi_pci_irq_find_prt_entry+0x8e/0x2e0
+       acpi_pci_irq_lookup+0x3a/0x1e0
+       acpi_pci_irq_enable+0x77/0x240
+       pcibios_enable_device+0x39/0x40
+       do_pci_enable_device.part.0+0x5d/0xe0
+       pci_enable_device_flags+0xfc/0x120
+       pci_enable_device+0x13/0x20
+       virtio_pci_probe+0x9e/0x170
+       local_pci_probe+0x48/0x80
+       pci_device_probe+0x105/0x1c0
+
+Christoph Lameter, May 30, 2007
+Sergey Senozhatsky, October 23, 2015
diff --git a/Documentation/mm/split_page_table_lock.rst b/Documentation/mm/split_page_table_lock.rst
new file mode 100644 (file)
index 0000000..c089196
--- /dev/null
@@ -0,0 +1,100 @@
+.. _split_page_table_lock:
+
+=====================
+Split page table lock
+=====================
+
+Originally, mm->page_table_lock spinlock protected all page tables of the
+mm_struct. But this approach leads to poor page fault scalability of
+multi-threaded applications due high contention on the lock. To improve
+scalability, split page table lock was introduced.
+
+With split page table lock we have separate per-table lock to serialize
+access to the table. At the moment we use split lock for PTE and PMD
+tables. Access to higher level tables protected by mm->page_table_lock.
+
+There are helpers to lock/unlock a table and other accessor functions:
+
+ - pte_offset_map_lock()
+       maps pte and takes PTE table lock, returns pointer to the taken
+       lock;
+ - pte_unmap_unlock()
+       unlocks and unmaps PTE table;
+ - pte_alloc_map_lock()
+       allocates PTE table if needed and take the lock, returns pointer
+       to taken lock or NULL if allocation failed;
+ - pte_lockptr()
+       returns pointer to PTE table lock;
+ - pmd_lock()
+       takes PMD table lock, returns pointer to taken lock;
+ - pmd_lockptr()
+       returns pointer to PMD table lock;
+
+Split page table lock for PTE tables is enabled compile-time if
+CONFIG_SPLIT_PTLOCK_CPUS (usually 4) is less or equal to NR_CPUS.
+If split lock is disabled, all tables are guarded by mm->page_table_lock.
+
+Split page table lock for PMD tables is enabled, if it's enabled for PTE
+tables and the architecture supports it (see below).
+
+Hugetlb and split page table lock
+=================================
+
+Hugetlb can support several page sizes. We use split lock only for PMD
+level, but not for PUD.
+
+Hugetlb-specific helpers:
+
+ - huge_pte_lock()
+       takes pmd split lock for PMD_SIZE page, mm->page_table_lock
+       otherwise;
+ - huge_pte_lockptr()
+       returns pointer to table lock;
+
+Support of split page table lock by an architecture
+===================================================
+
+There's no need in special enabling of PTE split page table lock: everything
+required is done by pgtable_pte_page_ctor() and pgtable_pte_page_dtor(), which
+must be called on PTE table allocation / freeing.
+
+Make sure the architecture doesn't use slab allocator for page table
+allocation: slab uses page->slab_cache for its pages.
+This field shares storage with page->ptl.
+
+PMD split lock only makes sense if you have more than two page table
+levels.
+
+PMD split lock enabling requires pgtable_pmd_page_ctor() call on PMD table
+allocation and pgtable_pmd_page_dtor() on freeing.
+
+Allocation usually happens in pmd_alloc_one(), freeing in pmd_free() and
+pmd_free_tlb(), but make sure you cover all PMD table allocation / freeing
+paths: i.e X86_PAE preallocate few PMDs on pgd_alloc().
+
+With everything in place you can set CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK.
+
+NOTE: pgtable_pte_page_ctor() and pgtable_pmd_page_ctor() can fail -- it must
+be handled properly.
+
+page->ptl
+=========
+
+page->ptl is used to access split page table lock, where 'page' is struct
+page of page containing the table. It shares storage with page->private
+(and few other fields in union).
+
+To avoid increasing size of struct page and have best performance, we use a
+trick:
+
+ - if spinlock_t fits into long, we use page->ptr as spinlock, so we
+   can avoid indirect access and save a cache line.
+ - if size of spinlock_t is bigger then size of long, we use page->ptl as
+   pointer to spinlock_t and allocate it dynamically. This allows to use
+   split lock with enabled DEBUG_SPINLOCK or DEBUG_LOCK_ALLOC, but costs
+   one more cache line for indirect access;
+
+The spinlock_t allocated in pgtable_pte_page_ctor() for PTE table and in
+pgtable_pmd_page_ctor() for PMD table.
+
+Please, never access page->ptl directly -- use appropriate helper.
diff --git a/Documentation/mm/swap.rst b/Documentation/mm/swap.rst
new file mode 100644 (file)
index 0000000..78819bd
--- /dev/null
@@ -0,0 +1,5 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+====
+Swap
+====
diff --git a/Documentation/mm/transhuge.rst b/Documentation/mm/transhuge.rst
new file mode 100644 (file)
index 0000000..216db1d
--- /dev/null
@@ -0,0 +1,187 @@
+.. _transhuge:
+
+============================
+Transparent Hugepage Support
+============================
+
+This document describes design principles for Transparent Hugepage (THP)
+support and its interaction with other parts of the memory management
+system.
+
+Design principles
+=================
+
+- "graceful fallback": mm components which don't have transparent hugepage
+  knowledge fall back to breaking huge pmd mapping into table of ptes and,
+  if necessary, split a transparent hugepage. Therefore these components
+  can continue working on the regular pages or regular pte mappings.
+
+- if a hugepage allocation fails because of memory fragmentation,
+  regular pages should be gracefully allocated instead and mixed in
+  the same vma without any failure or significant delay and without
+  userland noticing
+
+- if some task quits and more hugepages become available (either
+  immediately in the buddy or through the VM), guest physical memory
+  backed by regular pages should be relocated on hugepages
+  automatically (with khugepaged)
+
+- it doesn't require memory reservation and in turn it uses hugepages
+  whenever possible (the only possible reservation here is kernelcore=
+  to avoid unmovable pages to fragment all the memory but such a tweak
+  is not specific to transparent hugepage support and it's a generic
+  feature that applies to all dynamic high order allocations in the
+  kernel)
+
+get_user_pages and follow_page
+==============================
+
+get_user_pages and follow_page if run on a hugepage, will return the
+head or tail pages as usual (exactly as they would do on
+hugetlbfs). Most GUP users will only care about the actual physical
+address of the page and its temporary pinning to release after the I/O
+is complete, so they won't ever notice the fact the page is huge. But
+if any driver is going to mangle over the page structure of the tail
+page (like for checking page->mapping or other bits that are relevant
+for the head page and not the tail page), it should be updated to jump
+to check head page instead. Taking a reference on any head/tail page would
+prevent the page from being split by anyone.
+
+.. note::
+   these aren't new constraints to the GUP API, and they match the
+   same constraints that apply to hugetlbfs too, so any driver capable
+   of handling GUP on hugetlbfs will also work fine on transparent
+   hugepage backed mappings.
+
+Graceful fallback
+=================
+
+Code walking pagetables but unaware about huge pmds can simply call
+split_huge_pmd(vma, pmd, addr) where the pmd is the one returned by
+pmd_offset. It's trivial to make the code transparent hugepage aware
+by just grepping for "pmd_offset" and adding split_huge_pmd where
+missing after pmd_offset returns the pmd. Thanks to the graceful
+fallback design, with a one liner change, you can avoid to write
+hundreds if not thousands of lines of complex code to make your code
+hugepage aware.
+
+If you're not walking pagetables but you run into a physical hugepage
+that you can't handle natively in your code, you can split it by
+calling split_huge_page(page). This is what the Linux VM does before
+it tries to swapout the hugepage for example. split_huge_page() can fail
+if the page is pinned and you must handle this correctly.
+
+Example to make mremap.c transparent hugepage aware with a one liner
+change::
+
+       diff --git a/mm/mremap.c b/mm/mremap.c
+       --- a/mm/mremap.c
+       +++ b/mm/mremap.c
+       @@ -41,6 +41,7 @@ static pmd_t *get_old_pmd(struct mm_stru
+                       return NULL;
+
+               pmd = pmd_offset(pud, addr);
+       +       split_huge_pmd(vma, pmd, addr);
+               if (pmd_none_or_clear_bad(pmd))
+                       return NULL;
+
+Locking in hugepage aware code
+==============================
+
+We want as much code as possible hugepage aware, as calling
+split_huge_page() or split_huge_pmd() has a cost.
+
+To make pagetable walks huge pmd aware, all you need to do is to call
+pmd_trans_huge() on the pmd returned by pmd_offset. You must hold the
+mmap_lock in read (or write) mode to be sure a huge pmd cannot be
+created from under you by khugepaged (khugepaged collapse_huge_page
+takes the mmap_lock in write mode in addition to the anon_vma lock). If
+pmd_trans_huge returns false, you just fallback in the old code
+paths. If instead pmd_trans_huge returns true, you have to take the
+page table lock (pmd_lock()) and re-run pmd_trans_huge. Taking the
+page table lock will prevent the huge pmd being converted into a
+regular pmd from under you (split_huge_pmd can run in parallel to the
+pagetable walk). If the second pmd_trans_huge returns false, you
+should just drop the page table lock and fallback to the old code as
+before. Otherwise, you can proceed to process the huge pmd and the
+hugepage natively. Once finished, you can drop the page table lock.
+
+Refcounts and transparent huge pages
+====================================
+
+Refcounting on THP is mostly consistent with refcounting on other compound
+pages:
+
+  - get_page()/put_page() and GUP operate on head page's ->_refcount.
+
+  - ->_refcount in tail pages is always zero: get_page_unless_zero() never
+    succeeds on tail pages.
+
+  - map/unmap of the pages with PTE entry increment/decrement ->_mapcount
+    on relevant sub-page of the compound page.
+
+  - map/unmap of the whole compound page is accounted for in compound_mapcount
+    (stored in first tail page). For file huge pages, we also increment
+    ->_mapcount of all sub-pages in order to have race-free detection of
+    last unmap of subpages.
+
+PageDoubleMap() indicates that the page is *possibly* mapped with PTEs.
+
+For anonymous pages, PageDoubleMap() also indicates ->_mapcount in all
+subpages is offset up by one. This additional reference is required to
+get race-free detection of unmap of subpages when we have them mapped with
+both PMDs and PTEs.
+
+This optimization is required to lower the overhead of per-subpage mapcount
+tracking. The alternative is to alter ->_mapcount in all subpages on each
+map/unmap of the whole compound page.
+
+For anonymous pages, we set PG_double_map when a PMD of the page is split
+for the first time, but still have a PMD mapping. The additional references
+go away with the last compound_mapcount.
+
+File pages get PG_double_map set on the first map of the page with PTE and
+goes away when the page gets evicted from the page cache.
+
+split_huge_page internally has to distribute the refcounts in the head
+page to the tail pages before clearing all PG_head/tail bits from the page
+structures. It can be done easily for refcounts taken by page table
+entries, but we don't have enough information on how to distribute any
+additional pins (i.e. from get_user_pages). split_huge_page() fails any
+requests to split pinned huge pages: it expects page count to be equal to
+the sum of mapcount of all sub-pages plus one (split_huge_page caller must
+have a reference to the head page).
+
+split_huge_page uses migration entries to stabilize page->_refcount and
+page->_mapcount of anonymous pages. File pages just get unmapped.
+
+We are safe against physical memory scanners too: the only legitimate way
+a scanner can get a reference to a page is get_page_unless_zero().
+
+All tail pages have zero ->_refcount until atomic_add(). This prevents the
+scanner from getting a reference to the tail page up to that point. After the
+atomic_add() we don't care about the ->_refcount value. We already know how
+many references should be uncharged from the head page.
+
+For head page get_page_unless_zero() will succeed and we don't mind. It's
+clear where references should go after split: it will stay on the head page.
+
+Note that split_huge_pmd() doesn't have any limitations on refcounting:
+pmd can be split at any point and never fails.
+
+Partial unmap and deferred_split_huge_page()
+============================================
+
+Unmapping part of THP (with munmap() or other way) is not going to free
+memory immediately. Instead, we detect that a subpage of THP is not in use
+in page_remove_rmap() and queue the THP for splitting if memory pressure
+comes. Splitting will free up unused subpages.
+
+Splitting the page right away is not an option due to locking context in
+the place where we can detect partial unmap. It also might be
+counterproductive since in many cases partial unmap happens during exit(2) if
+a THP crosses a VMA boundary.
+
+The function deferred_split_huge_page() is used to queue a page for splitting.
+The splitting itself will happen when we get memory pressure via shrinker
+interface.
diff --git a/Documentation/mm/unevictable-lru.rst b/Documentation/mm/unevictable-lru.rst
new file mode 100644 (file)
index 0000000..b280367
--- /dev/null
@@ -0,0 +1,554 @@
+.. _unevictable_lru:
+
+==============================
+Unevictable LRU Infrastructure
+==============================
+
+.. contents:: :local:
+
+
+Introduction
+============
+
+This document describes the Linux memory manager's "Unevictable LRU"
+infrastructure and the use of this to manage several types of "unevictable"
+pages.
+
+The document attempts to provide the overall rationale behind this mechanism
+and the rationale for some of the design decisions that drove the
+implementation.  The latter design rationale is discussed in the context of an
+implementation description.  Admittedly, one can obtain the implementation
+details - the "what does it do?" - by reading the code.  One hopes that the
+descriptions below add value by provide the answer to "why does it do that?".
+
+
+
+The Unevictable LRU
+===================
+
+The Unevictable LRU facility adds an additional LRU list to track unevictable
+pages and to hide these pages from vmscan.  This mechanism is based on a patch
+by Larry Woodman of Red Hat to address several scalability problems with page
+reclaim in Linux.  The problems have been observed at customer sites on large
+memory x86_64 systems.
+
+To illustrate this with an example, a non-NUMA x86_64 platform with 128GB of
+main memory will have over 32 million 4k pages in a single node.  When a large
+fraction of these pages are not evictable for any reason [see below], vmscan
+will spend a lot of time scanning the LRU lists looking for the small fraction
+of pages that are evictable.  This can result in a situation where all CPUs are
+spending 100% of their time in vmscan for hours or days on end, with the system
+completely unresponsive.
+
+The unevictable list addresses the following classes of unevictable pages:
+
+ * Those owned by ramfs.
+
+ * Those mapped into SHM_LOCK'd shared memory regions.
+
+ * Those mapped into VM_LOCKED [mlock()ed] VMAs.
+
+The infrastructure may also be able to handle other conditions that make pages
+unevictable, either by definition or by circumstance, in the future.
+
+
+The Unevictable LRU Page List
+-----------------------------
+
+The Unevictable LRU page list is a lie.  It was never an LRU-ordered list, but a
+companion to the LRU-ordered anonymous and file, active and inactive page lists;
+and now it is not even a page list.  But following familiar convention, here in
+this document and in the source, we often imagine it as a fifth LRU page list.
+
+The Unevictable LRU infrastructure consists of an additional, per-node, LRU list
+called the "unevictable" list and an associated page flag, PG_unevictable, to
+indicate that the page is being managed on the unevictable list.
+
+The PG_unevictable flag is analogous to, and mutually exclusive with, the
+PG_active flag in that it indicates on which LRU list a page resides when
+PG_lru is set.
+
+The Unevictable LRU infrastructure maintains unevictable pages as if they were
+on an additional LRU list for a few reasons:
+
+ (1) We get to "treat unevictable pages just like we treat other pages in the
+     system - which means we get to use the same code to manipulate them, the
+     same code to isolate them (for migrate, etc.), the same code to keep track
+     of the statistics, etc..." [Rik van Riel]
+
+ (2) We want to be able to migrate unevictable pages between nodes for memory
+     defragmentation, workload management and memory hotplug.  The Linux kernel
+     can only migrate pages that it can successfully isolate from the LRU
+     lists (or "Movable" pages: outside of consideration here).  If we were to
+     maintain pages elsewhere than on an LRU-like list, where they can be
+     detected by isolate_lru_page(), we would prevent their migration.
+
+The unevictable list does not differentiate between file-backed and anonymous,
+swap-backed pages.  This differentiation is only important while the pages are,
+in fact, evictable.
+
+The unevictable list benefits from the "arrayification" of the per-node LRU
+lists and statistics originally proposed and posted by Christoph Lameter.
+
+
+Memory Control Group Interaction
+--------------------------------
+
+The unevictable LRU facility interacts with the memory control group [aka
+memory controller; see Documentation/admin-guide/cgroup-v1/memory.rst] by
+extending the lru_list enum.
+
+The memory controller data structure automatically gets a per-node unevictable
+list as a result of the "arrayification" of the per-node LRU lists (one per
+lru_list enum element).  The memory controller tracks the movement of pages to
+and from the unevictable list.
+
+When a memory control group comes under memory pressure, the controller will
+not attempt to reclaim pages on the unevictable list.  This has a couple of
+effects:
+
+ (1) Because the pages are "hidden" from reclaim on the unevictable list, the
+     reclaim process can be more efficient, dealing only with pages that have a
+     chance of being reclaimed.
+
+ (2) On the other hand, if too many of the pages charged to the control group
+     are unevictable, the evictable portion of the working set of the tasks in
+     the control group may not fit into the available memory.  This can cause
+     the control group to thrash or to OOM-kill tasks.
+
+
+.. _mark_addr_space_unevict:
+
+Marking Address Spaces Unevictable
+----------------------------------
+
+For facilities such as ramfs none of the pages attached to the address space
+may be evicted.  To prevent eviction of any such pages, the AS_UNEVICTABLE
+address space flag is provided, and this can be manipulated by a filesystem
+using a number of wrapper functions:
+
+ * ``void mapping_set_unevictable(struct address_space *mapping);``
+
+       Mark the address space as being completely unevictable.
+
+ * ``void mapping_clear_unevictable(struct address_space *mapping);``
+
+       Mark the address space as being evictable.
+
+ * ``int mapping_unevictable(struct address_space *mapping);``
+
+       Query the address space, and return true if it is completely
+       unevictable.
+
+These are currently used in three places in the kernel:
+
+ (1) By ramfs to mark the address spaces of its inodes when they are created,
+     and this mark remains for the life of the inode.
+
+ (2) By SYSV SHM to mark SHM_LOCK'd address spaces until SHM_UNLOCK is called.
+     Note that SHM_LOCK is not required to page in the locked pages if they're
+     swapped out; the application must touch the pages manually if it wants to
+     ensure they're in memory.
+
+ (3) By the i915 driver to mark pinned address space until it's unpinned. The
+     amount of unevictable memory marked by i915 driver is roughly the bounded
+     object size in debugfs/dri/0/i915_gem_objects.
+
+
+Detecting Unevictable Pages
+---------------------------
+
+The function page_evictable() in mm/internal.h determines whether a page is
+evictable or not using the query function outlined above [see section
+:ref:`Marking address spaces unevictable <mark_addr_space_unevict>`]
+to check the AS_UNEVICTABLE flag.
+
+For address spaces that are so marked after being populated (as SHM regions
+might be), the lock action (e.g. SHM_LOCK) can be lazy, and need not populate
+the page tables for the region as does, for example, mlock(), nor need it make
+any special effort to push any pages in the SHM_LOCK'd area to the unevictable
+list.  Instead, vmscan will do this if and when it encounters the pages during
+a reclamation scan.
+
+On an unlock action (such as SHM_UNLOCK), the unlocker (e.g. shmctl()) must scan
+the pages in the region and "rescue" them from the unevictable list if no other
+condition is keeping them unevictable.  If an unevictable region is destroyed,
+the pages are also "rescued" from the unevictable list in the process of
+freeing them.
+
+page_evictable() also checks for mlocked pages by testing an additional page
+flag, PG_mlocked (as wrapped by PageMlocked()), which is set when a page is
+faulted into a VM_LOCKED VMA, or found in a VMA being VM_LOCKED.
+
+
+Vmscan's Handling of Unevictable Pages
+--------------------------------------
+
+If unevictable pages are culled in the fault path, or moved to the unevictable
+list at mlock() or mmap() time, vmscan will not encounter the pages until they
+have become evictable again (via munlock() for example) and have been "rescued"
+from the unevictable list.  However, there may be situations where we decide,
+for the sake of expediency, to leave an unevictable page on one of the regular
+active/inactive LRU lists for vmscan to deal with.  vmscan checks for such
+pages in all of the shrink_{active|inactive|page}_list() functions and will
+"cull" such pages that it encounters: that is, it diverts those pages to the
+unevictable list for the memory cgroup and node being scanned.
+
+There may be situations where a page is mapped into a VM_LOCKED VMA, but the
+page is not marked as PG_mlocked.  Such pages will make it all the way to
+shrink_active_list() or shrink_page_list() where they will be detected when
+vmscan walks the reverse map in page_referenced() or try_to_unmap().  The page
+is culled to the unevictable list when it is released by the shrinker.
+
+To "cull" an unevictable page, vmscan simply puts the page back on the LRU list
+using putback_lru_page() - the inverse operation to isolate_lru_page() - after
+dropping the page lock.  Because the condition which makes the page unevictable
+may change once the page is unlocked, __pagevec_lru_add_fn() will recheck the
+unevictable state of a page before placing it on the unevictable list.
+
+
+MLOCKED Pages
+=============
+
+The unevictable page list is also useful for mlock(), in addition to ramfs and
+SYSV SHM.  Note that mlock() is only available in CONFIG_MMU=y situations; in
+NOMMU situations, all mappings are effectively mlocked.
+
+
+History
+-------
+
+The "Unevictable mlocked Pages" infrastructure is based on work originally
+posted by Nick Piggin in an RFC patch entitled "mm: mlocked pages off LRU".
+Nick posted his patch as an alternative to a patch posted by Christoph Lameter
+to achieve the same objective: hiding mlocked pages from vmscan.
+
+In Nick's patch, he used one of the struct page LRU list link fields as a count
+of VM_LOCKED VMAs that map the page (Rik van Riel had the same idea three years
+earlier).  But this use of the link field for a count prevented the management
+of the pages on an LRU list, and thus mlocked pages were not migratable as
+isolate_lru_page() could not detect them, and the LRU list link field was not
+available to the migration subsystem.
+
+Nick resolved this by putting mlocked pages back on the LRU list before
+attempting to isolate them, thus abandoning the count of VM_LOCKED VMAs.  When
+Nick's patch was integrated with the Unevictable LRU work, the count was
+replaced by walking the reverse map when munlocking, to determine whether any
+other VM_LOCKED VMAs still mapped the page.
+
+However, walking the reverse map for each page when munlocking was ugly and
+inefficient, and could lead to catastrophic contention on a file's rmap lock,
+when many processes which had it mlocked were trying to exit.  In 5.18, the
+idea of keeping mlock_count in Unevictable LRU list link field was revived and
+put to work, without preventing the migration of mlocked pages.  This is why
+the "Unevictable LRU list" cannot be a linked list of pages now; but there was
+no use for that linked list anyway - though its size is maintained for meminfo.
+
+
+Basic Management
+----------------
+
+mlocked pages - pages mapped into a VM_LOCKED VMA - are a class of unevictable
+pages.  When such a page has been "noticed" by the memory management subsystem,
+the page is marked with the PG_mlocked flag.  This can be manipulated using the
+PageMlocked() functions.
+
+A PG_mlocked page will be placed on the unevictable list when it is added to
+the LRU.  Such pages can be "noticed" by memory management in several places:
+
+ (1) in the mlock()/mlock2()/mlockall() system call handlers;
+
+ (2) in the mmap() system call handler when mmapping a region with the
+     MAP_LOCKED flag;
+
+ (3) mmapping a region in a task that has called mlockall() with the MCL_FUTURE
+     flag;
+
+ (4) in the fault path and when a VM_LOCKED stack segment is expanded; or
+
+ (5) as mentioned above, in vmscan:shrink_page_list() when attempting to
+     reclaim a page in a VM_LOCKED VMA by page_referenced() or try_to_unmap().
+
+mlocked pages become unlocked and rescued from the unevictable list when:
+
+ (1) mapped in a range unlocked via the munlock()/munlockall() system calls;
+
+ (2) munmap()'d out of the last VM_LOCKED VMA that maps the page, including
+     unmapping at task exit;
+
+ (3) when the page is truncated from the last VM_LOCKED VMA of an mmapped file;
+     or
+
+ (4) before a page is COW'd in a VM_LOCKED VMA.
+
+
+mlock()/mlock2()/mlockall() System Call Handling
+------------------------------------------------
+
+mlock(), mlock2() and mlockall() system call handlers proceed to mlock_fixup()
+for each VMA in the range specified by the call.  In the case of mlockall(),
+this is the entire active address space of the task.  Note that mlock_fixup()
+is used for both mlocking and munlocking a range of memory.  A call to mlock()
+an already VM_LOCKED VMA, or to munlock() a VMA that is not VM_LOCKED, is
+treated as a no-op and mlock_fixup() simply returns.
+
+If the VMA passes some filtering as described in "Filtering Special VMAs"
+below, mlock_fixup() will attempt to merge the VMA with its neighbors or split
+off a subset of the VMA if the range does not cover the entire VMA.  Any pages
+already present in the VMA are then marked as mlocked by mlock_page() via
+mlock_pte_range() via walk_page_range() via mlock_vma_pages_range().
+
+Before returning from the system call, do_mlock() or mlockall() will call
+__mm_populate() to fault in the remaining pages via get_user_pages() and to
+mark those pages as mlocked as they are faulted.
+
+Note that the VMA being mlocked might be mapped with PROT_NONE.  In this case,
+get_user_pages() will be unable to fault in the pages.  That's okay.  If pages
+do end up getting faulted into this VM_LOCKED VMA, they will be handled in the
+fault path - which is also how mlock2()'s MLOCK_ONFAULT areas are handled.
+
+For each PTE (or PMD) being faulted into a VMA, the page add rmap function
+calls mlock_vma_page(), which calls mlock_page() when the VMA is VM_LOCKED
+(unless it is a PTE mapping of a part of a transparent huge page).  Or when
+it is a newly allocated anonymous page, lru_cache_add_inactive_or_unevictable()
+calls mlock_new_page() instead: similar to mlock_page(), but can make better
+judgments, since this page is held exclusively and known not to be on LRU yet.
+
+mlock_page() sets PageMlocked immediately, then places the page on the CPU's
+mlock pagevec, to batch up the rest of the work to be done under lru_lock by
+__mlock_page().  __mlock_page() sets PageUnevictable, initializes mlock_count
+and moves the page to unevictable state ("the unevictable LRU", but with
+mlock_count in place of LRU threading).  Or if the page was already PageLRU
+and PageUnevictable and PageMlocked, it simply increments the mlock_count.
+
+But in practice that may not work ideally: the page may not yet be on an LRU, or
+it may have been temporarily isolated from LRU.  In such cases the mlock_count
+field cannot be touched, but will be set to 0 later when __pagevec_lru_add_fn()
+returns the page to "LRU".  Races prohibit mlock_count from being set to 1 then:
+rather than risk stranding a page indefinitely as unevictable, always err with
+mlock_count on the low side, so that when munlocked the page will be rescued to
+an evictable LRU, then perhaps be mlocked again later if vmscan finds it in a
+VM_LOCKED VMA.
+
+
+Filtering Special VMAs
+----------------------
+
+mlock_fixup() filters several classes of "special" VMAs:
+
+1) VMAs with VM_IO or VM_PFNMAP set are skipped entirely.  The pages behind
+   these mappings are inherently pinned, so we don't need to mark them as
+   mlocked.  In any case, most of the pages have no struct page in which to so
+   mark the page.  Because of this, get_user_pages() will fail for these VMAs,
+   so there is no sense in attempting to visit them.
+
+2) VMAs mapping hugetlbfs page are already effectively pinned into memory.  We
+   neither need nor want to mlock() these pages.  But __mm_populate() includes
+   hugetlbfs ranges, allocating the huge pages and populating the PTEs.
+
+3) VMAs with VM_DONTEXPAND are generally userspace mappings of kernel pages,
+   such as the VDSO page, relay channel pages, etc.  These pages are inherently
+   unevictable and are not managed on the LRU lists.  __mm_populate() includes
+   these ranges, populating the PTEs if not already populated.
+
+4) VMAs with VM_MIXEDMAP set are not marked VM_LOCKED, but __mm_populate()
+   includes these ranges, populating the PTEs if not already populated.
+
+Note that for all of these special VMAs, mlock_fixup() does not set the
+VM_LOCKED flag.  Therefore, we won't have to deal with them later during
+munlock(), munmap() or task exit.  Neither does mlock_fixup() account these
+VMAs against the task's "locked_vm".
+
+
+munlock()/munlockall() System Call Handling
+-------------------------------------------
+
+The munlock() and munlockall() system calls are handled by the same
+mlock_fixup() function as mlock(), mlock2() and mlockall() system calls are.
+If called to munlock an already munlocked VMA, mlock_fixup() simply returns.
+Because of the VMA filtering discussed above, VM_LOCKED will not be set in
+any "special" VMAs.  So, those VMAs will be ignored for munlock.
+
+If the VMA is VM_LOCKED, mlock_fixup() again attempts to merge or split off the
+specified range.  All pages in the VMA are then munlocked by munlock_page() via
+mlock_pte_range() via walk_page_range() via mlock_vma_pages_range() - the same
+function used when mlocking a VMA range, with new flags for the VMA indicating
+that it is munlock() being performed.
+
+munlock_page() uses the mlock pagevec to batch up work to be done under
+lru_lock by  __munlock_page().  __munlock_page() decrements the page's
+mlock_count, and when that reaches 0 it clears PageMlocked and clears
+PageUnevictable, moving the page from unevictable state to inactive LRU.
+
+But in practice that may not work ideally: the page may not yet have reached
+"the unevictable LRU", or it may have been temporarily isolated from it.  In
+those cases its mlock_count field is unusable and must be assumed to be 0: so
+that the page will be rescued to an evictable LRU, then perhaps be mlocked
+again later if vmscan finds it in a VM_LOCKED VMA.
+
+
+Migrating MLOCKED Pages
+-----------------------
+
+A page that is being migrated has been isolated from the LRU lists and is held
+locked across unmapping of the page, updating the page's address space entry
+and copying the contents and state, until the page table entry has been
+replaced with an entry that refers to the new page.  Linux supports migration
+of mlocked pages and other unevictable pages.  PG_mlocked is cleared from the
+the old page when it is unmapped from the last VM_LOCKED VMA, and set when the
+new page is mapped in place of migration entry in a VM_LOCKED VMA.  If the page
+was unevictable because mlocked, PG_unevictable follows PG_mlocked; but if the
+page was unevictable for other reasons, PG_unevictable is copied explicitly.
+
+Note that page migration can race with mlocking or munlocking of the same page.
+There is mostly no problem since page migration requires unmapping all PTEs of
+the old page (including munlock where VM_LOCKED), then mapping in the new page
+(including mlock where VM_LOCKED).  The page table locks provide sufficient
+synchronization.
+
+However, since mlock_vma_pages_range() starts by setting VM_LOCKED on a VMA,
+before mlocking any pages already present, if one of those pages were migrated
+before mlock_pte_range() reached it, it would get counted twice in mlock_count.
+To prevent that, mlock_vma_pages_range() temporarily marks the VMA as VM_IO,
+so that mlock_vma_page() will skip it.
+
+To complete page migration, we place the old and new pages back onto the LRU
+afterwards.  The "unneeded" page - old page on success, new page on failure -
+is freed when the reference count held by the migration process is released.
+
+
+Compacting MLOCKED Pages
+------------------------
+
+The memory map can be scanned for compactable regions and the default behavior
+is to let unevictable pages be moved.  /proc/sys/vm/compact_unevictable_allowed
+controls this behavior (see Documentation/admin-guide/sysctl/vm.rst).  The work
+of compaction is mostly handled by the page migration code and the same work
+flow as described in Migrating MLOCKED Pages will apply.
+
+
+MLOCKING Transparent Huge Pages
+-------------------------------
+
+A transparent huge page is represented by a single entry on an LRU list.
+Therefore, we can only make unevictable an entire compound page, not
+individual subpages.
+
+If a user tries to mlock() part of a huge page, and no user mlock()s the
+whole of the huge page, we want the rest of the page to be reclaimable.
+
+We cannot just split the page on partial mlock() as split_huge_page() can
+fail and a new intermittent failure mode for the syscall is undesirable.
+
+We handle this by keeping PTE-mlocked huge pages on evictable LRU lists:
+the PMD on the border of a VM_LOCKED VMA will be split into a PTE table.
+
+This way the huge page is accessible for vmscan.  Under memory pressure the
+page will be split, subpages which belong to VM_LOCKED VMAs will be moved
+to the unevictable LRU and the rest can be reclaimed.
+
+/proc/meminfo's Unevictable and Mlocked amounts do not include those parts
+of a transparent huge page which are mapped only by PTEs in VM_LOCKED VMAs.
+
+
+mmap(MAP_LOCKED) System Call Handling
+-------------------------------------
+
+In addition to the mlock(), mlock2() and mlockall() system calls, an application
+can request that a region of memory be mlocked by supplying the MAP_LOCKED flag
+to the mmap() call.  There is one important and subtle difference here, though.
+mmap() + mlock() will fail if the range cannot be faulted in (e.g. because
+mm_populate fails) and returns with ENOMEM while mmap(MAP_LOCKED) will not fail.
+The mmaped area will still have properties of the locked area - pages will not
+get swapped out - but major page faults to fault memory in might still happen.
+
+Furthermore, any mmap() call or brk() call that expands the heap by a task
+that has previously called mlockall() with the MCL_FUTURE flag will result
+in the newly mapped memory being mlocked.  Before the unevictable/mlock
+changes, the kernel simply called make_pages_present() to allocate pages
+and populate the page table.
+
+To mlock a range of memory under the unevictable/mlock infrastructure,
+the mmap() handler and task address space expansion functions call
+populate_vma_page_range() specifying the vma and the address range to mlock.
+
+
+munmap()/exit()/exec() System Call Handling
+-------------------------------------------
+
+When unmapping an mlocked region of memory, whether by an explicit call to
+munmap() or via an internal unmap from exit() or exec() processing, we must
+munlock the pages if we're removing the last VM_LOCKED VMA that maps the pages.
+Before the unevictable/mlock changes, mlocking did not mark the pages in any
+way, so unmapping them required no processing.
+
+For each PTE (or PMD) being unmapped from a VMA, page_remove_rmap() calls
+munlock_vma_page(), which calls munlock_page() when the VMA is VM_LOCKED
+(unless it was a PTE mapping of a part of a transparent huge page).
+
+munlock_page() uses the mlock pagevec to batch up work to be done under
+lru_lock by  __munlock_page().  __munlock_page() decrements the page's
+mlock_count, and when that reaches 0 it clears PageMlocked and clears
+PageUnevictable, moving the page from unevictable state to inactive LRU.
+
+But in practice that may not work ideally: the page may not yet have reached
+"the unevictable LRU", or it may have been temporarily isolated from it.  In
+those cases its mlock_count field is unusable and must be assumed to be 0: so
+that the page will be rescued to an evictable LRU, then perhaps be mlocked
+again later if vmscan finds it in a VM_LOCKED VMA.
+
+
+Truncating MLOCKED Pages
+------------------------
+
+File truncation or hole punching forcibly unmaps the deleted pages from
+userspace; truncation even unmaps and deletes any private anonymous pages
+which had been Copied-On-Write from the file pages now being truncated.
+
+Mlocked pages can be munlocked and deleted in this way: like with munmap(),
+for each PTE (or PMD) being unmapped from a VMA, page_remove_rmap() calls
+munlock_vma_page(), which calls munlock_page() when the VMA is VM_LOCKED
+(unless it was a PTE mapping of a part of a transparent huge page).
+
+However, if there is a racing munlock(), since mlock_vma_pages_range() starts
+munlocking by clearing VM_LOCKED from a VMA, before munlocking all the pages
+present, if one of those pages were unmapped by truncation or hole punch before
+mlock_pte_range() reached it, it would not be recognized as mlocked by this VMA,
+and would not be counted out of mlock_count.  In this rare case, a page may
+still appear as PageMlocked after it has been fully unmapped: and it is left to
+release_pages() (or __page_cache_release()) to clear it and update statistics
+before freeing (this event is counted in /proc/vmstat unevictable_pgs_cleared,
+which is usually 0).
+
+
+Page Reclaim in shrink_*_list()
+-------------------------------
+
+vmscan's shrink_active_list() culls any obviously unevictable pages -
+i.e. !page_evictable(page) pages - diverting those to the unevictable list.
+However, shrink_active_list() only sees unevictable pages that made it onto the
+active/inactive LRU lists.  Note that these pages do not have PageUnevictable
+set - otherwise they would be on the unevictable list and shrink_active_list()
+would never see them.
+
+Some examples of these unevictable pages on the LRU lists are:
+
+ (1) ramfs pages that have been placed on the LRU lists when first allocated.
+
+ (2) SHM_LOCK'd shared memory pages.  shmctl(SHM_LOCK) does not attempt to
+     allocate or fault in the pages in the shared memory region.  This happens
+     when an application accesses the page the first time after SHM_LOCK'ing
+     the segment.
+
+ (3) pages still mapped into VM_LOCKED VMAs, which should be marked mlocked,
+     but events left mlock_count too low, so they were munlocked too early.
+
+vmscan's shrink_inactive_list() and shrink_page_list() also divert obviously
+unevictable pages found on the inactive lists to the appropriate memory cgroup
+and node unevictable list.
+
+rmap's page_referenced_one(), called via vmscan's shrink_active_list() or
+shrink_page_list(), and rmap's try_to_unmap_one() called via shrink_page_list(),
+check for (3) pages still mapped into VM_LOCKED VMAs, and call mlock_vma_page()
+to correct them.  Such pages are culled to the unevictable list when released
+by the shrinker.
diff --git a/Documentation/mm/vmalloc.rst b/Documentation/mm/vmalloc.rst
new file mode 100644 (file)
index 0000000..363fe20
--- /dev/null
@@ -0,0 +1,5 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================================
+Virtually Contiguous Memory Allocation
+======================================
diff --git a/Documentation/mm/vmalloced-kernel-stacks.rst b/Documentation/mm/vmalloced-kernel-stacks.rst
new file mode 100644 (file)
index 0000000..fc8c678
--- /dev/null
@@ -0,0 +1,153 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=====================================
+Virtually Mapped Kernel Stack Support
+=====================================
+
+:Author: Shuah Khan <skhan@linuxfoundation.org>
+
+.. contents:: :local:
+
+Overview
+--------
+
+This is a compilation of information from the code and original patch
+series that introduced the `Virtually Mapped Kernel Stacks feature
+<https://lwn.net/Articles/694348/>`
+
+Introduction
+------------
+
+Kernel stack overflows are often hard to debug and make the kernel
+susceptible to exploits. Problems could show up at a later time making
+it difficult to isolate and root-cause.
+
+Virtually-mapped kernel stacks with guard pages causes kernel stack
+overflows to be caught immediately rather than causing difficult to
+diagnose corruptions.
+
+HAVE_ARCH_VMAP_STACK and VMAP_STACK configuration options enable
+support for virtually mapped stacks with guard pages. This feature
+causes reliable faults when the stack overflows. The usability of
+the stack trace after overflow and response to the overflow itself
+is architecture dependent.
+
+.. note::
+        As of this writing, arm64, powerpc, riscv, s390, um, and x86 have
+        support for VMAP_STACK.
+
+HAVE_ARCH_VMAP_STACK
+--------------------
+
+Architectures that can support Virtually Mapped Kernel Stacks should
+enable this bool configuration option. The requirements are:
+
+- vmalloc space must be large enough to hold many kernel stacks. This
+  may rule out many 32-bit architectures.
+- Stacks in vmalloc space need to work reliably.  For example, if
+  vmap page tables are created on demand, either this mechanism
+  needs to work while the stack points to a virtual address with
+  unpopulated page tables or arch code (switch_to() and switch_mm(),
+  most likely) needs to ensure that the stack's page table entries
+  are populated before running on a possibly unpopulated stack.
+- If the stack overflows into a guard page, something reasonable
+  should happen. The definition of "reasonable" is flexible, but
+  instantly rebooting without logging anything would be unfriendly.
+
+VMAP_STACK
+----------
+
+VMAP_STACK bool configuration option when enabled allocates virtually
+mapped task stacks. This option depends on HAVE_ARCH_VMAP_STACK.
+
+- Enable this if you want the use virtually-mapped kernel stacks
+  with guard pages. This causes kernel stack overflows to be caught
+  immediately rather than causing difficult-to-diagnose corruption.
+
+.. note::
+
+        Using this feature with KASAN requires architecture support
+        for backing virtual mappings with real shadow memory, and
+        KASAN_VMALLOC must be enabled.
+
+.. note::
+
+        VMAP_STACK is enabled, it is not possible to run DMA on stack
+        allocated data.
+
+Kernel configuration options and dependencies keep changing. Refer to
+the latest code base:
+
+`Kconfig <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/Kconfig>`
+
+Allocation
+-----------
+
+When a new kernel thread is created, thread stack is allocated from
+virtually contiguous memory pages from the page level allocator. These
+pages are mapped into contiguous kernel virtual space with PAGE_KERNEL
+protections.
+
+alloc_thread_stack_node() calls __vmalloc_node_range() to allocate stack
+with PAGE_KERNEL protections.
+
+- Allocated stacks are cached and later reused by new threads, so memcg
+  accounting is performed manually on assigning/releasing stacks to tasks.
+  Hence, __vmalloc_node_range is called without __GFP_ACCOUNT.
+- vm_struct is cached to be able to find when thread free is initiated
+  in interrupt context. free_thread_stack() can be called in interrupt
+  context.
+- On arm64, all VMAP's stacks need to have the same alignment to ensure
+  that VMAP'd stack overflow detection works correctly. Arch specific
+  vmap stack allocator takes care of this detail.
+- This does not address interrupt stacks - according to the original patch
+
+Thread stack allocation is initiated from clone(), fork(), vfork(),
+kernel_thread() via kernel_clone(). Leaving a few hints for searching
+the code base to understand when and how thread stack is allocated.
+
+Bulk of the code is in:
+`kernel/fork.c <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/fork.c>`.
+
+stack_vm_area pointer in task_struct keeps track of the virtually allocated
+stack and a non-null stack_vm_area pointer serves as a indication that the
+virtually mapped kernel stacks are enabled.
+
+::
+
+        struct vm_struct *stack_vm_area;
+
+Stack overflow handling
+-----------------------
+
+Leading and trailing guard pages help detect stack overflows. When stack
+overflows into the guard pages, handlers have to be careful not overflow
+the stack again. When handlers are called, it is likely that very little
+stack space is left.
+
+On x86, this is done by handling the page fault indicating the kernel
+stack overflow on the double-fault stack.
+
+Testing VMAP allocation with guard pages
+----------------------------------------
+
+How do we ensure that VMAP_STACK is actually allocating with a leading
+and trailing guard page? The following lkdtm tests can help detect any
+regressions.
+
+::
+
+        void lkdtm_STACK_GUARD_PAGE_LEADING()
+        void lkdtm_STACK_GUARD_PAGE_TRAILING()
+
+Conclusions
+-----------
+
+- A percpu cache of vmalloced stacks appears to be a bit faster than a
+  high-order stack allocation, at least when the cache hits.
+- THREAD_INFO_IN_TASK gets rid of arch-specific thread_info entirely and
+  simply embed the thread_info (containing only flags) and 'int cpu' into
+  task_struct.
+- The thread stack can be free'ed as soon as the task is dead (without
+  waiting for RCU) and then, if vmapped stacks are in use, cache the
+  entire stack for reuse on the same cpu.
diff --git a/Documentation/mm/vmemmap_dedup.rst b/Documentation/mm/vmemmap_dedup.rst
new file mode 100644 (file)
index 0000000..c9c495f
--- /dev/null
@@ -0,0 +1,223 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================================
+A vmemmap diet for HugeTLB and Device DAX
+=========================================
+
+HugeTLB
+=======
+
+The struct page structures (page structs) are used to describe a physical
+page frame. By default, there is a one-to-one mapping from a page frame to
+it's corresponding page struct.
+
+HugeTLB pages consist of multiple base page size pages and is supported by many
+architectures. See Documentation/admin-guide/mm/hugetlbpage.rst for more
+details. On the x86-64 architecture, HugeTLB pages of size 2MB and 1GB are
+currently supported. Since the base page size on x86 is 4KB, a 2MB HugeTLB page
+consists of 512 base pages and a 1GB HugeTLB page consists of 4096 base pages.
+For each base page, there is a corresponding page struct.
+
+Within the HugeTLB subsystem, only the first 4 page structs are used to
+contain unique information about a HugeTLB page. __NR_USED_SUBPAGE provides
+this upper limit. The only 'useful' information in the remaining page structs
+is the compound_head field, and this field is the same for all tail pages.
+
+By removing redundant page structs for HugeTLB pages, memory can be returned
+to the buddy allocator for other uses.
+
+Different architectures support different HugeTLB pages. For example, the
+following table is the HugeTLB page size supported by x86 and arm64
+architectures. Because arm64 supports 4k, 16k, and 64k base pages and
+supports contiguous entries, so it supports many kinds of sizes of HugeTLB
+page.
+
++--------------+-----------+-----------------------------------------------+
+| Architecture | Page Size |                HugeTLB Page Size              |
++--------------+-----------+-----------+-----------+-----------+-----------+
+|    x86-64    |    4KB    |    2MB    |    1GB    |           |           |
++--------------+-----------+-----------+-----------+-----------+-----------+
+|              |    4KB    |   64KB    |    2MB    |    32MB   |    1GB    |
+|              +-----------+-----------+-----------+-----------+-----------+
+|    arm64     |   16KB    |    2MB    |   32MB    |     1GB   |           |
+|              +-----------+-----------+-----------+-----------+-----------+
+|              |   64KB    |    2MB    |  512MB    |    16GB   |           |
++--------------+-----------+-----------+-----------+-----------+-----------+
+
+When the system boot up, every HugeTLB page has more than one struct page
+structs which size is (unit: pages)::
+
+   struct_size = HugeTLB_Size / PAGE_SIZE * sizeof(struct page) / PAGE_SIZE
+
+Where HugeTLB_Size is the size of the HugeTLB page. We know that the size
+of the HugeTLB page is always n times PAGE_SIZE. So we can get the following
+relationship::
+
+   HugeTLB_Size = n * PAGE_SIZE
+
+Then::
+
+   struct_size = n * PAGE_SIZE / PAGE_SIZE * sizeof(struct page) / PAGE_SIZE
+               = n * sizeof(struct page) / PAGE_SIZE
+
+We can use huge mapping at the pud/pmd level for the HugeTLB page.
+
+For the HugeTLB page of the pmd level mapping, then::
+
+   struct_size = n * sizeof(struct page) / PAGE_SIZE
+               = PAGE_SIZE / sizeof(pte_t) * sizeof(struct page) / PAGE_SIZE
+               = sizeof(struct page) / sizeof(pte_t)
+               = 64 / 8
+               = 8 (pages)
+
+Where n is how many pte entries which one page can contains. So the value of
+n is (PAGE_SIZE / sizeof(pte_t)).
+
+This optimization only supports 64-bit system, so the value of sizeof(pte_t)
+is 8. And this optimization also applicable only when the size of struct page
+is a power of two. In most cases, the size of struct page is 64 bytes (e.g.
+x86-64 and arm64). So if we use pmd level mapping for a HugeTLB page, the
+size of struct page structs of it is 8 page frames which size depends on the
+size of the base page.
+
+For the HugeTLB page of the pud level mapping, then::
+
+   struct_size = PAGE_SIZE / sizeof(pmd_t) * struct_size(pmd)
+               = PAGE_SIZE / 8 * 8 (pages)
+               = PAGE_SIZE (pages)
+
+Where the struct_size(pmd) is the size of the struct page structs of a
+HugeTLB page of the pmd level mapping.
+
+E.g.: A 2MB HugeTLB page on x86_64 consists in 8 page frames while 1GB
+HugeTLB page consists in 4096.
+
+Next, we take the pmd level mapping of the HugeTLB page as an example to
+show the internal implementation of this optimization. There are 8 pages
+struct page structs associated with a HugeTLB page which is pmd mapped.
+
+Here is how things look before optimization::
+
+    HugeTLB                  struct pages(8 pages)         page frame(8 pages)
+ +-----------+ ---virt_to_page---> +-----------+   mapping to   +-----------+
+ |           |                     |     0     | -------------> |     0     |
+ |           |                     +-----------+                +-----------+
+ |           |                     |     1     | -------------> |     1     |
+ |           |                     +-----------+                +-----------+
+ |           |                     |     2     | -------------> |     2     |
+ |           |                     +-----------+                +-----------+
+ |           |                     |     3     | -------------> |     3     |
+ |           |                     +-----------+                +-----------+
+ |           |                     |     4     | -------------> |     4     |
+ |    PMD    |                     +-----------+                +-----------+
+ |   level   |                     |     5     | -------------> |     5     |
+ |  mapping  |                     +-----------+                +-----------+
+ |           |                     |     6     | -------------> |     6     |
+ |           |                     +-----------+                +-----------+
+ |           |                     |     7     | -------------> |     7     |
+ |           |                     +-----------+                +-----------+
+ |           |
+ |           |
+ |           |
+ +-----------+
+
+The value of page->compound_head is the same for all tail pages. The first
+page of page structs (page 0) associated with the HugeTLB page contains the 4
+page structs necessary to describe the HugeTLB. The only use of the remaining
+pages of page structs (page 1 to page 7) is to point to page->compound_head.
+Therefore, we can remap pages 1 to 7 to page 0. Only 1 page of page structs
+will be used for each HugeTLB page. This will allow us to free the remaining
+7 pages to the buddy allocator.
+
+Here is how things look after remapping::
+
+    HugeTLB                  struct pages(8 pages)         page frame(8 pages)
+ +-----------+ ---virt_to_page---> +-----------+   mapping to   +-----------+
+ |           |                     |     0     | -------------> |     0     |
+ |           |                     +-----------+                +-----------+
+ |           |                     |     1     | ---------------^ ^ ^ ^ ^ ^ ^
+ |           |                     +-----------+                  | | | | | |
+ |           |                     |     2     | -----------------+ | | | | |
+ |           |                     +-----------+                    | | | | |
+ |           |                     |     3     | -------------------+ | | | |
+ |           |                     +-----------+                      | | | |
+ |           |                     |     4     | ---------------------+ | | |
+ |    PMD    |                     +-----------+                        | | |
+ |   level   |                     |     5     | -----------------------+ | |
+ |  mapping  |                     +-----------+                          | |
+ |           |                     |     6     | -------------------------+ |
+ |           |                     +-----------+                            |
+ |           |                     |     7     | ---------------------------+
+ |           |                     +-----------+
+ |           |
+ |           |
+ |           |
+ +-----------+
+
+When a HugeTLB is freed to the buddy system, we should allocate 7 pages for
+vmemmap pages and restore the previous mapping relationship.
+
+For the HugeTLB page of the pud level mapping. It is similar to the former.
+We also can use this approach to free (PAGE_SIZE - 1) vmemmap pages.
+
+Apart from the HugeTLB page of the pmd/pud level mapping, some architectures
+(e.g. aarch64) provides a contiguous bit in the translation table entries
+that hints to the MMU to indicate that it is one of a contiguous set of
+entries that can be cached in a single TLB entry.
+
+The contiguous bit is used to increase the mapping size at the pmd and pte
+(last) level. So this type of HugeTLB page can be optimized only when its
+size of the struct page structs is greater than 1 page.
+
+Notice: The head vmemmap page is not freed to the buddy allocator and all
+tail vmemmap pages are mapped to the head vmemmap page frame. So we can see
+more than one struct page struct with PG_head (e.g. 8 per 2 MB HugeTLB page)
+associated with each HugeTLB page. The compound_head() can handle this
+correctly (more details refer to the comment above compound_head()).
+
+Device DAX
+==========
+
+The device-dax interface uses the same tail deduplication technique explained
+in the previous chapter, except when used with the vmemmap in
+the device (altmap).
+
+The following page sizes are supported in DAX: PAGE_SIZE (4K on x86_64),
+PMD_SIZE (2M on x86_64) and PUD_SIZE (1G on x86_64).
+
+The differences with HugeTLB are relatively minor.
+
+It only use 3 page structs for storing all information as opposed
+to 4 on HugeTLB pages.
+
+There's no remapping of vmemmap given that device-dax memory is not part of
+System RAM ranges initialized at boot. Thus the tail page deduplication
+happens at a later stage when we populate the sections. HugeTLB reuses the
+the head vmemmap page representing, whereas device-dax reuses the tail
+vmemmap page. This results in only half of the savings compared to HugeTLB.
+
+Deduplicated tail pages are not mapped read-only.
+
+Here's how things look like on device-dax after the sections are populated::
+
+ +-----------+ ---virt_to_page---> +-----------+   mapping to   +-----------+
+ |           |                     |     0     | -------------> |     0     |
+ |           |                     +-----------+                +-----------+
+ |           |                     |     1     | -------------> |     1     |
+ |           |                     +-----------+                +-----------+
+ |           |                     |     2     | ----------------^ ^ ^ ^ ^ ^
+ |           |                     +-----------+                   | | | | |
+ |           |                     |     3     | ------------------+ | | | |
+ |           |                     +-----------+                     | | | |
+ |           |                     |     4     | --------------------+ | | |
+ |    PMD    |                     +-----------+                       | | |
+ |   level   |                     |     5     | ----------------------+ | |
+ |  mapping  |                     +-----------+                         | |
+ |           |                     |     6     | ------------------------+ |
+ |           |                     +-----------+                           |
+ |           |                     |     7     | --------------------------+
+ |           |                     +-----------+
+ |           |
+ |           |
+ |           |
+ +-----------+
diff --git a/Documentation/mm/z3fold.rst b/Documentation/mm/z3fold.rst
new file mode 100644 (file)
index 0000000..224e3c6
--- /dev/null
@@ -0,0 +1,30 @@
+.. _z3fold:
+
+======
+z3fold
+======
+
+z3fold is a special purpose allocator for storing compressed pages.
+It is designed to store up to three compressed pages per physical page.
+It is a zbud derivative which allows for higher compression
+ratio keeping the simplicity and determinism of its predecessor.
+
+The main differences between z3fold and zbud are:
+
+* unlike zbud, z3fold allows for up to PAGE_SIZE allocations
+* z3fold can hold up to 3 compressed pages in its page
+* z3fold doesn't export any API itself and is thus intended to be used
+  via the zpool API.
+
+To keep the determinism and simplicity, z3fold, just like zbud, always
+stores an integral number of compressed pages per page, but it can store
+up to 3 pages unlike zbud which can store at most 2. Therefore the
+compression ratio goes to around 2.7x while zbud's one is around 1.7x.
+
+Unlike zbud (but like zsmalloc for that matter) z3fold_alloc() does not
+return a dereferenceable pointer. Instead, it returns an unsigned long
+handle which encodes actual location of the allocated object.
+
+Keeping effective compression ratio close to zsmalloc's, z3fold doesn't
+depend on MMU enabled and provides more predictable reclaim behavior
+which makes it a better fit for small and response-critical systems.
diff --git a/Documentation/mm/zsmalloc.rst b/Documentation/mm/zsmalloc.rst
new file mode 100644 (file)
index 0000000..6e79893
--- /dev/null
@@ -0,0 +1,82 @@
+.. _zsmalloc:
+
+========
+zsmalloc
+========
+
+This allocator is designed for use with zram. Thus, the allocator is
+supposed to work well under low memory conditions. In particular, it
+never attempts higher order page allocation which is very likely to
+fail under memory pressure. On the other hand, if we just use single
+(0-order) pages, it would suffer from very high fragmentation --
+any object of size PAGE_SIZE/2 or larger would occupy an entire page.
+This was one of the major issues with its predecessor (xvmalloc).
+
+To overcome these issues, zsmalloc allocates a bunch of 0-order pages
+and links them together using various 'struct page' fields. These linked
+pages act as a single higher-order page i.e. an object can span 0-order
+page boundaries. The code refers to these linked pages as a single entity
+called zspage.
+
+For simplicity, zsmalloc can only allocate objects of size up to PAGE_SIZE
+since this satisfies the requirements of all its current users (in the
+worst case, page is incompressible and is thus stored "as-is" i.e. in
+uncompressed form). For allocation requests larger than this size, failure
+is returned (see zs_malloc).
+
+Additionally, zs_malloc() does not return a dereferenceable pointer.
+Instead, it returns an opaque handle (unsigned long) which encodes actual
+location of the allocated object. The reason for this indirection is that
+zsmalloc does not keep zspages permanently mapped since that would cause
+issues on 32-bit systems where the VA region for kernel space mappings
+is very small. So, before using the allocating memory, the object has to
+be mapped using zs_map_object() to get a usable pointer and subsequently
+unmapped using zs_unmap_object().
+
+stat
+====
+
+With CONFIG_ZSMALLOC_STAT, we could see zsmalloc internal information via
+``/sys/kernel/debug/zsmalloc/<user name>``. Here is a sample of stat output::
+
+ # cat /sys/kernel/debug/zsmalloc/zram0/classes
+
+ class  size almost_full almost_empty obj_allocated   obj_used pages_used pages_per_zspage
+    ...
+    ...
+     9   176           0            1           186        129          8                4
+    10   192           1            0          2880       2872        135                3
+    11   208           0            1           819        795         42                2
+    12   224           0            1           219        159         12                4
+    ...
+    ...
+
+
+class
+       index
+size
+       object size zspage stores
+almost_empty
+       the number of ZS_ALMOST_EMPTY zspages(see below)
+almost_full
+       the number of ZS_ALMOST_FULL zspages(see below)
+obj_allocated
+       the number of objects allocated
+obj_used
+       the number of objects allocated to the user
+pages_used
+       the number of pages allocated for the class
+pages_per_zspage
+       the number of 0-order pages to make a zspage
+
+We assign a zspage to ZS_ALMOST_EMPTY fullness group when n <= N / f, where
+
+* n = number of allocated objects
+* N = total number of objects zspage can store
+* f = fullness_threshold_frac(ie, 4 at the moment)
+
+Similarly, we assign zspage to:
+
+* ZS_ALMOST_FULL  when n > N / f
+* ZS_EMPTY        when n == 0
+* ZS_FULL         when n == N
index 0c82761..30c69e1 100644 (file)
@@ -13,7 +13,7 @@
 监测数据访问
 ============
 
-:doc:`DAMON </vm/damon/index>` 允许轻量级的数据访问监测。使用DAMON,
+:doc:`DAMON </mm/damon/index>` 允许轻量级的数据访问监测。使用DAMON,
 用户可以分析他们系统的内存访问模式,并优化它们。
 
 .. toctree::
index 1500bdb..c976f3e 100644 (file)
@@ -229,4 +229,4 @@ DAMON_RECLAIM再次什么都不做,这样我们就可以退回到基于LRU列
 
 .. [1] https://research.google/pubs/pub48551/
 .. [2] https://lwn.net/Articles/787611/
-.. [3] https://www.kernel.org/doc/html/latest/vm/free_page_reporting.html
+.. [3] https://www.kernel.org/doc/html/latest/mm/free_page_reporting.html
index 2c7d910..aeae2ab 100644 (file)
@@ -33,9 +33,9 @@ DAMON 为不同的用户提供了下面这些接口。
   口相同。这将在下一个LTS内核发布后被移除,所以用户应该转移到
   :ref:`sysfs interface <sysfs_interface>`。
 - *内核空间编程接口。*
-  :doc:`这 </vm/damon/api>` 这是为内核空间程序员准备的。使用它,用户可以通过为你编写内
+  :doc:`这 </mm/damon/api>` 这是为内核空间程序员准备的。使用它,用户可以通过为你编写内
   核空间的DAMON应用程序,最灵活有效地利用DAMON的每一个功能。你甚至可以为各种地址空间扩展DAMON。
-  详细情况请参考接口 :doc:`文件 </vm/damon/api>`。
+  详细情况请参考接口 :doc:`文件 </mm/damon/api>`。
 
 sysfs接口
 =========
@@ -148,7 +148,7 @@ contexts/<N>/monitoring_attrs/
 在 ``nr_regions`` 目录下,有两个文件分别用于DAMON监测区域的下限和上限(``min`` 和 ``max`` ),
 这两个文件控制着监测的开销。你可以通过向这些文件的写入和读出来设置和获取这些值。
 
-关于间隔和监测区域范围的更多细节,请参考设计文件 (:doc:`/vm/damon/design`)。
+关于间隔和监测区域范围的更多细节,请参考设计文件 (:doc:`/mm/damon/design`)。
 
 contexts/<N>/targets/
 ---------------------
@@ -320,7 +320,7 @@ DAMON导出了八个文件, ``attrs``, ``target_ids``, ``init_regions``,
 ----
 
 用户可以通过读取和写入 ``attrs`` 文件获得和设置 ``采样间隔`` 、 ``聚集间隔`` 、 ``更新间隔``
-以及监测目标区域的最小/最大数量。要详细了解监测属性,请参考 `:doc:/vm/damon/design` 。例如,
+以及监测目标区域的最小/最大数量。要详细了解监测属性,请参考 `:doc:/mm/damon/design` 。例如,
 下面的命令将这些值设置为5ms、100ms、1000ms、10和1000,然后再次检查::
 
     # cd <debugfs>/damon
index 10bc438..8a94ad8 100644 (file)
@@ -101,7 +101,7 @@ Todolist:
 ========
 
 如何在内核中分配和使用内存。请注意,在
-:doc:`/vm/index` 中有更多的内存管理文档。
+:doc:`/mm/index` 中有更多的内存管理文档。
 
 .. toctree::
    :maxdepth: 1
index ad7bb8c..bf85bac 100644 (file)
@@ -118,7 +118,7 @@ TODOList:
    sound/index
    filesystems/index
    scheduler/index
-   vm/index
+   mm/index
    peci/index
 
 TODOList:
diff --git a/Documentation/translations/zh_CN/mm/active_mm.rst b/Documentation/translations/zh_CN/mm/active_mm.rst
new file mode 100644 (file)
index 0000000..c2816f5
--- /dev/null
@@ -0,0 +1,85 @@
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/mm/active_mm.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+
+=========
+Active MM
+=========
+
+这是一封linux之父回复开发者的一封邮件,所以翻译时我尽量保持邮件格式的完整。
+
+::
+
+ List:       linux-kernel
+ Subject:    Re: active_mm
+ From:       Linus Torvalds <torvalds () transmeta ! com>
+ Date:       1999-07-30 21:36:24
+
+ 因为我并不经常写解释,所以已经抄送到linux-kernel邮件列表,而当我做这些,
+ 且更多的人在阅读它们时,我觉得棒极了。
+
+ 1999年7月30日 星期五, David Mosberger 写道:
+ >
+ > 是否有一个简短的描述,说明task_struct中的
+ >  "mm" 和 "active_mm"应该如何使用? (如果
+ > 这个问题在邮件列表中讨论过,我表示歉意--我刚
+ > 刚度假回来,有一段时间没能关注linux-kernel了)。
+
+ 基本上,新的设定是:
+
+  - 我们有“真实地址空间”和“匿名地址空间”。区别在于,匿名地址空间根本不关心用
+    户级页表,所以当我们做上下文切换到匿名地址空间时,我们只是让以前的地址空间
+    处于活动状态。
+
+    一个“匿名地址空间”的明显用途是任何不需要任何用户映射的线程--所有的内核线
+    程基本上都属于这一类,但即使是“真正的”线程也可以暂时说在一定时间内它们不
+    会对用户空间感兴趣,调度器不妨试着避免在切换VM状态上浪费时间。目前只有老
+    式的bdflush sync能做到这一点。
+
+  - “tsk->mm” 指向 “真实地址空间”。对于一个匿名进程来说,tsk->mm将是NULL,
+    其逻辑原因是匿名进程实际上根本就 “没有” 真正的地址空间。
+
+  - 然而,我们显然需要跟踪我们为这样的匿名用户“偷用”了哪个地址空间。为此,我们
+    有 “tsk->active_mm”,它显示了当前活动的地址空间是什么。
+
+    规则是,对于一个有真实地址空间的进程(即tsk->mm是 non-NULL),active_mm
+    显然必须与真实的mm相同。
+
+    对于一个匿名进程,tsk->mm == NULL,而tsk->active_mm是匿名进程运行时
+    “借用”的mm。当匿名进程被调度走时,借用的地址空间被返回并清除。
+
+ 为了支持所有这些,“struct mm_struct”现在有两个计数器:一个是 “mm_users”
+ 计数器,即有多少 “真正的地址空间用户”,另一个是 “mm_count”计数器,即 “lazy”
+ 用户(即匿名用户)的数量,如果有任何真正的用户,则加1。
+
+ 通常情况下,至少有一个真正的用户,但也可能是真正的用户在另一个CPU上退出,而
+ 一个lazy的用户仍在活动,所以你实际上得到的情况是,你有一个地址空间 **只**
+ 被lazy的用户使用。这通常是一个短暂的生命周期状态,因为一旦这个线程被安排给一
+ 个真正的线程,这个 “僵尸” mm就会被释放,因为 “mm_count”变成了零。
+
+ 另外,一个新的规则是,**没有人** 再把 “init_mm” 作为一个真正的MM了。
+ “init_mm”应该被认为只是一个 “没有其他上下文时的lazy上下文”,事实上,它主
+ 要是在启动时使用,当时还没有真正的VM被创建。因此,用来检查的代码
+
+   if (current->mm == &init_mm)
+
+ 一般来说,应该用
+
+   if (!current->mm)
+
+ 取代上面的写法(这更有意义--测试基本上是 “我们是否有一个用户环境”,并且通常
+ 由缺页异常处理程序和类似的东西来完成)。
+
+ 总之,我刚才在ftp.kernel.org上放了一个pre-patch-2.3.13-1,因为它稍微改
+ 变了接口以适配alpha(谁会想到呢,但alpha体系结构上下文切换代码实际上最终是
+ 最丑陋的之一--不像其他架构的MM和寄存器状态是分开的,alpha的PALcode将两者
+ 连接起来,你需要同时切换两者)。
+
+ (文档来源 http://marc.info/?l=linux-kernel&m=93337278602211&w=2)
diff --git a/Documentation/translations/zh_CN/mm/balance.rst b/Documentation/translations/zh_CN/mm/balance.rst
new file mode 100644 (file)
index 0000000..6fd7920
--- /dev/null
@@ -0,0 +1,81 @@
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/mm/balance.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+
+========
+内存平衡
+========
+
+2000年1月开始,作者:Kanoj Sarcar <kanoj@sgi.com>
+
+对于 !__GFP_HIGH 和 !__GFP_KSWAPD_RECLAIM 以及非 __GFP_IO 的分配,需要进行
+内存平衡。
+
+调用者避免回收的第一个原因是调用者由于持有自旋锁或处于中断环境中而无法睡眠。第二个
+原因可能是,调用者愿意在不产生页面回收开销的情况下分配失败。这可能发生在有0阶回退
+选项的机会主义高阶分配请求中。在这种情况下,调用者可能也希望避免唤醒kswapd。
+
+__GFP_IO分配请求是为了防止文件系统死锁。
+
+在没有非睡眠分配请求的情况下,做平衡似乎是有害的。页面回收可以被懒散地启动,也就是
+说,只有在需要的时候(也就是区域的空闲内存为0),而不是让它成为一个主动的过程。
+
+也就是说,内核应该尝试从直接映射池中满足对直接映射页的请求,而不是回退到dma池中,
+这样就可以保持dma池为dma请求(不管是不是原子的)所填充。类似的争论也适用于高内存
+和直接映射的页面。相反,如果有很多空闲的dma页,最好是通过从dma池中分配一个来满足
+常规的内存请求,而不是产生常规区域平衡的开销。
+
+在2.2中,只有当空闲页总数低于总内存的1/64时,才会启动内存平衡/页面回收。如果dma
+和常规内存的比例合适,即使dma区完全空了,也很可能不会进行平衡。2.2已经在不同内存
+大小的生产机器上运行,即使有这个问题存在,似乎也做得不错。在2.3中,由于HIGHMEM的
+存在,这个问题变得更加严重。
+
+在2.3中,区域平衡可以用两种方式之一来完成:根据区域的大小(可能是低级区域的大小),
+我们可以在初始化阶段决定在平衡任何区域时应该争取多少空闲页。好的方面是,在平衡的时
+候,我们不需要看低级区的大小,坏的方面是,我们可能会因为忽略低级区可能较低的使用率
+而做过于频繁的平衡。另外,只要对分配程序稍作修改,就有可能将memclass()宏简化为一
+个简单的等式。
+
+另一个可能的解决方案是,我们只在一个区 **和** 其所有低级区的空闲内存低于该区及其
+低级区总内存的1/64时进行平衡。这就解决了2.2的平衡问题,并尽可能地保持了与2.2行为
+的接近。另外,平衡算法在各种架构上的工作方式也是一样的,这些架构有不同数量和类型的
+内存区。如果我们想变得更花哨一点,我们可以在未来为不同区域的自由页面分配不同的权重。
+
+请注意,如果普通区的大小与dma区相比是巨大的,那么在决定是否平衡普通区的时候,考虑
+空闲的dma页就变得不那么重要了。那么第一个解决方案就变得更有吸引力。
+
+所附的补丁实现了第二个解决方案。它还 “修复”了两个问题:首先,在低内存条件下,kswapd
+被唤醒,就像2.2中的非睡眠分配。第二,HIGHMEM区也被平衡了,以便给replace_with_highmem()
+一个争取获得HIGHMEM页的机会,同时确保HIGHMEM分配不会落回普通区。这也确保了HIGHMEM
+页不会被泄露(例如,在一个HIGHMEM页在交换缓存中但没有被任何人使用的情况下)。
+
+kswapd还需要知道它应该平衡哪些区。kswapd主要是在无法进行平衡的情况下需要的,可能
+是因为所有的分配请求都来自中断上下文,而所有的进程上下文都在睡眠。对于2.3,
+kswapd并不真正需要平衡高内存区,因为中断上下文并不请求高内存页。kswapd看zone
+结构体中的zone_wake_kswapd字段来决定一个区是否需要平衡。
+
+如果从进程内存和shm中偷取页面可以减轻该页面节点中任何区的内存压力,而该区的内存压力
+已经低于其水位,则会进行偷取。
+
+watemark[WMARK_MIN/WMARK_LOW/WMARK_HIGH]/low_on_memory/zone_wake_kswapd:
+这些是每个区的字段,用于确定一个区何时需要平衡。当页面数低于水位[WMARK_MIN]时,
+hysteric 的字段low_on_memory被设置。这个字段会一直被设置,直到空闲页数变成水位
+[WMARK_HIGH]。当low_on_memory被设置时,页面分配请求将尝试释放该区域的一些页面(如果
+请求中设置了GFP_WAIT)。与此相反的是,决定唤醒kswapd以释放一些区的页。这个决定不是基于
+hysteresis 的,而是当空闲页的数量低于watermark[WMARK_LOW]时就会进行;在这种情况下,
+zone_wake_kswapd也被设置。
+
+
+我所听到的(超棒的)想法:
+
+1. 动态经历应该影响平衡:可以跟踪一个区的失败请求的数量,并反馈到平衡方案中(jalvo@mbay.net)。
+
+2. 实现一个类似于replace_with_highmem()的replace_with_regular(),以保留dma页面。
+   (lkd@tantalophile.demon.co.uk)
diff --git a/Documentation/translations/zh_CN/mm/damon/api.rst b/Documentation/translations/zh_CN/mm/damon/api.rst
new file mode 100644 (file)
index 0000000..5593a83
--- /dev/null
@@ -0,0 +1,32 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+:Original: Documentation/mm/damon/api.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+
+=======
+API参考
+=======
+
+内核空间的程序可以使用下面的API来使用DAMON的每个功能。你所需要做的就是引用 ``damon.h`` ,
+它位于源代码树的include/linux/。
+
+结构体
+======
+
+该API在以下内核代码中:
+
+include/linux/damon.h
+
+
+函数
+====
+
+该API在以下内核代码中:
+
+mm/damon/core.c
diff --git a/Documentation/translations/zh_CN/mm/damon/design.rst b/Documentation/translations/zh_CN/mm/damon/design.rst
new file mode 100644 (file)
index 0000000..16e3db3
--- /dev/null
@@ -0,0 +1,140 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+:Original: Documentation/mm/damon/design.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+
+====
+设计
+====
+
+可配置的层
+==========
+
+DAMON提供了数据访问监控功能,同时使其准确性和开销可控。基本的访问监控需要依赖于目标地址空间
+并为之优化的基元。另一方面,作为DAMON的核心,准确性和开销的权衡机制是在纯逻辑空间中。DAMON
+将这两部分分离在不同的层中,并定义了它的接口,以允许各种低层次的基元实现与核心逻辑的配置。
+
+由于这种分离的设计和可配置的接口,用户可以通过配置核心逻辑和适当的低级基元实现来扩展DAMON的
+任何地址空间。如果没有提供合适的,用户可以自己实现基元。
+
+例如,物理内存、虚拟内存、交换空间、那些特定的进程、NUMA节点、文件和支持的内存设备将被支持。
+另外,如果某些架构或设备支持特殊的优化访问检查基元,这些基元将很容易被配置。
+
+
+特定地址空间基元的参考实现
+==========================
+
+基本访问监测的低级基元被定义为两部分。:
+
+1. 确定地址空间的监测目标地址范围
+2. 目标空间中特定地址范围的访问检查。
+
+DAMON目前为物理和虚拟地址空间提供了基元的实现。下面两个小节描述了这些工作的方式。
+
+
+基于VMA的目标地址范围构造
+-------------------------
+
+这仅仅是针对虚拟地址空间基元的实现。对于物理地址空间,只是要求用户手动设置监控目标地址范围。
+
+在进程的超级巨大的虚拟地址空间中,只有小部分被映射到物理内存并被访问。因此,跟踪未映射的地
+址区域只是一种浪费。然而,由于DAMON可以使用自适应区域调整机制来处理一定程度的噪声,所以严
+格来说,跟踪每一个映射并不是必须的,但在某些情况下甚至会产生很高的开销。也就是说,监测目标
+内部过于巨大的未映射区域应该被移除,以不占用自适应机制的时间。
+
+出于这个原因,这个实现将复杂的映射转换为三个不同的区域,覆盖地址空间的每个映射区域。这三个
+区域之间的两个空隙是给定地址空间中两个最大的未映射区域。这两个最大的未映射区域是堆和最上面
+的mmap()区域之间的间隙,以及在大多数情况下最下面的mmap()区域和堆之间的间隙。因为这些间隙
+在通常的地址空间中是异常巨大的,排除这些间隙就足以做出合理的权衡。下面详细说明了这一点::
+
+    <heap>
+    <BIG UNMAPPED REGION 1>
+    <uppermost mmap()-ed region>
+    (small mmap()-ed regions and munmap()-ed regions)
+    <lowermost mmap()-ed region>
+    <BIG UNMAPPED REGION 2>
+    <stack>
+
+
+基于PTE访问位的访问检查
+-----------------------
+
+物理和虚拟地址空间的实现都使用PTE Accessed-bit进行基本访问检查。唯一的区别在于从地址中
+找到相关的PTE访问位的方式。虚拟地址的实现是为该地址的目标任务查找页表,而物理地址的实现则
+是查找与该地址有映射关系的每一个页表。通过这种方式,实现者找到并清除下一个采样目标地址的位,
+并检查该位是否在一个采样周期后再次设置。这可能会干扰其他使用访问位的内核子系统,即空闲页跟
+踪和回收逻辑。为了避免这种干扰,DAMON使其与空闲页面跟踪相互排斥,并使用 ``PG_idle`` 和
+``PG_young`` 页面标志来解决与回收逻辑的冲突,就像空闲页面跟踪那样。
+
+
+独立于地址空间的核心机制
+========================
+
+下面四个部分分别描述了DAMON的核心机制和五个监测属性,即 ``采样间隔`` 、 ``聚集间隔`` 、
+``更新间隔`` 、 ``最小区域数`` 和 ``最大区域数`` 。
+
+
+访问频率监测
+------------
+
+DAMON的输出显示了在给定的时间内哪些页面的访问频率是多少。访问频率的分辨率是通过设置
+``采样间隔`` 和 ``聚集间隔`` 来控制的。详细地说,DAMON检查每个 ``采样间隔`` 对每
+个页面的访问,并将结果汇总。换句话说,计算每个页面的访问次数。在每个 ``聚合间隔`` 过
+去后,DAMON调用先前由用户注册的回调函数,以便用户可以阅读聚合的结果,然后再清除这些结
+果。这可以用以下简单的伪代码来描述::
+
+    while monitoring_on:
+        for page in monitoring_target:
+            if accessed(page):
+                nr_accesses[page] += 1
+        if time() % aggregation_interval == 0:
+            for callback in user_registered_callbacks:
+                callback(monitoring_target, nr_accesses)
+            for page in monitoring_target:
+                nr_accesses[page] = 0
+        sleep(sampling interval)
+
+这种机制的监测开销将随着目标工作负载规模的增长而任意增加。
+
+
+基于区域的抽样调查
+------------------
+
+为了避免开销的无限制增加,DAMON将假定具有相同访问频率的相邻页面归入一个区域。只要保持
+这个假设(一个区域内的页面具有相同的访问频率),该区域内就只需要检查一个页面。因此,对
+于每个 ``采样间隔`` ,DAMON在每个区域中随机挑选一个页面,等待一个 ``采样间隔`` ,检
+查该页面是否同时被访问,如果被访问则增加该区域的访问频率。因此,监测开销是可以通过设置
+区域的数量来控制的。DAMON允许用户设置最小和最大的区域数量来进行权衡。
+
+然而,如果假设没有得到保证,这个方案就不能保持输出的质量。
+
+
+适应性区域调整
+--------------
+
+即使最初的监测目标区域被很好地构建以满足假设(同一区域内的页面具有相似的访问频率),数
+据访问模式也会被动态地改变。这将导致监测质量下降。为了尽可能地保持假设,DAMON根据每个
+区域的访问频率自适应地进行合并和拆分。
+
+对于每个 ``聚集区间`` ,它比较相邻区域的访问频率,如果频率差异较小,就合并这些区域。
+然后,在它报告并清除每个区域的聚合接入频率后,如果区域总数不超过用户指定的最大区域数,
+它将每个区域拆分为两个或三个区域。
+
+通过这种方式,DAMON提供了其最佳的质量和最小的开销,同时保持了用户为其权衡设定的界限。
+
+
+动态目标空间更新处理
+--------------------
+
+监测目标地址范围可以动态改变。例如,虚拟内存可以动态地被映射和解映射。物理内存可以被
+热插拔。
+
+由于在某些情况下变化可能相当频繁,DAMON允许监控操作检查动态变化,包括内存映射变化,
+并仅在用户指定的时间间隔( ``更新间隔`` )中的每个时间段,将其应用于监控操作相关的
+数据结构,如抽象的监控目标内存区。
\ No newline at end of file
diff --git a/Documentation/translations/zh_CN/mm/damon/faq.rst b/Documentation/translations/zh_CN/mm/damon/faq.rst
new file mode 100644 (file)
index 0000000..de4be41
--- /dev/null
@@ -0,0 +1,48 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+:Original: Documentation/mm/damon/faq.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+
+========
+常见问题
+========
+
+为什么是一个新的子系统,而不是扩展perf或其他用户空间工具?
+==========================================================
+
+首先,因为它需要尽可能的轻量级,以便可以在线使用,所以应该避免任何不必要的开销,如内核-用户
+空间的上下文切换成本。第二,DAMON的目标是被包括内核在内的其他程序所使用。因此,对特定工具
+(如perf)的依赖性是不可取的。这就是DAMON在内核空间实现的两个最大的原因。
+
+
+“闲置页面跟踪” 或 “perf mem” 可以替代DAMON吗?
+==============================================
+
+闲置页跟踪是物理地址空间访问检查的一个低层次的原始方法。“perf mem”也是类似的,尽管它可以
+使用采样来减少开销。另一方面,DAMON是一个更高层次的框架,用于监控各种地址空间。它专注于内
+存管理优化,并提供复杂的精度/开销处理机制。因此,“空闲页面跟踪” 和 “perf mem” 可以提供
+DAMON输出的一个子集,但不能替代DAMON。
+
+
+DAMON是否只支持虚拟内存?
+=========================
+
+不,DAMON的核心是独立于地址空间的。用户可以在DAMON核心上实现和配置特定地址空间的低级原始
+部分,包括监测目标区域的构造和实际的访问检查。通过这种方式,DAMON用户可以用任何访问检查技
+术来监测任何地址空间。
+
+尽管如此,DAMON默认为虚拟内存和物理内存提供了基于vma/rmap跟踪和PTE访问位检查的地址空间
+相关功能的实现,以供参考和方便使用。
+
+
+我可以简单地监测页面的粒度吗?
+==============================
+
+是的,你可以通过设置 ``min_nr_regions`` 属性高于工作集大小除以页面大小的值来实现。
+因为监视目标区域的大小被强制为 ``>=page size`` ,所以区域分割不会产生任何影响。
diff --git a/Documentation/translations/zh_CN/mm/damon/index.rst b/Documentation/translations/zh_CN/mm/damon/index.rst
new file mode 100644 (file)
index 0000000..b03bf30
--- /dev/null
@@ -0,0 +1,32 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+:Original: Documentation/mm/damon/index.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+
+==========================
+DAMON:数据访问监视器
+==========================
+
+DAMON是Linux内核的一个数据访问监控框架子系统。DAMON的核心机制使其成为
+(该核心机制详见(Documentation/translations/zh_CN/mm/damon/design.rst))
+
+ - *准确度* (监测输出对DRAM级别的内存管理足够有用;但可能不适合CPU Cache级别),
+ - *轻量级* (监控开销低到可以在线应用),以及
+ - *可扩展* (无论目标工作负载的大小,开销的上限值都在恒定范围内)。
+
+因此,利用这个框架,内核的内存管理机制可以做出高级决策。会导致高数据访问监控开销的实
+验性内存管理优化工作可以再次进行。同时,在用户空间,有一些特殊工作负载的用户可以编写
+个性化的应用程序,以便更好地了解和优化他们的工作负载和系统。
+
+.. toctree::
+   :maxdepth: 2
+
+   faq
+   design
+   api
diff --git a/Documentation/translations/zh_CN/mm/free_page_reporting.rst b/Documentation/translations/zh_CN/mm/free_page_reporting.rst
new file mode 100644 (file)
index 0000000..5bfd580
--- /dev/null
@@ -0,0 +1,38 @@
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/mm/free_page_reporting.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+==========
+空闲页报告
+==========
+
+空闲页报告是一个API,设备可以通过它来注册接收系统当前未使用的页面列表。这在虚拟
+化的情况下是很有用的,客户机能够使用这些数据来通知管理器它不再使用内存中的某些页
+面。
+
+对于驱动,通常是气球驱动要使用这个功能,它将分配和初始化一个page_reporting_dev_info
+结构体。它要填充的结构体中的字段是用于处理散点列表的 "report" 函数指针。它还必
+须保证每次调用该函数时能处理至少相当于PAGE_REPORTING_CAPACITY的散点列表条目。
+假设没有其他页面报告设备已经注册, 对page_reporting_register的调用将向报告框
+架注册页面报告接口。
+
+一旦注册,页面报告API将开始向驱动报告成批的页面。API将在接口被注册后2秒开始报告
+页面,并在任何足够高的页面被释放之后2秒继续报告。
+
+报告的页面将被存储在传递给报告函数的散列表中,最后一个条目的结束位被设置在条目
+nent-1中。 当页面被报告函数处理时,分配器将无法访问它们。一旦报告函数完成,这些
+页将被返回到它们所获得的自由区域。
+
+在移除使用空闲页报告的驱动之前,有必要调用page_reporting_unregister,以移除
+目前被空闲页报告使用的page_reporting_dev_info结构体。这样做将阻止进一步的报
+告通过该接口发出。如果另一个驱动或同一驱动被注册,它就有可能恢复前一个驱动在报告
+空闲页方面的工作。
+
+
+Alexander Duyck, 2019年12月04日
diff --git a/Documentation/translations/zh_CN/mm/frontswap.rst b/Documentation/translations/zh_CN/mm/frontswap.rst
new file mode 100644 (file)
index 0000000..4349753
--- /dev/null
@@ -0,0 +1,196 @@
+:Original: Documentation/mm/frontswap.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+=========
+Frontswap
+=========
+
+Frontswap为交换页提供了一个 “transcendent memory” 的接口。在一些环境中,由
+于交换页被保存在RAM(或类似RAM的设备)中,而不是交换磁盘,因此可以获得巨大的性能
+节省(提高)。
+
+.. _Transcendent memory in a nutshell: https://lwn.net/Articles/454795/
+
+Frontswap之所以这么命名,是因为它可以被认为是与swap设备的“back”存储相反。存
+储器被认为是一个同步并发安全的面向页面的“伪RAM设备”,符合transcendent memory
+(如Xen的“tmem”,或内核内压缩内存,又称“zcache”,或未来的类似RAM的设备)的要
+求;这个伪RAM设备不能被内核直接访问或寻址,其大小未知且可能随时间变化。驱动程序通过
+调用frontswap_register_ops将自己与frontswap链接起来,以适当地设置frontswap_ops
+的功能,它提供的功能必须符合某些策略,如下所示:
+
+一个 “init” 将设备准备好接收与指定的交换设备编号(又称“类型”)相关的frontswap
+交换页。一个 “store” 将把该页复制到transcendent memory,并与该页的类型和偏移
+量相关联。一个 “load” 将把该页,如果找到的话,从transcendent memory复制到内核
+内存,但不会从transcendent memory中删除该页。一个 “invalidate_page” 将从
+transcendent memory中删除该页,一个 “invalidate_area” 将删除所有与交换类型
+相关的页(例如,像swapoff)并通知 “device” 拒绝进一步存储该交换类型。
+
+一旦一个页面被成功存储,在该页面上的匹配加载通常会成功。因此,当内核发现自己处于需
+要交换页面的情况时,它首先尝试使用frontswap。如果存储的结果是成功的,那么数据就已
+经成功的保存到了transcendent memory中,并且避免了磁盘写入,如果后来再读回数据,
+也避免了磁盘读取。如果存储返回失败,transcendent memory已经拒绝了该数据,且该页
+可以像往常一样被写入交换空间。
+
+请注意,如果一个页面被存储,而该页面已经存在于transcendent memory中(一个 “重复”
+的存储),要么存储成功,数据被覆盖,要么存储失败,该页面被废止。这确保了旧的数据永远
+不会从frontswap中获得。
+
+如果配置正确,对frontswap的监控是通过 `/sys/kernel/debug/frontswap` 目录下的
+debugfs完成的。frontswap的有效性可以通过以下方式测量(在所有交换设备中):
+
+``failed_stores``
+       有多少次存储的尝试是失败的
+
+``loads``
+       尝试了多少次加载(应该全部成功)
+
+``succ_stores``
+       有多少次存储的尝试是成功的
+
+``invalidates``
+       尝试了多少次作废
+
+后台实现可以提供额外的指标。
+
+经常问到的问题
+==============
+
+* 价值在哪里?
+
+当一个工作负载开始交换时,性能就会下降。Frontswap通过提供一个干净的、动态的接口来
+读取和写入交换页到 “transcendent memory”,从而大大增加了许多这样的工作负载的性
+能,否则内核是无法直接寻址的。当数据被转换为不同的形式和大小(比如压缩)或者被秘密
+移动(对于一些类似RAM的设备来说,这可能对写平衡很有用)时,这个接口是理想的。交换
+页(和被驱逐的页面缓存页)是这种比RAM慢但比磁盘快得多的“伪RAM设备”的一大用途。
+
+Frontswap对内核的影响相当小,为各种系统配置中更动态、更灵活的RAM利用提供了巨大的
+灵活性:
+
+在单一内核的情况下,又称“zcache”,页面被压缩并存储在本地内存中,从而增加了可以安
+全保存在RAM中的匿名页面总数。Zcache本质上是用压缩/解压缩的CPU周期换取更好的内存利
+用率。Benchmarks测试显示,当内存压力较低时,几乎没有影响,而在高内存压力下的一些
+工作负载上,则有明显的性能改善(25%以上)。
+
+“RAMster” 在zcache的基础上增加了对集群系统的 “peer-to-peer” transcendent memory
+的支持。Frontswap页面像zcache一样被本地压缩,但随后被“remotified” 到另一个系
+统的RAM。这使得RAM可以根据需要动态地来回负载平衡,也就是说,当系统A超载时,它可以
+交换到系统B,反之亦然。RAMster也可以被配置成一个内存服务器,因此集群中的许多服务器
+可以根据需要动态地交换到配置有大量内存的单一服务器上......而不需要预先配置每个客户
+有多少内存可用
+
+在虚拟情况下,虚拟化的全部意义在于统计地将物理资源在多个虚拟机的不同需求之间进行复
+用。对于RAM来说,这真的很难做到,而且在不改变内核的情况下,要做好这一点的努力基本上
+是失败的(除了一些广为人知的特殊情况下的工作负载)。具体来说,Xen Transcendent Memory
+后端允许管理器拥有的RAM “fallow”,不仅可以在多个虚拟机之间进行“time-shared”,
+而且页面可以被压缩和重复利用,以优化RAM的利用率。当客户操作系统被诱导交出未充分利用
+的RAM时(如 “selfballooning”),突然出现的意外内存压力可能会导致交换;frontswap
+允许这些页面被交换到管理器RAM中或从管理器RAM中交换(如果整体主机系统内存条件允许),
+从而减轻计划外交换可能带来的可怕的性能影响。
+
+一个KVM的实现正在进行中,并且已经被RFC'ed到lkml。而且,利用frontswap,对NVM作为
+内存扩展技术的调查也在进行中。
+
+* 当然,在某些情况下可能有性能上的优势,但frontswap的空间/时间开销是多少?
+
+如果 CONFIG_FRONTSWAP 被禁用,每个 frontswap 钩子都会编译成空,唯一的开销是每
+个 swapon'ed swap 设备的几个额外字节。如果 CONFIG_FRONTSWAP 被启用,但没有
+frontswap的 “backend” 寄存器,每读或写一个交换页就会有一个额外的全局变量,而不
+是零。如果 CONFIG_FRONTSWAP 被启用,并且有一个frontswap的backend寄存器,并且
+后端每次 “store” 请求都失败(即尽管声称可能,但没有提供内存),CPU 的开销仍然可以
+忽略不计 - 因为每次frontswap失败都是在交换页写到磁盘之前,系统很可能是 I/O 绑定
+的,无论如何使用一小部分的 CPU 都是不相关的。
+
+至于空间,如果CONFIG_FRONTSWAP被启用,并且有一个frontswap的backend注册,那么
+每个交换设备的每个交换页都会被分配一个比特。这是在内核已经为每个交换设备的每个交换
+页分配的8位(在2.6.34之前是16位)上增加的。(Hugh Dickins观察到,frontswap可能
+会偷取现有的8个比特,但是我们以后再来担心这个小的优化问题)。对于标准的4K页面大小的
+非常大的交换盘(这很罕见),这是每32GB交换盘1MB开销。
+
+当交换页存储在transcendent memory中而不是写到磁盘上时,有一个副作用,即这可能会
+产生更多的内存压力,有可能超过其他的优点。一个backend,比如zcache,必须实现策略
+来仔细(但动态地)管理内存限制,以确保这种情况不会发生。
+
+* 好吧,那就用内核骇客能理解的术语来快速概述一下这个frontswap补丁的作用如何?
+
+我们假设在内核初始化过程中,一个frontswap 的 “backend” 已经注册了;这个注册表
+明这个frontswap 的 “backend” 可以访问一些不被内核直接访问的“内存”。它到底提
+供了多少内存是完全动态和随机的。
+
+每当一个交换设备被交换时,就会调用frontswap_init(),把交换设备的编号(又称“类
+型”)作为一个参数传给它。这就通知了frontswap,以期待 “store” 与该号码相关的交
+换页的尝试。
+
+每当交换子系统准备将一个页面写入交换设备时(参见swap_writepage()),就会调用
+frontswap_store。Frontswap与frontswap backend协商,如果backend说它没有空
+间,frontswap_store返回-1,内核就会照常把页换到交换设备上。注意,来自frontswap
+backend的响应对内核来说是不可预测的;它可能选择从不接受一个页面,可能接受每九个
+页面,也可能接受每一个页面。但是如果backend确实接受了一个页面,那么这个页面的数
+据已经被复制并与类型和偏移量相关联了,而且backend保证了数据的持久性。在这种情况
+下,frontswap在交换设备的“frontswap_map” 中设置了一个位,对应于交换设备上的
+页面偏移量,否则它就会将数据写入该设备。
+
+当交换子系统需要交换一个页面时(swap_readpage()),它首先调用frontswap_load(),
+检查frontswap_map,看这个页面是否早先被frontswap backend接受。如果是,该页
+的数据就会从frontswap后端填充,换入就完成了。如果不是,正常的交换代码将被执行,
+以便从真正的交换设备上获得这一页的数据。
+
+所以每次frontswap backend接受一个页面时,交换设备的读取和(可能)交换设备的写
+入都被 “frontswap backend store” 和(可能)“frontswap backend loads”
+所取代,这可能会快得多。
+
+* frontswap不能被配置为一个 “特殊的” 交换设备,它的优先级要高于任何真正的交换
+  设备(例如像zswap,或者可能是swap-over-nbd/NFS)?
+
+首先,现有的交换子系统不允许有任何种类的交换层次结构。也许它可以被重写以适应层次
+结构,但这将需要相当大的改变。即使它被重写,现有的交换子系统也使用了块I/O层,它
+假定交换设备是固定大小的,其中的任何页面都是可线性寻址的。Frontswap几乎没有触
+及现有的交换子系统,而是围绕着块I/O子系统的限制,提供了大量的灵活性和动态性。
+
+例如,frontswap backend对任何交换页的接受是完全不可预测的。这对frontswap backend
+的定义至关重要,因为它赋予了backend完全动态的决定权。在zcache中,人们无法预
+先知道一个页面的可压缩性如何。可压缩性 “差” 的页面会被拒绝,而 “差” 本身也可
+以根据当前的内存限制动态地定义。
+
+此外,frontswap是完全同步的,而真正的交换设备,根据定义,是异步的,并且使用
+块I/O。块I/O层不仅是不必要的,而且可能进行 “优化”,这对面向RAM的设备来说是
+不合适的,包括将一些页面的写入延迟相当长的时间。同步是必须的,以确保后端的动
+态性,并避免棘手的竞争条件,这将不必要地大大增加frontswap和/或块I/O子系统的
+复杂性。也就是说,只有最初的 “store” 和 “load” 操作是需要同步的。一个独立
+的异步线程可以自由地操作由frontswap存储的页面。例如,RAMster中的 “remotification”
+线程使用标准的异步内核套接字,将压缩的frontswap页面移动到远程机器。同样,
+KVM的客户方实现可以进行客户内压缩,并使用 “batched” hypercalls。
+
+在虚拟化环境中,动态性允许管理程序(或主机操作系统)做“intelligent overcommit”。
+例如,它可以选择只接受页面,直到主机交换可能即将发生,然后强迫客户机做他们
+自己的交换。
+
+transcendent memory规格的frontswap有一个坏处。因为任何 “store” 都可
+能失败,所以必须在一个真正的交换设备上有一个真正的插槽来交换页面。因此,
+frontswap必须作为每个交换设备的 “影子” 来实现,它有可能容纳交换设备可能
+容纳的每一个页面,也有可能根本不容纳任何页面。这意味着frontswap不能包含比
+swap设备总数更多的页面。例如,如果在某些安装上没有配置交换设备,frontswap
+就没有用。无交换设备的便携式设备仍然可以使用frontswap,但是这种设备的
+backend必须配置某种 “ghost” 交换设备,并确保它永远不会被使用。
+
+
+* 为什么会有这种关于 “重复存储” 的奇怪定义?如果一个页面以前被成功地存储过,
+  难道它不能总是被成功地覆盖吗?
+
+几乎总是可以的,不,有时不能。考虑一个例子,数据被压缩了,原来的4K页面被压
+缩到了1K。现在,有人试图用不可压缩的数据覆盖该页,因此会占用整个4K。但是
+backend没有更多的空间了。在这种情况下,这个存储必须被拒绝。每当frontswap
+拒绝一个会覆盖的存储时,它也必须使旧的数据作废,并确保它不再被访问。因为交
+换子系统会把新的数据写到读交换设备上,这是确保一致性的正确做法。
+
+* 为什么frontswap补丁会创建新的头文件swapfile.h?
+
+frontswap代码依赖于一些swap子系统内部的数据结构,这些数据结构多年来一直
+在静态和全局之间来回移动。这似乎是一个合理的妥协:将它们定义为全局,但在一
+个新的包含文件中声明它们,该文件不被包含swap.h的大量源文件所包含。
+
+Dan Magenheimer,最后更新于2012年4月9日
diff --git a/Documentation/translations/zh_CN/mm/highmem.rst b/Documentation/translations/zh_CN/mm/highmem.rst
new file mode 100644 (file)
index 0000000..f74800a
--- /dev/null
@@ -0,0 +1,137 @@
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/mm/highmem.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+==========
+高内存处理
+==========
+
+作者: Peter Zijlstra <a.p.zijlstra@chello.nl>
+
+.. contents:: :local:
+
+高内存是什么?
+==============
+
+当物理内存的大小接近或超过虚拟内存的最大大小时,就会使用高内存(highmem)。在这一点上,内
+核不可能在任何时候都保持所有可用的物理内存的映射。这意味着内核需要开始使用它想访问的物理内
+存的临时映射。
+
+没有被永久映射覆盖的那部分(物理)内存就是我们所说的 "高内存"。对于这个边界的确切位置,有
+各种架构上的限制。
+
+例如,在i386架构中,我们选择将内核映射到每个进程的虚拟空间,这样我们就不必为内核的进入/退
+出付出全部的TLB作废代价。这意味着可用的虚拟内存空间(i386上为4GiB)必须在用户和内核空间之
+间进行划分。
+
+使用这种方法的架构的传统分配方式是3:1,3GiB用于用户空间,顶部的1GiB用于内核空间。::
+
+               +--------+ 0xffffffff
+               | Kernel |
+               +--------+ 0xc0000000
+               |        |
+               | User   |
+               |        |
+               +--------+ 0x00000000
+
+这意味着内核在任何时候最多可以映射1GiB的物理内存,但是由于我们需要虚拟地址空间来做其他事
+情--包括访问其余物理内存的临时映射--实际的直接映射通常会更少(通常在~896MiB左右)。
+
+其他有mm上下文标签的TLB的架构可以有独立的内核和用户映射。然而,一些硬件(如一些ARM)在使
+用mm上下文标签时,其虚拟空间有限。
+
+
+临时虚拟映射
+============
+
+内核包含几种创建临时映射的方法。下面的列表按照使用的优先顺序显示了它们。
+
+* kmap_local_page()。这个函数是用来要求短期映射的。它可以从任何上下文(包括中断)中调用,
+  但是映射只能在获取它们的上下文中使用。
+
+  在可行的情况下,这个函数应该比其他所有的函数优先使用。
+
+  这些映射是线程本地和CPU本地的,这意味着映射只能从这个线程中访问,并且当映射处于活动状
+  态时,该线程与CPU绑定。即使线程被抢占了(因为抢占永远不会被函数禁用),CPU也不能通过
+  CPU-hotplug从系统中拔出,直到映射被处理掉。
+
+  在本地的kmap区域中采取pagefaults是有效的,除非获取本地映射的上下文由于其他原因不允许
+  这样做。
+
+  kmap_local_page()总是返回一个有效的虚拟地址,并且假定kunmap_local()不会失败。
+
+  嵌套kmap_local_page()和kmap_atomic()映射在一定程度上是允许的(最多到KMAP_TYPE_NR),
+  但是它们的调用必须严格排序,因为映射的实现是基于堆栈的。关于如何管理嵌套映射的细节,
+  请参见kmap_local_page() kdocs(包含在 "函数 "部分)。
+
+* kmap_atomic().  这允许对单个页面进行非常短的时间映射。由于映射被限制在发布它的CPU上,
+  它表现得很好,但发布的任务因此被要求留在该CPU上直到它完成,以免其他任务取代它的映射。
+
+  kmap_atomic()也可以被中断上下文使用,因为它不睡眠,调用者也可能在调用kunmap_atomic()
+  后才睡眠。
+
+  内核中对kmap_atomic()的每次调用都会创建一个不可抢占的段,并禁用缺页异常。这可能是
+  未预期延迟的来源之一。因此用户应该选择kmap_local_page()而不是kmap_atomic()。
+
+  假设k[un]map_atomic()不会失败。
+
+* kmap()。这应该被用来对单个页面进行短时间的映射,对抢占或迁移没有限制。它会带来开销,
+  因为映射空间是受限制的,并且受到全局锁的保护,以实现同步。当不再需要映射时,必须用
+  kunmap()释放该页被映射的地址。
+
+  映射变化必须广播到所有CPU(核)上,kmap()还需要在kmap的池被回绕(TLB项用光了,需要从第
+   一项复用)时进行全局TLB无效化,当映射空间被完全利用时,它可能会阻塞,直到有一个可用的
+   槽出现。因此,kmap()只能从可抢占的上下文中调用。
+
+  如果一个映射必须持续相对较长的时间,上述所有的工作都是必要的,但是内核中大部分的
+  高内存映射都是短暂的,而且只在一个地方使用。这意味着在这种情况下,kmap()的成本大
+  多被浪费了。kmap()并不是为长期映射而设计的,但是它已经朝着这个方向发展了,在较新
+  的代码中强烈不鼓励使用它,前面的函数集应该是首选。
+
+  在64位系统中,调用kmap_local_page()、kmap_atomic()和kmap()没有实际作用,因为64位
+  地址空间足以永久映射所有物理内存页面。
+
+* vmap()。这可以用来将多个物理页长期映射到一个连续的虚拟空间。它需要全局同步来解除
+  映射。
+
+临时映射的成本
+==============
+
+创建临时映射的代价可能相当高。体系架构必须操作内核的页表、数据TLB和/或MMU的寄存器。
+
+如果CONFIG_HIGHMEM没有被设置,那么内核会尝试用一点计算来创建映射,将页面结构地址转换成
+指向页面内容的指针,而不是去捣鼓映射。在这种情况下,解映射操作可能是一个空操作。
+
+如果CONFIG_MMU没有被设置,那么就不可能有临时映射和高内存。在这种情况下,也将使用计算方法。
+
+
+i386 PAE
+========
+
+在某些情况下,i386 架构将允许你在 32 位机器上安装多达 64GiB 的内存。但这有一些后果:
+
+* Linux需要为系统中的每个页面建立一个页帧结构,而且页帧需要驻在永久映射中,这意味着:
+
+* 你最多可以有896M/sizeof(struct page)页帧;由于页结构体是32字节的,所以最终会有
+  112G的页;然而,内核需要在内存中存储更多的页帧......
+
+* PAE使你的页表变大--这使系统变慢,因为更多的数据需要在TLB填充等方面被访问。一个好处
+  是,PAE有更多的PTE位,可以提供像NX和PAT这样的高级功能。
+
+一般的建议是,你不要在32位机器上使用超过8GiB的空间--尽管更多的空间可能对你和你的工作
+量有用,但你几乎是靠你自己--不要指望内核开发者真的会很关心事情的进展情况。
+
+函数
+====
+
+该API在以下内核代码中:
+
+include/linux/highmem.h
+
+include/linux/highmem-internal.h
diff --git a/Documentation/translations/zh_CN/mm/hmm.rst b/Documentation/translations/zh_CN/mm/hmm.rst
new file mode 100644 (file)
index 0000000..5024a8a
--- /dev/null
@@ -0,0 +1,361 @@
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/mm/hmm.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+==================
+异构内存管理 (HMM)
+==================
+
+提供基础设施和帮助程序以将非常规内存(设备内存,如板上 GPU 内存)集成到常规内核路径中,其
+基石是此类内存的专用struct page(请参阅本文档的第 5 至 7 节)。
+
+HMM 还为 SVM(共享虚拟内存)提供了可选的帮助程序,即允许设备透明地访问与 CPU 一致的程序
+地址,这意味着 CPU 上的任何有效指针也是该设备的有效指针。这对于简化高级异构计算的使用变得
+必不可少,其中 GPU、DSP 或 FPGA 用于代表进程执行各种计算。
+
+本文档分为以下部分:在第一部分中,我揭示了与使用特定于设备的内存分配器相关的问题。在第二
+部分中,我揭示了许多平台固有的硬件限制。第三部分概述了 HMM 设计。第四部分解释了 CPU 页
+表镜像的工作原理以及 HMM 在这种情况下的目的。第五部分处理内核中如何表示设备内存。最后,
+最后一节介绍了一个新的迁移助手,它允许利用设备 DMA 引擎。
+
+.. contents:: :local:
+
+使用特定于设备的内存分配器的问题
+================================
+
+具有大量板载内存(几 GB)的设备(如 GPU)历来通过专用驱动程序特定 API 管理其内存。这会
+造成设备驱动程序分配和管理的内存与常规应用程序内存(私有匿名、共享内存或常规文件支持内存)
+之间的隔断。从这里开始,我将把这个方面称为分割的地址空间。我使用共享地址空间来指代相反的情况:
+即,设备可以透明地使用任何应用程序内存区域。
+
+分割的地址空间的发生是因为设备只能访问通过设备特定 API 分配的内存。这意味着从设备的角度来
+看,程序中的所有内存对象并不平等,这使得依赖于广泛的库的大型程序变得复杂。
+
+具体来说,这意味着想要利用像 GPU 这样的设备的代码需要在通用分配的内存(malloc、mmap
+私有、mmap 共享)和通过设备驱动程序 API 分配的内存之间复制对象(这仍然以 mmap 结束,
+但是是设备文件)。
+
+对于平面数据集(数组、网格、图像……),这并不难实现,但对于复杂数据集(列表、树……),
+很难做到正确。复制一个复杂的数据集需要重新映射其每个元素之间的所有指针关系。这很容易出错,
+而且由于数据集和地址的重复,程序更难调试。
+
+分割地址空间也意味着库不能透明地使用它们从核心程序或另一个库中获得的数据,因此每个库可能
+不得不使用设备特定的内存分配器来重复其输入数据集。大型项目会因此受到影响,并因为各种内存
+拷贝而浪费资源。
+
+复制每个库的API以接受每个设备特定分配器分配的内存作为输入或输出,并不是一个可行的选择。
+这将导致库入口点的组合爆炸。
+
+最后,随着高级语言结构(在 C++ 中,当然也在其他语言中)的进步,编译器现在有可能在没有程
+序员干预的情况下利用 GPU 和其他设备。某些编译器识别的模式仅适用于共享地址空间。对所有
+其他模式,使用共享地址空间也更合理。
+
+
+I/O 总线、设备内存特性
+======================
+
+由于一些限制,I/O 总线削弱了共享地址空间。大多数 I/O 总线只允许从设备到主内存的基本
+内存访问;甚至缓存一致性通常是可选的。从 CPU 访问设备内存甚至更加有限。通常情况下,它
+不是缓存一致的。
+
+如果我们只考虑 PCIE 总线,那么设备可以访问主内存(通常通过 IOMMU)并与 CPU 缓存一
+致。但是,它只允许设备对主存储器进行一组有限的原子操作。这在另一个方向上更糟:CPU
+只能访问有限范围的设备内存,而不能对其执行原子操作。因此,从内核的角度来看,设备内存不
+能被视为与常规内存等同。
+
+另一个严重的因素是带宽有限(约 32GBytes/s,PCIE 4.0 和 16 通道)。这比最快的 GPU
+内存 (1 TBytes/s) 慢 33 倍。最后一个限制是延迟。从设备访问主内存的延迟比设备访问自
+己的内存时高一个数量级。
+
+一些平台正在开发新的 I/O 总线或对 PCIE 的添加/修改以解决其中一些限制
+(OpenCAPI、CCIX)。它们主要允许 CPU 和设备之间的双向缓存一致性,并允许架构支持的所
+有原子操作。遗憾的是,并非所有平台都遵循这一趋势,并且一些主要架构没有针对这些问题的硬
+件解决方案。
+
+因此,为了使共享地址空间有意义,我们不仅必须允许设备访问任何内存,而且还必须允许任何内
+存在设备使用时迁移到设备内存(在迁移时阻止 CPU 访问)。
+
+
+共享地址空间和迁移
+==================
+
+HMM 打算提供两个主要功能。第一个是通过复制cpu页表到设备页表中来共享地址空间,因此对
+于进程地址空间中的任何有效主内存地址,相同的地址指向相同的物理内存。
+
+为了实现这一点,HMM 提供了一组帮助程序来填充设备页表,同时跟踪 CPU 页表更新。设备页表
+更新不像 CPU 页表更新那么容易。要更新设备页表,您必须分配一个缓冲区(或使用预先分配的
+缓冲区池)并在其中写入 GPU 特定命令以执行更新(取消映射、缓存失效和刷新等)。这不能通
+过所有设备的通用代码来完成。因此,为什么HMM提供了帮助器,在把硬件的具体细节留给设备驱
+动程序的同时,把一切可以考虑的因素都考虑进去了。
+
+HMM 提供的第二种机制是一种新的 ZONE_DEVICE 内存,它允许为设备内存的每个页面分配一个
+struct page。这些页面很特殊,因为 CPU 无法映射它们。然而,它们允许使用现有的迁移机
+制将主内存迁移到设备内存,从 CPU 的角度来看,一切看起来都像是换出到磁盘的页面。使用
+struct page可以与现有的 mm 机制进行最简单、最干净的集成。再次,HMM 仅提供帮助程序,
+首先为设备内存热插拔新的 ZONE_DEVICE 内存,然后执行迁移。迁移内容和时间的策略决定留
+给设备驱动程序。
+
+请注意,任何 CPU 对设备页面的访问都会触发缺页异常并迁移回主内存。例如,当支持给定CPU
+地址 A 的页面从主内存页面迁移到设备页面时,对地址 A 的任何 CPU 访问都会触发缺页异常
+并启动向主内存的迁移。
+
+凭借这两个特性,HMM 不仅允许设备镜像进程地址空间并保持 CPU 和设备页表同步,而且还通
+过迁移设备正在使用的数据集部分来利用设备内存。
+
+
+地址空间镜像实现和API
+=====================
+
+地址空间镜像的主要目标是允许将一定范围的 CPU 页表复制到一个设备页表中;HMM 有助于
+保持两者同步。想要镜像进程地址空间的设备驱动程序必须从注册 mmu_interval_notifier
+开始::
+
+ int mmu_interval_notifier_insert(struct mmu_interval_notifier *interval_sub,
+                                 struct mm_struct *mm, unsigned long start,
+                                 unsigned long length,
+                                 const struct mmu_interval_notifier_ops *ops);
+
+在 ops->invalidate() 回调期间,设备驱动程序必须对范围执行更新操作(将范围标记为只
+读,或完全取消映射等)。设备必须在驱动程序回调返回之前完成更新。
+
+当设备驱动程序想要填充一个虚拟地址范围时,它可以使用::
+
+  int hmm_range_fault(struct hmm_range *range);
+
+如果请求写访问,它将在丢失或只读条目上触发缺页异常(见下文)。缺页异常使用通用的 mm 缺
+页异常代码路径,就像 CPU 缺页异常一样。
+
+这两个函数都将 CPU 页表条目复制到它们的 pfns 数组参数中。该数组中的每个条目对应于虚拟
+范围中的一个地址。HMM 提供了一组标志来帮助驱动程序识别特殊的 CPU 页表项。
+
+在 sync_cpu_device_pagetables() 回调中锁定是驱动程序必须尊重的最重要的方面,以保
+持事物正确同步。使用模式是::
+
+ int driver_populate_range(...)
+ {
+      struct hmm_range range;
+      ...
+
+      range.notifier = &interval_sub;
+      range.start = ...;
+      range.end = ...;
+      range.hmm_pfns = ...;
+
+      if (!mmget_not_zero(interval_sub->notifier.mm))
+          return -EFAULT;
+
+ again:
+      range.notifier_seq = mmu_interval_read_begin(&interval_sub);
+      mmap_read_lock(mm);
+      ret = hmm_range_fault(&range);
+      if (ret) {
+          mmap_read_unlock(mm);
+          if (ret == -EBUSY)
+                 goto again;
+          return ret;
+      }
+      mmap_read_unlock(mm);
+
+      take_lock(driver->update);
+      if (mmu_interval_read_retry(&ni, range.notifier_seq) {
+          release_lock(driver->update);
+          goto again;
+      }
+
+      /* Use pfns array content to update device page table,
+       * under the update lock */
+
+      release_lock(driver->update);
+      return 0;
+ }
+
+driver->update 锁与驱动程序在其 invalidate() 回调中使用的锁相同。该锁必须在调用
+mmu_interval_read_retry() 之前保持,以避免与并发 CPU 页表更新发生任何竞争。
+
+利用 default_flags 和 pfn_flags_mask
+====================================
+
+hmm_range 结构有 2 个字段,default_flags 和 pfn_flags_mask,它们指定整个范围
+的故障或快照策略,而不必为 pfns 数组中的每个条目设置它们。
+
+例如,如果设备驱动程序需要至少具有读取权限的范围的页面,它会设置::
+
+    range->default_flags = HMM_PFN_REQ_FAULT;
+    range->pfn_flags_mask = 0;
+
+并如上所述调用 hmm_range_fault()。这将填充至少具有读取权限的范围内的所有页面。
+
+现在假设驱动程序想要做同样的事情,除了它想要拥有写权限的范围内的一页。现在驱动程序设
+置::
+
+    range->default_flags = HMM_PFN_REQ_FAULT;
+    range->pfn_flags_mask = HMM_PFN_REQ_WRITE;
+    range->pfns[index_of_write] = HMM_PFN_REQ_WRITE;
+
+有了这个,HMM 将在至少读取(即有效)的所有页面中异常,并且对于地址
+== range->start + (index_of_write << PAGE_SHIFT) 它将异常写入权限,即,如果
+CPU pte 没有设置写权限,那么HMM将调用handle_mm_fault()。
+
+hmm_range_fault 完成后,标志位被设置为页表的当前状态,即 HMM_PFN_VALID | 如果页
+面可写,将设置 HMM_PFN_WRITE。
+
+
+从核心内核的角度表示和管理设备内存
+==================================
+
+尝试了几种不同的设计来支持设备内存。第一个使用特定于设备的数据结构来保存有关迁移内存
+的信息,HMM 将自身挂接到 mm 代码的各个位置,以处理对设备内存支持的地址的任何访问。
+事实证明,这最终复制了 struct page 的大部分字段,并且还需要更新许多内核代码路径才
+能理解这种新的内存类型。
+
+大多数内核代码路径从不尝试访问页面后面的内存,而只关心struct page的内容。正因为如此,
+HMM 切换到直接使用 struct page 用于设备内存,这使得大多数内核代码路径不知道差异。
+我们只需要确保没有人试图从 CPU 端映射这些页面。
+
+移入和移出设备内存
+==================
+
+由于 CPU 无法直接访问设备内存,因此设备驱动程序必须使用硬件 DMA 或设备特定的加载/存
+储指令来迁移数据。migrate_vma_setup()、migrate_vma_pages() 和
+migrate_vma_finalize() 函数旨在使驱动程序更易于编写并集中跨驱动程序的通用代码。
+
+在将页面迁移到设备私有内存之前,需要创建特殊的设备私有 ``struct page`` 。这些将用
+作特殊的“交换”页表条目,以便 CPU 进程在尝试访问已迁移到设备专用内存的页面时会发生异常。
+
+这些可以通过以下方式分配和释放::
+
+    struct resource *res;
+    struct dev_pagemap pagemap;
+
+    res = request_free_mem_region(&iomem_resource, /* number of bytes */,
+                                  "name of driver resource");
+    pagemap.type = MEMORY_DEVICE_PRIVATE;
+    pagemap.range.start = res->start;
+    pagemap.range.end = res->end;
+    pagemap.nr_range = 1;
+    pagemap.ops = &device_devmem_ops;
+    memremap_pages(&pagemap, numa_node_id());
+
+    memunmap_pages(&pagemap);
+    release_mem_region(pagemap.range.start, range_len(&pagemap.range));
+
+还有devm_request_free_mem_region(), devm_memremap_pages(),
+devm_memunmap_pages() 和 devm_release_mem_region() 当资源可以绑定到 ``struct device``.
+
+整体迁移步骤类似于在系统内存中迁移 NUMA 页面(see :ref:`Page migration <page_migration>`) ,
+但这些步骤分为设备驱动程序特定代码和共享公共代码:
+
+1. ``mmap_read_lock()``
+
+   设备驱动程序必须将 ``struct vm_area_struct`` 传递给migrate_vma_setup(),
+   因此需要在迁移期间保留 mmap_read_lock() 或 mmap_write_lock()。
+
+2. ``migrate_vma_setup(struct migrate_vma *args)``
+
+   设备驱动初始化了 ``struct migrate_vma`` 的字段,并将该指针传递给
+   migrate_vma_setup()。``args->flags`` 字段是用来过滤哪些源页面应该被迁移。
+   例如,设置 ``MIGRATE_VMA_SELECT_SYSTEM`` 将只迁移系统内存,设置
+   ``MIGRATE_VMA_SELECT_DEVICE_PRIVATE`` 将只迁移驻留在设备私有内存中的页
+   面。如果后者被设置, ``args->pgmap_owner`` 字段被用来识别驱动所拥有的设备
+   私有页。这就避免了试图迁移驻留在其他设备中的设备私有页。目前,只有匿名的私有VMA
+   范围可以被迁移到系统内存和设备私有内存。
+
+   migrate_vma_setup()所做的第一步是用 ``mmu_notifier_invalidate_range_start()``
+   和 ``mmu_notifier_invalidate_range_end()`` 调用来遍历设备周围的页表,使
+   其他设备的MMU无效,以便在 ``args->src`` 数组中填写要迁移的PFN。
+   ``invalidate_range_start()`` 回调传递给一个``struct mmu_notifier_range`` ,
+   其 ``event`` 字段设置为MMU_NOTIFY_MIGRATE, ``owner`` 字段设置为传递给
+   migrate_vma_setup()的 ``args->pgmap_owner`` 字段。这允许设备驱动跳过无
+   效化回调,只无效化那些实际正在迁移的设备私有MMU映射。这一点将在下一节详细解释。
+
+
+   在遍历页表时,一个 ``pte_none()`` 或 ``is_zero_pfn()`` 条目导致一个有效
+   的  “zero” PFN 存储在 ``args->src`` 阵列中。这让驱动分配设备私有内存并清
+   除它,而不是复制一个零页。到系统内存或设备私有结构页的有效PTE条目将被
+   ``lock_page()``锁定,与LRU隔离(如果系统内存和设备私有页不在LRU上),从进
+   程中取消映射,并插入一个特殊的迁移PTE来代替原来的PTE。 migrate_vma_setup()
+   还清除了 ``args->dst`` 数组。
+
+3. 设备驱动程序分配目标页面并将源页面复制到目标页面。
+
+   驱动程序检查每个 ``src`` 条目以查看该 ``MIGRATE_PFN_MIGRATE`` 位是否已
+   设置并跳过未迁移的条目。设备驱动程序还可以通过不填充页面的 ``dst`` 数组来选
+   择跳过页面迁移。
+
+   然后,驱动程序分配一个设备私有 struct page 或一个系统内存页,用 ``lock_page()``
+   锁定该页,并将 ``dst`` 数组条目填入::
+
+     dst[i] = migrate_pfn(page_to_pfn(dpage));
+
+   现在驱动程序知道这个页面正在被迁移,它可以使设备私有 MMU 映射无效并将设备私有
+   内存复制到系统内存或另一个设备私有页面。由于核心 Linux 内核会处理 CPU 页表失
+   效,因此设备驱动程序只需使其自己的 MMU 映射失效。
+
+   驱动程序可以使用 ``migrate_pfn_to_page(src[i])`` 来获取源设备的
+   ``struct page`` 面,并将源页面复制到目标设备上,如果指针为 ``NULL`` ,意
+   味着源页面没有被填充到系统内存中,则清除目标设备的私有内存。
+
+4. ``migrate_vma_pages()``
+
+   这一步是实际“提交”迁移的地方。
+
+   如果源页是 ``pte_none()`` 或 ``is_zero_pfn()`` 页,这时新分配的页会被插
+   入到CPU的页表中。如果一个CPU线程在同一页面上发生异常,这可能会失败。然而,页
+   表被锁定,只有一个新页会被插入。如果它失去了竞争,设备驱动将看到
+   ``MIGRATE_PFN_MIGRATE`` 位被清除。
+
+   如果源页被锁定、隔离等,源 ``struct page`` 信息现在被复制到目标
+   ``struct page`` ,最终完成CPU端的迁移。
+
+5. 设备驱动为仍在迁移的页面更新设备MMU页表,回滚未迁移的页面。
+
+   如果 ``src`` 条目仍然有 ``MIGRATE_PFN_MIGRATE`` 位被设置,设备驱动可以
+   更新设备MMU,如果 ``MIGRATE_PFN_WRITE`` 位被设置,则设置写启用位。
+
+6. ``migrate_vma_finalize()``
+
+   这一步用新页的页表项替换特殊的迁移页表项,并释放对源和目的 ``struct page``
+   的引用。
+
+7. ``mmap_read_unlock()``
+
+   现在可以释放锁了。
+
+独占访问存储器
+==============
+
+一些设备具有诸如原子PTE位的功能,可以用来实现对系统内存的原子访问。为了支持对一
+个共享的虚拟内存页的原子操作,这样的设备需要对该页的访问是排他的,而不是来自CPU
+的任何用户空间访问。  ``make_device_exclusive_range()`` 函数可以用来使一
+个内存范围不能从用户空间访问。
+
+这将用特殊的交换条目替换给定范围内的所有页的映射。任何试图访问交换条目的行为都会
+导致一个异常,该异常会通过用原始映射替换该条目而得到恢复。驱动程序会被通知映射已
+经被MMU通知器改变,之后它将不再有对该页的独占访问。独占访问被保证持续到驱动程序
+放弃页面锁和页面引用为止,这时页面上的任何CPU异常都可以按所述进行。
+
+内存 cgroup (memcg) 和 rss 统计
+===============================
+
+目前,设备内存被视为 rss 计数器中的任何常规页面(如果设备页面用于匿名,则为匿名,
+如果设备页面用于文件支持页面,则为文件,如果设备页面用于共享内存,则为 shmem)。
+这是为了保持现有应用程序的故意选择,这些应用程序可能在不知情的情况下开始使用设备
+内存,运行不受影响。
+
+一个缺点是 OOM 杀手可能会杀死使用大量设备内存而不是大量常规系统内存的应用程序,
+因此不会释放太多系统内存。在决定以不同方式计算设备内存之前,我们希望收集更多关
+于应用程序和系统在存在设备内存的情况下在内存压力下如何反应的实际经验。
+
+对内存 cgroup 做出了相同的决定。设备内存页面根据相同的内存 cgroup 计算,常规
+页面将被计算在内。这确实简化了进出设备内存的迁移。这也意味着从设备内存迁移回常规
+内存不会失败,因为它会超过内存 cgroup 限制。一旦我们对设备内存的使用方式及其对
+内存资源控制的影响有了更多的了解,我们可能会在后面重新考虑这个选择。
+
+请注意,设备内存永远不能由设备驱动程序或通过 GUP 固定,因此此类内存在进程退出时
+总是被释放的。或者在共享内存或文件支持内存的情况下,当删除最后一个引用时。
diff --git a/Documentation/translations/zh_CN/mm/hugetlbfs_reserv.rst b/Documentation/translations/zh_CN/mm/hugetlbfs_reserv.rst
new file mode 100644 (file)
index 0000000..752e569
--- /dev/null
@@ -0,0 +1,436 @@
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/mm/hugetlbfs_reserv.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+==============
+Hugetlbfs 预留
+==============
+
+概述
+====
+
+:ref:`hugetlbpage` 中描述的巨页通常是预先分配给应用程序使用的。如果VMA指
+示要使用巨页,这些巨页会在缺页异常时被实例化到任务的地址空间。如果在缺页异常
+时没有巨页存在,任务就会被发送一个SIGBUS,并经常不高兴地死去。在加入巨页支
+持后不久,人们决定,在mmap()时检测巨页的短缺情况会更好。这个想法是,如果
+没有足够的巨页来覆盖映射,mmap()将失败。这首先是在mmap()时在代码中做一个
+简单的检查,以确定是否有足够的空闲巨页来覆盖映射。就像内核中的大多数东西一
+样,代码随着时间的推移而不断发展。然而,基本的想法是在mmap()时 “预留”
+巨页,以确保巨页可以用于该映射中的缺页异常。下面的描述试图描述在v4.10内核
+中是如何进行巨页预留处理的。
+
+
+读者
+====
+这个描述主要是针对正在修改hugetlbfs代码的内核开发者。
+
+
+数据结构
+========
+
+resv_huge_pages
+       这是一个全局的(per-hstate)预留的巨页的计数。预留的巨页只对预留它们的任
+       务可用。因此,一般可用的巨页的数量被计算为(``free_huge_pages - resv_huge_pages``)。
+Reserve Map
+       预留映射由以下结构体描述::
+
+               struct resv_map {
+                       struct kref refs;
+                       spinlock_t lock;
+                       struct list_head regions;
+                       long adds_in_progress;
+                       struct list_head region_cache;
+                       long region_cache_count;
+               };
+
+       系统中每个巨页映射都有一个预留映射。resv_map中的regions列表描述了映射中的
+       区域。一个区域被描述为::
+
+               struct file_region {
+                       struct list_head link;
+                       long from;
+                       long to;
+               };
+
+       file_region结构体的 ‘from’ 和 ‘to’ 字段是进入映射的巨页索引。根据映射的类型,在
+       reserv_map 中的一个区域可能表示该范围存在预留,或预留不存在。
+Flags for MAP_PRIVATE Reservations
+       这些被存储在预留的映射指针的底部。
+
+       ``#define HPAGE_RESV_OWNER    (1UL << 0)``
+               表示该任务是与该映射相关的预留的所有者。
+       ``#define HPAGE_RESV_UNMAPPED (1UL << 1)``
+               表示最初映射此范围(并创建储备)的任务由于COW失败而从该任务(子任务)中取消映
+               射了一个页面。
+Page Flags
+       PagePrivate页面标志是用来指示在释放巨页时必须恢复巨页的预留。更多细节将在
+       “释放巨页” 一节中讨论。
+
+
+预留映射位置(私有或共享)
+==========================
+
+一个巨页映射或段要么是私有的,要么是共享的。如果是私有的,它通常只对一个地址空间
+(任务)可用。如果是共享的,它可以被映射到多个地址空间(任务)。对于这两种类型的映射,
+预留映射的位置和语义是明显不同的。位置的差异是:
+
+- 对于私有映射,预留映射挂在VMA结构体上。具体来说,就是vma->vm_private_data。这个保
+  留映射是在创建映射(mmap(MAP_PRIVATE))时创建的。
+- 对于共享映射,预留映射挂在inode上。具体来说,就是inode->i_mapping->private_data。
+  由于共享映射总是由hugetlbfs文件系统中的文件支持,hugetlbfs代码确保每个节点包含一个预
+  留映射。因此,预留映射在创建节点时被分配。
+
+
+创建预留
+========
+当创建一个巨大的有页面支持的共享内存段(shmget(SHM_HUGETLB))或通过mmap(MAP_HUGETLB)
+创建一个映射时,就会创建预留。这些操作会导致对函数hugetlb_reserve_pages()的调用::
+
+       int hugetlb_reserve_pages(struct inode *inode,
+                                 long from, long to,
+                                 struct vm_area_struct *vma,
+                                 vm_flags_t vm_flags)
+
+hugetlb_reserve_pages()做的第一件事是检查在调用shmget()或mmap()时是否指定了NORESERVE
+标志。如果指定了NORESERVE,那么这个函数立即返回,因为不需要预留。
+
+参数'from'和'to'是映射或基础文件的巨页索引。对于shmget(),'from'总是0,'to'对应于段/映射
+的长度。对于mmap(),offset参数可以用来指定进入底层文件的偏移量。在这种情况下,'from'和'to'
+参数已经被这个偏移量所调整。
+
+PRIVATE和SHARED映射之间的一个很大的区别是预留在预留映射中的表示方式。
+
+- 对于共享映射,预留映射中的条目表示对应页面的预留存在或曾经存在。当预留被消耗时,预留映射不被
+  修改。
+- 对于私有映射,预留映射中没有条目表示相应页面存在预留。随着预留被消耗,条目被添加到预留映射中。
+  因此,预留映射也可用于确定哪些预留已被消耗。
+
+对于私有映射,hugetlb_reserve_pages()创建预留映射并将其挂在VMA结构体上。此外,
+HPAGE_RESV_OWNER标志被设置,以表明该VMA拥有预留。
+
+预留映射被查阅以确定当前映射/段需要多少巨页预留。对于私有映射,这始终是一个值(to - from)。
+然而,对于共享映射来说,一些预留可能已经存在于(to - from)的范围内。关于如何实现这一点的细节,
+请参见 :ref:`预留映射的修改 <resv_map_modifications>` 一节。
+
+该映射可能与一个子池(subpool)相关联。如果是这样,将查询子池以确保有足够的空间用于映射。子池
+有可能已经预留了可用于映射的预留空间。更多细节请参见 :ref: `子池预留 <sub_pool_resv>`
+一节。
+
+在咨询了预留映射和子池之后,就知道了需要的新预留数量。hugetlb_acct_memory()函数被调用以检查
+并获取所要求的预留数量。hugetlb_acct_memory()调用到可能分配和调整剩余页数的函数。然而,在这
+些函数中,代码只是检查以确保有足够的空闲的巨页来容纳预留。如果有的话,全局预留计数resv_huge_pages
+会被调整,如下所示::
+
+       if (resv_needed <= (resv_huge_pages - free_huge_pages))
+               resv_huge_pages += resv_needed;
+
+注意,在检查和调整这些计数器时,全局锁hugetlb_lock会被预留。
+
+如果有足够的空闲的巨页,并且全局计数resv_huge_pages被调整,那么与映射相关的预留映射被修改以
+反映预留。在共享映射的情况下,将存在一个file_region,包括'from'-'to'范围。对于私有映射,
+不对预留映射进行修改,因为没有条目表示存在预留。
+
+如果hugetlb_reserve_pages()成功,全局预留数和与映射相关的预留映射将根据需要被修改,以确保
+在'from'-'to'范围内存在预留。
+
+消耗预留/分配一个巨页
+===========================
+
+当与预留相关的巨页在相应的映射中被分配和实例化时,预留就被消耗了。该分配是在函数alloc_huge_page()
+中进行的::
+
+       struct page *alloc_huge_page(struct vm_area_struct *vma,
+                                    unsigned long addr, int avoid_reserve)
+
+alloc_huge_page被传递给一个VMA指针和一个虚拟地址,因此它可以查阅预留映射以确定是否存在预留。
+此外,alloc_huge_page需要一个参数avoid_reserve,该参数表示即使看起来已经为指定的地址预留了
+预留,也不应该使用预留。avoid_reserve参数最常被用于写时拷贝和页面迁移的情况下,即现有页面的额
+外拷贝被分配。
+
+
+调用辅助函数vma_needs_reservation()来确定是否存在对映射(vma)中地址的预留。关于这个函数的详
+细内容,请参见 :ref:`预留映射帮助函数 <resv_map_helpers>` 一节。从
+vma_needs_reservation()返回的值通常为0或1。如果该地址存在预留,则为0,如果不存在预留,则为1。
+如果不存在预留,并且有一个与映射相关联的子池,则查询子池以确定它是否包含预留。如果子池包含预留,
+则可将其中一个用于该分配。然而,在任何情况下,avoid_reserve参数都会优先考虑为分配使用预留。在
+确定预留是否存在并可用于分配后,调用dequeue_huge_page_vma()函数。这个函数需要两个与预留有关
+的参数:
+
+- avoid_reserve,这是传递给alloc_huge_page()的同一个值/参数。
+- chg,尽管这个参数的类型是long,但只有0或1的值被传递给dequeue_huge_page_vma。如果该值为0,
+  则表明存在预留(关于可能的问题,请参见 “预留和内存策略” 一节)。如果值
+  为1,则表示不存在预留,如果可能的话,必须从全局空闲池中取出该页。
+
+与VMA的内存策略相关的空闲列表被搜索到一个空闲页。如果找到了一个页面,当该页面从空闲列表中移除时,
+free_huge_pages的值被递减。如果有一个与该页相关的预留,将进行以下调整::
+
+       SetPagePrivate(page);   /* 表示分配这个页面消耗了一个预留,
+                                * 如果遇到错误,以至于必须释放这个页面,预留将被
+                                * 恢复。 */
+       resv_huge_pages--;      /* 减少全局预留计数 */
+
+注意,如果找不到满足VMA内存策略的巨页,将尝试使用伙伴分配器分配一个。这就带来了超出预留范围
+的剩余巨页和超额分配的问题。即使分配了一个多余的页面,也会进行与上面一样的基于预留的调整:
+SetPagePrivate(page) 和 resv_huge_pages--.
+
+在获得一个新的巨页后,(page)->private被设置为与该页面相关的子池的值,如果它存在的话。当页
+面被释放时,这将被用于子池的计数。
+
+然后调用函数vma_commit_reservation(),根据预留的消耗情况调整预留映射。一般来说,这涉及
+到确保页面在区域映射的file_region结构体中被表示。对于预留存在的共享映射,预留映射中的条目
+已经存在,所以不做任何改变。然而,如果共享映射中没有预留,或者这是一个私有映射,则必须创建一
+个新的条目。
+
+注意,如果找不到满足VMA内存策略的巨页,将尝试使用伙伴分配器分配一个。这就带来了超出预留范围
+的剩余巨页和过度分配的问题。即使分配了一个多余的页面,也会进行与上面一样的基于预留的调整。
+SetPagePrivate(page)和resv_huge_pages-。
+
+在获得一个新的巨页后,(page)->private被设置为与该页面相关的子池的值,如果它存在的话。当页
+面被释放时,这将被用于子池的计数。
+
+然后调用函数vma_commit_reservation(),根据预留的消耗情况调整预留映射。一般来说,这涉及
+到确保页面在区域映射的file_region结构体中被表示。对于预留存在的共享映射,预留映射中的条目
+已经存在,所以不做任何改变。然而,如果共享映射中没有预留,或者这是一个私有映射,则必须创建
+一个新的条目。
+
+在alloc_huge_page()开始调用vma_needs_reservation()和页面分配后调用
+vma_commit_reservation()之间,预留映射有可能被改变。如果hugetlb_reserve_pages在共
+享映射中为同一页面被调用,这将是可能的。在这种情况下,预留计数和子池空闲页计数会有一个偏差。
+这种罕见的情况可以通过比较vma_needs_reservation和vma_commit_reservation的返回值来
+识别。如果检测到这种竞争,子池和全局预留计数将被调整以进行补偿。关于这些函数的更多信息,请
+参见 :ref:`预留映射帮助函数 <resv_map_helpers>` 一节。
+
+
+实例化巨页
+==========
+
+在巨页分配之后,页面通常被添加到分配任务的页表中。在此之前,共享映射中的页面被添加到页面缓
+存中,私有映射中的页面被添加到匿名反向映射中。在这两种情况下,PagePrivate标志被清除。因此,
+当一个已经实例化的巨页被释放时,不会对全局预留计数(resv_huge_pages)进行调整。
+
+
+释放巨页
+========
+
+巨页释放是由函数free_huge_page()执行的。这个函数是hugetlbfs复合页的析构器。因此,它只传
+递一个指向页面结构体的指针。当一个巨页被释放时,可能需要进行预留计算。如果该页与包含保
+留的子池相关联,或者该页在错误路径上被释放,必须恢复全局预留计数,就会出现这种情况。
+
+page->private字段指向与该页相关的任何子池。如果PagePrivate标志被设置,它表明全局预留计数
+应该被调整(关于如何设置这些标志的信息,请参见
+:ref: `消耗预留/分配一个巨页 <consume_resv>` )。
+
+
+该函数首先调用hugepage_subpool_put_pages()来处理该页。如果这个函数返回一个0的值(不等于
+传递的1的值),它表明预留与子池相关联,这个新释放的页面必须被用来保持子池预留的数量超过最小值。
+因此,在这种情况下,全局resv_huge_pages计数器被递增。
+
+如果页面中设置了PagePrivate标志,那么全局resv_huge_pages计数器将永远被递增。
+
+子池预留
+========
+
+有一个结构体hstate与每个巨页尺寸相关联。hstate跟踪所有指定大小的巨页。一个子池代表一
+个hstate中的页面子集,它与一个已挂载的hugetlbfs文件系统相关
+
+当一个hugetlbfs文件系统被挂载时,可以指定min_size选项,它表示文件系统所需的最小的巨页数量。
+如果指定了这个选项,与min_size相对应的巨页的数量将被预留给文件系统使用。这个数字在结构体
+hugepage_subpool的min_hpages字段中被跟踪。在挂载时,hugetlb_acct_memory(min_hpages)
+被调用以预留指定数量的巨页。如果它们不能被预留,挂载就会失败。
+
+当从子池中获取或释放页面时,会调用hugepage_subpool_get/put_pages()函数。
+hugepage_subpool_get/put_pages被传递给巨页数量,以此来调整子池的 “已用页面” 计数
+(get为下降,put为上升)。通常情况下,如果子池中没有足够的页面,它们会返回与传递的相同的值或
+一个错误。
+
+然而,如果预留与子池相关联,可能会返回一个小于传递值的返回值。这个返回值表示必须进行的额外全局
+池调整的数量。例如,假设一个子池包含3个预留的巨页,有人要求5个。与子池相关的3个预留页可以用来
+满足部分请求。但是,必须从全局池中获得2个页面。为了向调用者转达这一信息,将返回值2。然后,调用
+者要负责从全局池中获取另外两个页面。
+
+
+COW和预留
+==========
+
+由于共享映射都指向并使用相同的底层页面,COW最大的预留问题是私有映射。在这种情况下,两个任务可
+以指向同一个先前分配的页面。一个任务试图写到该页,所以必须分配一个新的页,以便每个任务都指向它
+自己的页。
+
+当该页最初被分配时,该页的预留被消耗了。当由于COW而试图分配一个新的页面时,有可能没有空闲的巨
+页,分配会失败。
+
+当最初创建私有映射时,通过设置所有者的预留映射指针中的HPAGE_RESV_OWNER位来标记映射的所有者。
+由于所有者创建了映射,所有者拥有与映射相关的所有预留。因此,当一个写异常发生并且没有可用的页面
+时,对预留的所有者和非所有者采取不同的行动。
+
+在发生异常的任务不是所有者的情况下,异常将失败,该任务通常会收到一个SIGBUS。
+
+如果所有者是发生异常的任务,我们希望它能够成功,因为它拥有原始的预留。为了达到这个目的,该页被
+从非所有者任务中解映射出来。这样一来,唯一的引用就是来自拥有者的任务。此外,HPAGE_RESV_UNMAPPED
+位被设置在非拥有任务的预留映射指针中。如果非拥有者任务后来在一个不存在的页面上发生异常,它可能
+会收到一个SIGBUS。但是,映射/预留的原始拥有者的行为将与预期一致。
+
+预留映射的修改
+==============
+
+以下低级函数用于对预留映射进行修改。通常情况下,这些函数不会被直接调用。而是调用一个预留映射辅
+助函数,该函数调用这些低级函数中的一个。这些低级函数在源代码(mm/hugetlb.c)中得到了相当好的
+记录。这些函数是::
+
+       long region_chg(struct resv_map *resv, long f, long t);
+       long region_add(struct resv_map *resv, long f, long t);
+       void region_abort(struct resv_map *resv, long f, long t);
+       long region_count(struct resv_map *resv, long f, long t);
+
+在预留映射上的操作通常涉及两个操作:
+
+1) region_chg()被调用来检查预留映射,并确定在指定的范围[f, t]内有多少页目前没有被代表。
+
+   调用代码执行全局检查和分配,以确定是否有足够的巨页使操作成功。
+
+2)
+  a) 如果操作能够成功,regi_add()将被调用,以实际修改先前传递给regi_chg()的相同范围
+     [f, t]的预留映射。
+  b) 如果操作不能成功,region_abort被调用,在相同的范围[f, t]内中止操作。
+
+注意,这是一个两步的过程, region_add()和 region_abort()在事先调用 region_chg()后保证
+成功。 region_chg()负责预先分配任何必要的数据结构以确保后续操作(特别是 region_add())的
+成功。
+
+如上所述,region_chg()确定该范围内当前没有在映射中表示的页面的数量。region_add()返回添加
+到映射中的范围内的页数。在大多数情况下, region_add() 的返回值与 region_chg() 的返回值相
+同。然而,在共享映射的情况下,有可能在调用 region_chg() 和 region_add() 之间对预留映射进
+行更改。在这种情况下,regi_add()的返回值将与regi_chg()的返回值不符。在这种情况下,全局计数
+和子池计数很可能是不正确的,需要调整。检查这种情况并进行适当的调整是调用者的责任。
+
+函数region_del()被调用以从预留映射中移除区域。
+它通常在以下情况下被调用:
+
+- 当hugetlbfs文件系统中的一个文件被删除时,该节点将被释放,预留映射也被释放。在释放预留映射
+  之前,所有单独的file_region结构体必须被释放。在这种情况下,region_del的范围是[0, LONG_MAX]。
+- 当一个hugetlbfs文件正在被截断时。在这种情况下,所有在新文件大小之后分配的页面必须被释放。
+  此外,预留映射中任何超过新文件大小的file_region条目必须被删除。在这种情况下,region_del
+  的范围是[new_end_of_file, LONG_MAX]。
+- 当在一个hugetlbfs文件中打洞时。在这种情况下,巨页被一次次从文件的中间移除。当这些页被移除
+  时,region_del()被调用以从预留映射中移除相应的条目。在这种情况下,region_del被传递的范
+  围是[page_idx, page_idx + 1]。
+
+在任何情况下,region_del()都会返回从预留映射中删除的页面数量。在非常罕见的情况下,region_del()
+会失败。这只能发生在打洞的情况下,即它必须分割一个现有的file_region条目,而不能分配一个新的
+结构体。在这种错误情况下,region_del()将返回-ENOMEM。这里的问题是,预留映射将显示对该页有
+预留。然而,子池和全局预留计数将不反映该预留。为了处理这种情况,调用函数hugetlb_fix_reserve_counts()
+来调整计数器,使其与不能被删除的预留映射条目相对应。
+
+region_count()在解除私有巨页映射时被调用。在私有映射中,预留映射中没有条目表明存在一个预留。
+因此,通过计算预留映射中的条目数,我们知道有多少预留被消耗了,有多少预留是未完成的
+(Outstanding = (end - start) - region_count(resv, start, end))。由于映射正在消
+失,子池和全局预留计数被未完成的预留数量所减去。
+
+预留映射帮助函数
+================
+
+有几个辅助函数可以查询和修改预留映射。这些函数只对特定的巨页的预留感兴趣,所以它们只是传入一个
+地址而不是一个范围。此外,它们还传入相关的VMA。从VMA中,可以确定映射的类型(私有或共享)和预留
+映射的位置(inode或VMA)。这些函数只是调用 “预留映射的修改” 一节中描述的基础函数。然而,
+它们确实考虑到了私有和共享映射的预留映射条目的 “相反” 含义,并向调用者隐藏了这个细节::
+
+       long vma_needs_reservation(struct hstate *h,
+                                  struct vm_area_struct *vma,
+                                  unsigned long addr)
+
+该函数为指定的页面调用 region_chg()。如果不存在预留,则返回1。如果存在预留,则返回0::
+
+       long vma_commit_reservation(struct hstate *h,
+                                   struct vm_area_struct *vma,
+                                   unsigned long addr)
+
+这将调用 region_add(),用于指定的页面。与region_chg和region_add的情况一样,该函数应在
+先前调用的vma_needs_reservation后调用。它将为该页添加一个预留条目。如果预留被添加,它将
+返回1,如果没有则返回0。返回值应与之前调用vma_needs_reservation的返回值进行比较。如果出
+现意外的差异,说明在两次调用之间修改了预留映射::
+
+       void vma_end_reservation(struct hstate *h,
+                                struct vm_area_struct *vma,
+                                unsigned long addr)
+
+这将调用指定页面的 region_abort()。与region_chg和region_abort的情况一样,该函数应在
+先前调用的vma_needs_reservation后被调用。它将中止/结束正在进行的预留添加操作::
+
+       long vma_add_reservation(struct hstate *h,
+                                struct vm_area_struct *vma,
+                                unsigned long addr)
+
+这是一个特殊的包装函数,有助于在错误路径上清理预留。它只从repare_reserve_on_error()函数
+中调用。该函数与vma_needs_reservation一起使用,试图将一个预留添加到预留映射中。它考虑到
+了私有和共享映射的不同预留映射语义。因此,region_add被调用用于共享映射(因为映射中的条目表
+示预留),而region_del被调用用于私有映射(因为映射中没有条目表示预留)。关于在错误路径上需
+要做什么的更多信息,请参见  “错误路径中的预留清理”  。
+
+
+错误路径中的预留清理
+====================
+
+正如在:ref:`预留映射帮助函数<resv_map_helpers>` 一节中提到的,预留的修改分两步进行。首
+先,在分配页面之前调用vma_needs_reservation。如果分配成功,则调用vma_commit_reservation。
+如果不是,则调用vma_end_reservation。全局和子池的预留计数根据操作的成功或失败进行调整,
+一切都很好。
+
+此外,在一个巨页被实例化后,PagePrivate标志被清空,这样,当页面最终被释放时,计数是
+正确的。
+
+然而,有几种情况是,在一个巨页被分配后,但在它被实例化之前,就遇到了错误。在这种情况下,
+页面分配已经消耗了预留,并进行了适当的子池、预留映射和全局计数调整。如果页面在这个时候被释放
+(在实例化和清除PagePrivate之前),那么free_huge_page将增加全局预留计数。然而,预留映射
+显示报留被消耗了。这种不一致的状态将导致预留的巨页的 “泄漏” 。全局预留计数将比它原本的要高,
+并阻止分配一个预先分配的页面。
+
+函数 restore_reserve_on_error() 试图处理这种情况。它有相当完善的文档。这个函数的目的
+是将预留映射恢复到页面分配前的状态。通过这种方式,预留映射的状态将与页面释放后的全局预留计
+数相对应。
+
+函数restore_reserve_on_error本身在试图恢复预留映射条目时可能会遇到错误。在这种情况下,
+它将简单地清除该页的PagePrivate标志。这样一来,当页面被释放时,全局预留计数将不会被递增。
+然而,预留映射将继续看起来像预留被消耗了一样。一个页面仍然可以被分配到该地址,但它不会像最
+初设想的那样使用一个预留页。
+
+有一些代码(最明显的是userfaultfd)不能调用restore_reserve_on_error。在这种情况下,
+它简单地修改了PagePrivate,以便在释放巨页时不会泄露预留。
+
+
+预留和内存策略
+==============
+当git第一次被用来管理Linux代码时,每个节点的巨页列表就存在于hstate结构中。预留的概念是
+在一段时间后加入的。当预留被添加时,没有尝试将内存策略考虑在内。虽然cpusets与内存策略不
+完全相同,但hugetlb_acct_memory中的这个注释总结了预留和cpusets/内存策略之间的相互作
+用::
+
+
+       /*
+        * 当cpuset被配置时,它打破了严格的hugetlb页面预留,因为计数是在一个全局变量上完
+        * 成的。在有cpuset的情况下,这样的预留完全是垃圾,因为预留没有根据当前cpuset的
+        * 页面可用性来检查。在任务所在的cpuset中缺乏空闲的htlb页面时,应用程序仍然有可能
+        * 被内核OOM'ed。试图用cpuset来执行严格的计数几乎是不可能的(或者说太难看了),因
+        * 为cpuset太不稳定了,任务或内存节点可以在cpuset之间动态移动。与cpuset共享
+        * hugetlb映射的语义变化是不可取的。然而,为了预留一些语义,我们退回到检查当前空闲
+        * 页的可用性,作为一种最好的尝试,希望能将cpuset改变语义的影响降到最低。
+        */
+
+添加巨页预留是为了防止在缺页异常时出现意外的页面分配失败(OOM)。然而,如果一个应用
+程序使用cpusets或内存策略,就不能保证在所需的节点上有巨页可用。即使有足够数量的全局
+预留,也是如此。
+
+Hugetlbfs回归测试
+=================
+
+最完整的hugetlb测试集在libhugetlbfs仓库。如果你修改了任何hugetlb相关的代码,请使用
+libhugetlbfs测试套件来检查回归情况。此外,如果你添加了任何新的hugetlb功能,请在
+libhugetlbfs中添加适当的测试。
+
+--
+Mike Kravetz,2017年4月7日
diff --git a/Documentation/translations/zh_CN/mm/hwpoison.rst b/Documentation/translations/zh_CN/mm/hwpoison.rst
new file mode 100644 (file)
index 0000000..310862e
--- /dev/null
@@ -0,0 +1,166 @@
+
+:Original: Documentation/mm/hwpoison.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+
+========
+hwpoison
+========
+
+什么是hwpoison?
+===============
+
+
+即将推出的英特尔CPU支持从一些内存错误中恢复( ``MCA恢复`` )。这需要操作系统宣布
+一个页面"poisoned",杀死与之相关的进程,并避免在未来使用它。
+
+这个补丁包在虚拟机中实现了必要的(编程)框架。
+
+引用概述中的评论::
+
+       高级机器的检查与处理。处理方法是损坏的页面被硬件报告,通常是由于2位ECC内
+       存或高速缓存故障。
+
+       这主要是针对在后台检测到的损坏的页面。当当前的CPU试图访问它时,当前运行的进程
+       可以直接被杀死。因为还没有访问损坏的页面, 如果错误由于某种原因不能被处理,就可
+       以安全地忽略它. 而不是用另外一个机器检查去处理它。
+
+       处理不同状态的页面缓存页。这里棘手的部分是,相对于其他虚拟内存用户, 我们可以异
+       步访问任何页面。因为内存故障可能随时随地发生,可能违反了他们的一些假设。这就是
+       为什么这段代码必须非常小心。一般来说,它试图使用正常的锁规则,如获得标准锁,即使
+       这意味着错误处理可能需要很长的时间。
+
+       这里的一些操作有点低效,并且具有非线性的算法复杂性,因为数据结构没有针对这种情
+       况进行优化。特别是从vma到进程的映射就是这种情况。由于这种情况大概率是罕见的,所
+       以我们希望我们可以摆脱这种情况。
+
+该代码由mm/memory-failure.c中的高级处理程序、一个新的页面poison位和虚拟机中的
+各种检查组成,用来处理poison的页面。
+
+现在主要目标是KVM客户机,但它适用于所有类型的应用程序。支持KVM需要最近的qemu-kvm
+版本。
+
+对于KVM的使用,需要一个新的信号类型,这样KVM就可以用适当的地址将机器检查注入到客户
+机中。这在理论上也允许其他应用程序处理内存故障。我们的期望是,所有的应用程序都不要这
+样做,但一些非常专业的应用程序可能会这样做。
+
+故障恢复模式
+============
+
+有两种(实际上是三种)模式的内存故障恢复可以在。
+
+vm.memory_failure_recovery sysctl 置零:
+       所有的内存故障都会导致panic。请不要尝试恢复。
+
+早期处理
+       (可以在全局和每个进程中控制) 一旦检测到错误,立即向应用程序发送SIGBUS这允许
+       应用程序以温和的方式处理内存错误(例如,放弃受影响的对象) 这是KVM qemu使用的
+       模式。
+
+推迟处理
+       当应用程序运行到损坏的页面时,发送SIGBUS。这对不知道内存错误的应用程序来说是
+       最好的,默认情况下注意一些页面总是被当作late kill处理。
+
+用户控制
+========
+
+vm.memory_failure_recovery
+       参阅 sysctl.txt
+
+vm.memory_failure_early_kill
+       全局启用early kill
+
+PR_MCE_KILL
+       设置early/late kill mode/revert 到系统默认值。
+
+       arg1: PR_MCE_KILL_CLEAR:
+               恢复到系统默认值
+       arg1: PR_MCE_KILL_SET:
+               arg2定义了线程特定模式
+
+               PR_MCE_KILL_EARLY:
+                       Early kill
+               PR_MCE_KILL_LATE:
+                       Late kill
+               PR_MCE_KILL_DEFAULT
+                       使用系统全局默认值
+
+       注意,如果你想有一个专门的线程代表进程处理SIGBUS(BUS_MCEERR_AO),你应该在
+       指定线程上调用prctl(PR_MCE_KILL_EARLY)。否则,SIGBUS将被发送到主线程。
+
+PR_MCE_KILL_GET
+       返回当前模式
+
+测试
+====
+
+* madvise(MADV_HWPOISON, ....) (as root) - 在测试过程中Poison一个页面
+
+* 通过debugfs ``/sys/kernel/debug/hwpoison/`` hwpoison-inject模块
+
+  corrupt-pfn
+       在PFN处注入hwpoison故障,并echoed到这个文件。这做了一些早期过滤,以避
+       免在测试套件中损坏非预期页面。
+  unpoison-pfn
+       在PFN的Software-unpoison页面对应到这个文件。这样,一个页面可以再次被
+       复用。这只对Linux注入的故障起作用,对真正的内存故障不起作用。
+
+  注意这些注入接口并不稳定,可能会在不同的内核版本中发生变化
+
+  corrupt-filter-dev-major, corrupt-filter-dev-minor
+       只处理与块设备major/minor定义的文件系统相关的页面的内存故障。-1U是通
+       配符值。这应该只用于人工注入的测试。
+
+  corrupt-filter-memcg
+       限制注入到memgroup拥有的页面。由memcg的inode号指定。
+
+       Example::
+
+               mkdir /sys/fs/cgroup/mem/hwpoison
+
+               usemem -m 100 -s 1000 &
+               echo `jobs -p` > /sys/fs/cgroup/mem/hwpoison/tasks
+
+               memcg_ino=$(ls -id /sys/fs/cgroup/mem/hwpoison | cut -f1 -d' ')
+               echo $memcg_ino > /debug/hwpoison/corrupt-filter-memcg
+
+               page-types -p `pidof init`   --hwpoison  # shall do nothing
+               page-types -p `pidof usemem` --hwpoison  # poison its pages
+
+  corrupt-filter-flags-mask, corrupt-filter-flags-value
+       当指定时,只有在((page_flags & mask) == value)的情况下才会poison页面。
+       这允许对许多种类的页面进行压力测试。page_flags与/proc/kpageflags中的相
+       同。这些标志位在include/linux/kernel-page-flags.h中定义,并在
+       Documentation/admin-guide/mm/pagemap.rst中记录。
+
+* 架构特定的MCE注入器
+
+  x86 有 mce-inject, mce-test
+
+  在mce-test中的一些便携式hwpoison测试程序,见下文。
+
+引用
+====
+
+http://halobates.de/mce-lc09-2.pdf
+       09年LinuxCon的概述演讲
+
+git://git.kernel.org/pub/scm/utils/cpu/mce/mce-test.git
+       测试套件(在tsrc中的hwpoison特定可移植测试)。
+
+git://git.kernel.org/pub/scm/utils/cpu/mce/mce-inject.git
+       x86特定的注入器
+
+
+限制
+====
+- 不是所有的页面类型都被支持,而且永远不会。大多数内核内部对象不能被恢
+  复,目前只有LRU页。
+
+---
+Andi Kleen, 2009年10月
diff --git a/Documentation/translations/zh_CN/mm/index.rst b/Documentation/translations/zh_CN/mm/index.rst
new file mode 100644 (file)
index 0000000..2f53e37
--- /dev/null
@@ -0,0 +1,69 @@
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/mm/index.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+=================
+Linux内存管理文档
+=================
+
+这是一份关于了解Linux的内存管理子系统的指南。如果你正在寻找关于简单分配内存的
+建议,请参阅内存分配指南
+(Documentation/translations/zh_CN/core-api/memory-allocation.rst)。
+关于控制和调整的指南,请看管理指南
+(Documentation/translations/zh_CN/admin-guide/mm/index.rst)。
+
+
+.. toctree::
+   :maxdepth: 1
+
+   highmem
+
+该处剩余文档待原始文档有内容后翻译。
+
+
+遗留文档
+========
+
+这是一个关于Linux内存管理(MM)子系统内部的旧文档的集合,其中有不同层次的细节,
+包括注释和邮件列表的回复,用于阐述数据结构和算法的描述。它应该被很好地整合到上述
+结构化的文档中,如果它已经完成了它的使命,可以删除。
+
+.. toctree::
+   :maxdepth: 1
+
+   active_mm
+   balance
+   damon/index
+   free_page_reporting
+   ksm
+   frontswap
+   hmm
+   hwpoison
+   hugetlbfs_reserv
+   memory-model
+   mmu_notifier
+   numa
+   overcommit-accounting
+   page_frags
+   page_migration
+   page_owner
+   page_table_check
+   remap_file_pages
+   split_page_table_lock
+   vmalloced-kernel-stacks
+   z3fold
+   zsmalloc
+
+TODOLIST:
+* arch_pgtable_helpers
+* free_page_reporting
+* hugetlbfs_reserv
+* slub
+* transhuge
+* unevictable-lru
diff --git a/Documentation/translations/zh_CN/mm/ksm.rst b/Documentation/translations/zh_CN/mm/ksm.rst
new file mode 100644 (file)
index 0000000..d1f82e8
--- /dev/null
@@ -0,0 +1,70 @@
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/mm/ksm.rst
+
+:翻译:
+
+   徐鑫 xu xin <xu.xin16@zte.com.cn>
+
+============
+内核同页合并
+============
+
+KSM 是一种节省内存的数据去重功能,由CONFIG_KSM=y启用,并在2.6.32版本时被添加
+到Linux内核。详见 ``mm/ksm.c`` 的实现,以及http://lwn.net/Articles/306704和
+https://lwn.net/Articles/330589
+
+KSM的用户空间的接口在Documentation/translations/zh_CN/admin-guide/mm/ksm.rst
+文档中有描述。
+
+设计
+====
+
+概述
+----
+
+概述内容请见mm/ksm.c文档中的“DOC: Overview”
+
+逆映射
+------
+KSM维护着稳定树中的KSM页的逆映射信息。
+
+当KSM页面的共享数小于 ``max_page_sharing`` 的虚拟内存区域(VMAs)时,则代表了
+KSM页的稳定树其中的节点指向了一个rmap_item结构体类型的列表。同时,这个KSM页
+的 ``page->mapping`` 指向了该稳定树节点。
+
+如果共享数超过了阈值,KSM将给稳定树添加第二个维度。稳定树就变成链接一个或多
+个稳定树"副本"的"链"。每个副本都保留KSM页的逆映射信息,其中 ``page->mapping``
+指向该"副本"。
+
+每个链以及链接到该链中的所有"副本"强制不变的是,它们代表了相同的写保护内存
+内容,尽管任中一个"副本"是由同一片内存区的不同的KSM复制页所指向的。
+
+这样一来,相比与无限的逆映射链表,稳定树的查找计算复杂性不受影响。但在稳定树
+本身中不能有重复的KSM页面内容仍然是强制要求。
+
+由 ``max_page_sharing`` 强制决定的数据去重限制是必要的,以此来避免虚拟内存
+rmap链表变得过大。rmap的遍历具有O(N)的复杂度,其中N是共享页面的rmap_项(即
+虚拟映射)的数量,而这个共享页面的节点数量又被 ``max_page_sharing`` 所限制。
+因此,这有效地将线性O(N)计算复杂度从rmap遍历中分散到不同的KSM页面上。ksmd进
+程在稳定节点"链"上的遍历也是O(N),但这个N是稳定树"副本"的数量,而不是rmap项
+的数量,因此它对ksmd性能没有显著影响。实际上,最佳稳定树"副本"的候选节点将
+保留在"副本"列表的开头。
+
+``max_page_sharing`` 的值设置得高了会促使更快的内存合并(因为将有更少的稳定
+树副本排队进入稳定节点chain->hlist)和更高的数据去重系数,但代价是在交换、压
+缩、NUMA平衡和页面迁移过程中可能导致KSM页的最大rmap遍历速度较慢。
+
+``stable_node_dups/stable_node_chains`` 的比值还受 ``max_page_sharing`` 调控
+的影响,高比值可能意味着稳定节点dup中存在碎片,这可以通过在ksmd中引入碎片算
+法来解决,该算法将rmap项从一个稳定节点dup重定位到另一个稳定节点dup,以便释放
+那些仅包含极少rmap项的稳定节点"dup",但这可能会增加ksmd进程的CPU使用率,并可
+能会减慢应用程序在KSM页面上的只读计算。
+
+KSM会定期扫描稳定节点"链"中链接的所有稳定树"副本",以便删减过时了的稳定节点。
+这种扫描的频率由 ``stable_node_chains_prune_millisecs`` 这个sysfs 接口定义。
+
+参考
+====
+内核代码请见mm/ksm.c。
+涉及的函数(mm_slot  ksm_scan  stable_node  rmap_item)。
diff --git a/Documentation/translations/zh_CN/mm/memory-model.rst b/Documentation/translations/zh_CN/mm/memory-model.rst
new file mode 100644 (file)
index 0000000..77ec149
--- /dev/null
@@ -0,0 +1,135 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+:Original: Documentation/mm/memory-model.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+
+============
+物理内存模型
+============
+
+系统中的物理内存可以用不同的方式进行寻址。最简单的情况是,物理内存从地址0开
+始,跨越一个连续的范围,直到最大的地址。然而,这个范围可能包含CPU无法访问的
+小孔隙。那么,在完全不同的地址可能有几个连续的范围。而且,别忘了NUMA,即不
+同的内存库连接到不同的CPU。
+
+Linux使用两种内存模型中的一种对这种多样性进行抽象。FLATMEM和SPARSEM。每
+个架构都定义了它所支持的内存模型,默认的内存模型是什么,以及是否有可能手动
+覆盖该默认值。
+
+所有的内存模型都使用排列在一个或多个数组中的 `struct page` 来跟踪物理页
+帧的状态。
+
+无论选择哪种内存模型,物理页框号(PFN)和相应的 `struct page` 之间都存
+在一对一的映射关系。
+
+每个内存模型都定义了 :c:func:`pfn_to_page` 和 :c:func:`page_to_pfn`
+帮助函数,允许从PFN到 `struct page` 的转换,反之亦然。
+
+FLATMEM
+=======
+
+最简单的内存模型是FLATMEM。这个模型适用于非NUMA系统的连续或大部分连续的
+物理内存。
+
+在FLATMEM内存模型中,有一个全局的 `mem_map` 数组来映射整个物理内存。对
+于大多数架构,孔隙在 `mem_map` 数组中都有条目。与孔洞相对应的 `struct page`
+对象从未被完全初始化。
+
+为了分配 `mem_map` 数组,架构特定的设置代码应该调用free_area_init()函数。
+然而,在调用memblock_free_all()函数之前,映射数组是不能使用的,该函数
+将所有的内存交给页分配器。
+
+一个架构可能会释放 `mem_map` 数组中不包括实际物理页的部分。在这种情况下,特
+定架构的 :c:func:`pfn_valid` 实现应该考虑到 `mem_map` 中的孔隙。
+
+使用FLATMEM,PFN和 `struct page` 之间的转换是直接的。 `PFN - ARCH_PFN_OFFSET`
+是 `mem_map` 数组的一个索引。
+
+`ARCH_PFN_OFFSET` 定义了物理内存起始地址不同于0的系统的第一个页框号。
+
+SPARSEMEM
+=========
+
+SPARSEMEM是Linux中最通用的内存模型,它是唯一支持若干高级功能的内存模型,
+如物理内存的热插拔、非易失性内存设备的替代内存图和较大系统的内存图的延迟
+初始化。
+
+SPARSEMEM模型将物理内存显示为一个部分的集合。一个区段用mem_section结构
+体表示,它包含 `section_mem_map` ,从逻辑上讲,它是一个指向 `struct page`
+阵列的指针。然而,它被存储在一些其他的magic中,以帮助分区管理。区段的大小
+和最大区段数是使用 `SECTION_SIZE_BITS` 和 `MAX_PHYSMEM_BITS` 常量
+来指定的,这两个常量是由每个支持SPARSEMEM的架构定义的。 `MAX_PHYSMEM_BITS`
+是一个架构所支持的物理地址的实际宽度,而 `SECTION_SIZE_BITS` 是一个任
+意的值。
+
+最大的段数表示为 `NR_MEM_SECTIONS` ,定义为
+
+.. math::
+
+   NR\_MEM\_SECTIONS = 2 ^ {(MAX\_PHYSMEM\_BITS - SECTION\_SIZE\_BITS)}
+
+`mem_section` 对象被安排在一个叫做 `mem_sections` 的二维数组中。这个数组的
+大小和位置取决于 `CONFIG_SPARSEM_EXTREME` 和可能的最大段数:
+
+* 当 `CONFIG_SPARSEMEM_EXTREME` 被禁用时, `mem_sections` 数组是静态的,有
+  `NR_MEM_SECTIONS` 行。每一行持有一个 `mem_section` 对象。
+* 当 `CONFIG_SPARSEMEM_EXTREME` 被启用时, `mem_sections` 数组被动态分配。
+  每一行包含价值 `PAGE_SIZE` 的 `mem_section` 对象,行数的计算是为了适应所有的
+  内存区。
+
+架构设置代码应该调用sparse_init()来初始化内存区和内存映射。
+
+通过SPARSEMEM,有两种可能的方式将PFN转换为相应的 `struct page` --"classic sparse"和
+ "sparse vmemmap"。选择是在构建时进行的,它由 `CONFIG_SPARSEMEM_VMEMMAP` 的
+ 值决定。
+
+Classic sparse在page->flags中编码了一个页面的段号,并使用PFN的高位来访问映射该页
+框的段。在一个区段内,PFN是指向页数组的索引。
+
+Sparse vmemmapvmemmap使用虚拟映射的内存映射来优化pfn_to_page和page_to_pfn操
+作。有一个全局的 `struct page *vmemmap` 指针,指向一个虚拟连续的 `struct page`
+对象阵列。PFN是该数组的一个索引,`struct page` 从 `vmemmap` 的偏移量是该页的PFN。
+
+为了使用vmemmap,一个架构必须保留一个虚拟地址的范围,以映射包含内存映射的物理页,并
+确保 `vmemmap`指向该范围。此外,架构应该实现 :c:func:`vmemmap_populate` 方法,
+它将分配物理内存并为虚拟内存映射创建页表。如果一个架构对vmemmap映射没有任何特殊要求,
+它可以使用通用内存管理提供的默认 :c:func:`vmemmap_populate_basepages`。
+
+虚拟映射的内存映射允许将持久性内存设备的 `struct page` 对象存储在这些设备上预先分
+配的存储中。这种存储用vmem_altmap结构表示,最终通过一长串的函数调用传递给
+vmemmap_populate()。vmemmap_populate()实现可以使用 `vmem_altmap` 和
+:c:func:`vmemmap_alloc_block_buf` 助手来分配持久性内存设备上的内存映射。
+
+ZONE_DEVICE
+===========
+`ZONE_DEVICE` 设施建立在 `SPARSEM_VMEMMAP` 之上,为设备驱动识别的物理地址范
+围提供 `struct page` `mem_map` 服务。 `ZONE_DEVICE` 的 "设备" 方面与以下
+事实有关:这些地址范围的页面对象从未被在线标记过,而且必须对设备进行引用,而不仅仅
+是页面,以保持内存被“锁定”以便使用。 `ZONE_DEVICE` ,通过 :c:func:`devm_memremap_pages` ,
+为给定的pfns范围执行足够的内存热插拔来开启 :c:func:`pfn_to_page`,
+:c:func:`page_to_pfn`, ,和 :c:func:`get_user_pages` 服务。由于页面引
+用计数永远不会低于1,所以页面永远不会被追踪为空闲内存,页面的 `struct list_head lru`
+空间被重新利用,用于向映射该内存的主机设备/驱动程序进行反向引用。
+
+虽然 `SPARSEMEM` 将内存作为一个区段的集合,可以选择收集并合成内存块,但
+`ZONE_DEVICE` 用户需要更小的颗粒度来填充 `mem_map` 。鉴于 `ZONE_DEVICE`
+内存从未被在线标记,因此它的内存范围从未通过sysfs内存热插拔api暴露在内存块边界
+上。这个实现依赖于这种缺乏用户接口的约束,允许子段大小的内存范围被指定给
+:c:func:`arch_add_memory` ,即内存热插拔的上半部分。子段支持允许2MB作为
+:c:func:`devm_memremap_pages` 的跨架构通用对齐颗粒度。
+
+`ZONE_DEVICE` 的用户是:
+
+* pmem: 通过DAX映射将平台持久性内存作为直接I/O目标使用。
+
+* hmm: 用 `->page_fault()` 和 `->page_free()` 事件回调扩展 `ZONE_DEVICE` ,
+  以允许设备驱动程序协调与设备内存相关的内存管理事件,通常是GPU内存。参见Documentation/mm/hmm.rst。
+
+* p2pdma: 创建 `struct page` 对象,允许PCI/E拓扑结构中的peer设备协调它们之间的
+  直接DMA操作,即绕过主机内存。
diff --git a/Documentation/translations/zh_CN/mm/mmu_notifier.rst b/Documentation/translations/zh_CN/mm/mmu_notifier.rst
new file mode 100644 (file)
index 0000000..ce3664d
--- /dev/null
@@ -0,0 +1,97 @@
+:Original: Documentation/mm/mmu_notifier.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+
+
+什么时候需要页表锁内通知?
+==========================
+
+当清除一个pte/pmd时,我们可以选择通过在页表锁下(通知版的\*_clear_flush调用
+mmu_notifier_invalidate_range)通知事件。但这种通知并不是在所有情况下都需要的。
+
+对于二级TLB(非CPU TLB),如IOMMU TLB或设备TLB(当设备使用类似ATS/PASID的东西让
+IOMMU走CPU页表来访问进程的虚拟地址空间)。只有两种情况需要在清除pte/pmd时在持有页
+表锁的同时通知这些二级TLB:
+
+  A) 在mmu_notifier_invalidate_range_end()之前,支持页的地址被释放。
+  B) 一个页表项被更新以指向一个新的页面(COW,零页上的写异常,__replace_page(),...)。
+
+情况A很明显,你不想冒风险让设备写到一个现在可能被一些完全不同的任务使用的页面。
+
+情况B更加微妙。为了正确起见,它需要按照以下序列发生:
+
+  - 上页表锁
+  - 清除页表项并通知 ([pmd/pte]p_huge_clear_flush_notify())
+  - 设置页表项以指向新页
+
+如果在设置新的pte/pmd值之前,清除页表项之后没有进行通知,那么你就会破坏设备的C11或
+C++11等内存模型。
+
+考虑以下情况(设备使用类似于ATS/PASID的功能)。
+
+两个地址addrA和addrB,这样|addrA - addrB| >= PAGE_SIZE,我们假设它们是COW的
+写保护(B的其他情况也适用)。
+
+::
+
+ [Time N] --------------------------------------------------------------------
+ CPU-thread-0  {尝试写到addrA}
+ CPU-thread-1  {尝试写到addrB}
+ CPU-thread-2  {}
+ CPU-thread-3  {}
+ DEV-thread-0  {读取addrA并填充设备TLB}
+ DEV-thread-2  {读取addrB并填充设备TLB}
+ [Time N+1] ------------------------------------------------------------------
+ CPU-thread-0  {COW_step0: {mmu_notifier_invalidate_range_start(addrA)}}
+ CPU-thread-1  {COW_step0: {mmu_notifier_invalidate_range_start(addrB)}}
+ CPU-thread-2  {}
+ CPU-thread-3  {}
+ DEV-thread-0  {}
+ DEV-thread-2  {}
+ [Time N+2] ------------------------------------------------------------------
+ CPU-thread-0  {COW_step1: {更新页表以指向addrA的新页}}
+ CPU-thread-1  {COW_step1: {更新页表以指向addrB的新页}}
+ CPU-thread-2  {}
+ CPU-thread-3  {}
+ DEV-thread-0  {}
+ DEV-thread-2  {}
+ [Time N+3] ------------------------------------------------------------------
+ CPU-thread-0  {preempted}
+ CPU-thread-1  {preempted}
+ CPU-thread-2  {写入addrA,这是对新页面的写入}
+ CPU-thread-3  {}
+ DEV-thread-0  {}
+ DEV-thread-2  {}
+ [Time N+3] ------------------------------------------------------------------
+ CPU-thread-0  {preempted}
+ CPU-thread-1  {preempted}
+ CPU-thread-2  {}
+ CPU-thread-3  {写入addrB,这是一个写入新页的过程}
+ DEV-thread-0  {}
+ DEV-thread-2  {}
+ [Time N+4] ------------------------------------------------------------------
+ CPU-thread-0  {preempted}
+ CPU-thread-1  {COW_step3: {mmu_notifier_invalidate_range_end(addrB)}}
+ CPU-thread-2  {}
+ CPU-thread-3  {}
+ DEV-thread-0  {}
+ DEV-thread-2  {}
+ [Time N+5] ------------------------------------------------------------------
+ CPU-thread-0  {preempted}
+ CPU-thread-1  {}
+ CPU-thread-2  {}
+ CPU-thread-3  {}
+ DEV-thread-0  {从旧页中读取addrA}
+ DEV-thread-2  {从新页面读取addrB}
+
+所以在这里,因为在N+2的时候,清空页表项没有和通知一起作废二级TLB,设备在看到addrA的新值之前
+就看到了addrB的新值。这就破坏了设备的总内存序。
+
+当改变一个pte的写保护或指向一个新的具有相同内容的写保护页(KSM)时,将mmu_notifier_invalidate_range
+调用延迟到页表锁外的mmu_notifier_invalidate_range_end()是可以的。即使做页表更新的线程
+在释放页表锁后但在调用mmu_notifier_invalidate_range_end()前被抢占,也是如此。
diff --git a/Documentation/translations/zh_CN/mm/numa.rst b/Documentation/translations/zh_CN/mm/numa.rst
new file mode 100644 (file)
index 0000000..b15cfee
--- /dev/null
@@ -0,0 +1,101 @@
+:Original: Documentation/mm/numa.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+
+始于1999年11月,作者: <kanoj@sgi.com>
+
+==========================
+何为非统一内存访问(NUMA)?
+==========================
+
+这个问题可以从几个视角来回答:硬件观点和Linux软件视角。
+
+从硬件角度看,NUMA系统是一个由多个组件或装配组成的计算机平台,每个组件可能包含0个或更多的CPU、
+本地内存和/或IO总线。为了简洁起见,并将这些物理组件/装配的硬件视角与软件抽象区分开来,我们在
+本文中称这些组件/装配为“单元”。
+
+每个“单元”都可以看作是系统的一个SMP[对称多处理器]子集——尽管独立的SMP系统所需的一些组件可能
+不会在任何给定的单元上填充。NUMA系统的单元通过某种系统互连连接在一起——例如,交叉开关或点对点
+链接是NUMA系统互连的常见类型。这两种类型的互连都可以聚合起来,以创建NUMA平台,其中的单元与其
+他单元有多个距离。
+
+对于Linux,感兴趣的NUMA平台主要是所谓的缓存相干NUMA--简称ccNUMA系统系统。在ccNUMA系统中,
+所有的内存都是可见的,并且可以从连接到任何单元的任何CPU中访问,缓存一致性是由处理器缓存和/或
+系统互连在硬件中处理。
+
+内存访问时间和有效的内存带宽取决于包含CPU的单元或进行内存访问的IO总线距离包含目标内存的单元
+有多远。例如,连接到同一单元的CPU对内存的访问将比访问其他远程单元的内存经历更快的访问时间和
+更高的带宽。 NUMA平台可以在任何给定单元上访问多种远程距离的(其他)单元。
+
+平台供应商建立NUMA系统并不只是为了让软件开发人员的生活变得有趣。相反,这种架构是提供可扩展
+内存带宽的一种手段。然而,为了实现可扩展的内存带宽,系统和应用软件必须安排大部分的内存引用
+[cache misses]到“本地”内存——同一单元的内存,如果有的话——或者到最近的有内存的单元。
+
+这就自然而然有了Linux软件对NUMA系统的视角:
+
+Linux将系统的硬件资源划分为多个软件抽象,称为“节点”。Linux将节点映射到硬件平台的物理单元
+上,对一些架构的细节进行了抽象。与物理单元一样,软件节点可能包含0或更多的CPU、内存和/或IO
+总线。同样,对“较近”节点的内存访问——映射到较近单元的节点——通常会比对较远单元的访问经历更快
+的访问时间和更高的有效带宽。
+
+对于一些架构,如x86,Linux将“隐藏”任何代表没有内存连接的物理单元的节点,并将连接到该单元
+的任何CPU重新分配到代表有内存的单元的节点上。因此,在这些架构上,我们不能假设Linux将所有
+的CPU与一个给定的节点相关联,会看到相同的本地内存访问时间和带宽。
+
+此外,对于某些架构,同样以x86为例,Linux支持对额外节点的仿真。对于NUMA仿真,Linux会将现
+有的节点或者非NUMA平台的系统内存分割成多个节点。每个模拟的节点将管理底层单元物理内存的一部
+分。NUMA仿真对于在非NUMA平台上测试NUMA内核和应用功能是非常有用的,当与cpusets一起使用时,
+可以作为一种内存资源管理机制。[见 Documentation/admin-guide/cgroup-v1/cpusets.rst]
+
+对于每个有内存的节点,Linux构建了一个独立的内存管理子系统,有自己的空闲页列表、使用中页列表、
+使用统计和锁来调解访问。此外,Linux为每个内存区[DMA、DMA32、NORMAL、HIGH_MEMORY、MOVABLE
+中的一个或多个]构建了一个有序的“区列表”。zonelist指定了当一个选定的区/节点不能满足分配请求
+时要访问的区/节点。当一个区没有可用的内存来满足请求时,这种情况被称为“overflow 溢出”或
+“fallback 回退”。
+
+由于一些节点包含多个包含不同类型内存的区,Linux必须决定是否对区列表进行排序,使分配回退到不同
+节点上的相同区类型,或同一节点上的不同区类型。这是一个重要的考虑因素,因为有些区,如DMA或DMA32,
+代表了相对稀缺的资源。Linux选择了一个默认的Node ordered zonelist。这意味着在使用按NUMA距
+离排序的远程节点之前,它会尝试回退到同一节点的其他分区。
+
+默认情况下,Linux会尝试从执行请求的CPU被分配到的节点中满足内存分配请求。具体来说,Linux将试
+图从请求来源的节点的适当分区列表中的第一个节点进行分配。这被称为“本地分配”。如果“本地”节点不能
+满足请求,内核将检查所选分区列表中其他节点的区域,寻找列表中第一个能满足请求的区域。
+
+本地分配将倾向于保持对分配的内存的后续访问 “本地”的底层物理资源和系统互连——只要内核代表其分配
+一些内存的任务后来不从该内存迁移。Linux调度器知道平台的NUMA拓扑结构——体现在“调度域”数据结构
+中[见 Documentation/scheduler/sched-domains.rst]——并且调度器试图尽量减少任务迁移到遥
+远的调度域中。然而,调度器并没有直接考虑到任务的NUMA足迹。因此,在充分不平衡的情况下,任务可
+以在节点之间迁移,远离其初始节点和内核数据结构。
+
+系统管理员和应用程序设计者可以使用各种CPU亲和命令行接口,如taskset(1)和numactl(1),以及程
+序接口,如sched_setaffinity(2),来限制任务的迁移,以改善NUMA定位。此外,人们可以使用
+Linux NUMA内存策略修改内核的默认本地分配行为。 [见
+:ref:`Documentation/admin-guide/mm/numa_memory_policy.rst <numa_memory_policy>`].
+
+系统管理员可以使用控制组和CPUsets限制非特权用户在调度或NUMA命令和功能中可以指定的CPU和节点
+的内存。 [见 Documentation/admin-guide/cgroup-v1/cpusets.rst]
+
+在不隐藏无内存节点的架构上,Linux会在分区列表中只包括有内存的区域[节点]。这意味着对于一个无
+内存的节点,“本地内存节点”——CPU节点的分区列表中的第一个区域的节点——将不是节点本身。相反,它
+将是内核在建立分区列表时选择的离它最近的有内存的节点。所以,默认情况下,本地分配将由内核提供
+最近的可用内存来完成。这是同一机制的结果,该机制允许这种分配在一个包含内存的节点溢出时回退到
+其他附近的节点。
+
+一些内核分配不希望或不能容忍这种分配回退行为。相反,他们想确保他们从指定的节点获得内存,或者
+得到通知说该节点没有空闲内存。例如,当一个子系统分配每个CPU的内存资源时,通常是这种情况。
+
+一个典型的分配模式是使用内核的numa_node_id()或CPU_to_node()函数获得“当前CPU”所在节点的
+节点ID,然后只从返回的节点ID请求内存。当这样的分配失败时,请求的子系统可以恢复到它自己的回退
+路径。板块内核内存分配器就是这样的一个例子。或者,子系统可以选择在分配失败时禁用或不启用自己。
+内核分析子系统就是这样的一个例子。
+
+如果架构支持——不隐藏无内存节点,那么连接到无内存节点的CPU将总是产生回退路径的开销,或者一些
+子系统如果试图完全从无内存的节点分配内存,将无法初始化。为了透明地支持这种架构,内核子系统可
+以使用numa_mem_id()或cpu_to_mem()函数来定位调用或指定CPU的“本地内存节点”。同样,这是同
+一个节点,默认的本地页分配将从这个节点开始尝试。
diff --git a/Documentation/translations/zh_CN/mm/overcommit-accounting.rst b/Documentation/translations/zh_CN/mm/overcommit-accounting.rst
new file mode 100644 (file)
index 0000000..d8452d8
--- /dev/null
@@ -0,0 +1,86 @@
+:Original: Documentation/mm/overcommit-accounting.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+
+
+==============
+超量使用审计
+==============
+
+Linux内核支持下列超量使用处理模式
+
+0
+       启发式超量使用处理。拒绝明显的地址空间超量使用。用于一个典型的系统。
+       它确保严重的疯狂分配失败,同时允许超量使用以减少swap的使用。在这种模式下,
+       允许root分配稍多的内存。这是默认的。
+1
+       总是超量使用。适用于一些科学应用。经典的例子是使用稀疏数组的代码,只是依赖
+       几乎完全由零页组成的虚拟内存
+
+2
+       不超量使用。系统提交的总地址空间不允许超过swap+一个可配置的物理RAM的数量
+       (默认为50%)。根据你使用的数量,在大多数情况下,这意味着一个进程在访问页面时
+       不会被杀死,但会在内存分配上收到相应的错误。
+
+       对于那些想保证他们的内存分配在未来可用而又不需要初始化每一个页面的应用程序来说
+       是很有用的。
+
+超量使用策略是通过sysctl  `vm.overcommit_memory` 设置的。
+
+可以通过 `vm.overcommit_ratio` (百分比)或 `vm.overcommit_kbytes` (绝对值)
+来设置超限数量。这些只有在 `vm.overcommit_memory` 被设置为2时才有效果。
+
+在 ``/proc/meminfo`` 中可以分别以CommitLimit和Committed_AS的形式查看当前
+的超量使用和提交量。
+
+陷阱
+====
+
+C语言的堆栈增长是一个隐含的mremap。如果你想得到绝对的保证,并在接近边缘的地方运行,
+你 **必须** 为你认为你需要的最大尺寸的堆栈进行mmap。对于典型的堆栈使用来说,这并
+不重要,但如果你真的非常关心的话,这就是一个值得关注的案例。
+
+
+在模式2中,MAP_NORESERVE标志被忽略。
+
+
+它是如何工作的
+==============
+
+超量使用是基于以下规则
+
+对于文件映射
+       | SHARED or READ-only   -       0 cost (该文件是映射而不是交换)
+       | PRIVATE WRITABLE      -       每个实例的映射大小
+
+对于匿名或者 ``/dev/zero`` 映射
+       | SHARED                        -       映射的大小
+       | PRIVATE READ-only     -       0 cost (但作用不大)
+       | PRIVATE WRITABLE      -       每个实例的映射大小
+
+额外的计数
+       | 通过mmap制作可写副本的页面
+       | 从同一池中提取的shmfs内存
+
+状态
+====
+
+*      我们核算mmap内存映射
+*      我们核算mprotect在提交中的变化
+*      我们核算mremap的大小变化
+*      我们的审计 brk
+*      审计munmap
+*      我们在/proc中报告commit 状态
+*      核对并检查分叉的情况
+*      审查堆栈处理/执行中的构建
+*      叙述SHMfs的情况
+*      实现实际限制的执行
+
+待续
+====
+*      ptrace 页计数(这很难)。
diff --git a/Documentation/translations/zh_CN/mm/page_frags.rst b/Documentation/translations/zh_CN/mm/page_frags.rst
new file mode 100644 (file)
index 0000000..20bd3fa
--- /dev/null
@@ -0,0 +1,38 @@
+:Original: Documentation/mm/page_frags.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+
+========
+页面片段
+========
+
+一个页面片段是一个任意长度的任意偏移的内存区域,它位于一个0或更高阶的复合页面中。
+该页中的多个碎片在该页的引用计数器中被单独计算。
+
+page_frag函数,page_frag_alloc和page_frag_free,为页面片段提供了一个简单
+的分配框架。这被网络堆栈和网络设备驱动使用,以提供一个内存的支持区域,作为
+sk_buff->head使用,或者用于skb_shared_info的 “frags” 部分。
+
+为了使用页面片段API,需要一个支持页面片段的缓冲区。这为碎片分配提供了一个中心点,
+并允许多个调用使用一个缓存的页面。这样做的好处是可以避免对get_page的多次调用,
+这在分配时开销可能会很大。然而,由于这种缓存的性质,要求任何对缓存的调用都要受到每
+个CPU的限制,或者每个CPU的限制,并在执行碎片分配时强制禁止中断。
+
+网络堆栈在每个CPU使用两个独立的缓存来处理碎片分配。netdev_alloc_cache被使用
+netdev_alloc_frag和__netdev_alloc_skb调用的调用者使用。napi_alloc_cache
+被调用__napi_alloc_frag和__napi_alloc_skb的调用者使用。这两个调用的主要区别是
+它们可能被调用的环境。“netdev” 前缀的函数可以在任何上下文中使用,因为这些函数
+将禁用中断,而 ”napi“ 前缀的函数只可以在softirq上下文中使用。
+
+许多网络设备驱动程序使用类似的方法来分配页面片段,但页面片段是在环或描述符级别上
+缓存的。为了实现这些情况,有必要提供一种拆解页面缓存的通用方法。出于这个原因,
+__page_frag_cache_drain被实现了。它允许通过一次调用从一个页面释放多个引用。
+这样做的好处是,它允许清理被添加到一个页面的多个引用,以避免每次分配都调用
+get_page。
+
+Alexander Duyck,2016年11月29日。
diff --git a/Documentation/translations/zh_CN/mm/page_migration.rst b/Documentation/translations/zh_CN/mm/page_migration.rst
new file mode 100644 (file)
index 0000000..076081d
--- /dev/null
@@ -0,0 +1,228 @@
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/mm/page_migration.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+========
+页面迁移
+========
+
+页面迁移允许在进程运行时在NUMA系统的节点之间移动页面的物理位置。这意味着进程所看到的虚拟地
+址并没有改变。然而,系统会重新安排这些页面的物理位置。
+
+也可以参见 :ref: `<异构内存管理 (HMM)>` 以了解将页面迁移到设备私有内存或从设备私有内存中迁移。
+
+页面迁移的主要目的是通过将页面移到访问该内存的进程所运行的处理器附近来减少内存访问的延迟。
+
+页面迁移允许进程通过MF_MOVE和MF_MOVE_ALL选项手动重新定位其页面所在的节点,同时通过
+mbind()设置一个新的内存策略。一个进程的页面也可以通过sys_migrate_pages()函数调用从另
+一个进程重新定位。migrate_pages()函数调用接收两组节点,并将一个进程位于旧节点上的页面移
+动到目标节点上。页面迁移功能由Andi Kleen的numactl包提供(需要0.9.3以上的版本,其仓库
+地址https://github.com/numactl/numactl.git)。numactl提供了libnuma,它为页面迁移
+提供了与其他NUMA功能类似的接口。执行 cat ``/proc/<pid>/numa_maps``  允许轻松查看进
+程的页面位置。参见proc(5)手册中的numa_maps文档。
+
+如果调度程序将一个进程重新安置到一个遥远的节点上的处理器,手动迁移是很有用的。批量调度程序
+或管理员可以检测到这种情况,并将进程的页面移到新处理器附近。内核本身只提供手动的页迁移支持。
+自动的页面迁移可以通过用户空间的进程移动页面来实现。一个特殊的函数调用 "move_pages" 允许
+在一个进程中移动单个页面。例如,NUMA分析器可以获得一个显示频繁的节点外访问的日志,并可以使
+用这个结果将页面移动到更有利的位置。
+
+较大型的设备通常使用cpusets将系统分割成若干个节点。Paul Jackson为cpusets配备了当任务被
+转移到另一个cpuset时移动页面的能力(见:ref:`CPUSETS <cpusets>`)。Cpusets允许进程定
+位的自动化。如果一个任务被移到一个新的cpuset上,那么它的所有页面也会随之移动,这样进程的
+性能就不会急剧下降。如果cpuset允许的内存节点发生变化,cpuset中的进程页也会被移动。
+
+页面迁移允许为所有迁移技术保留一组节点中页面的相对位置,这将保留生成的特定内存分配模式即使
+进程已被迁移。为了保留内存延迟,这一点是必要的。迁移后的进程将以类似的性能运行。
+
+页面迁移分几个步骤进行。首先为那些试图从内核中使用migrate_pages()的进程做一个高层次的
+描述(对于用户空间的使用,可以参考上面提到的Andi Kleen的numactl包),然后对低水平的细
+节工作做一个低水平描述。
+
+在内核中使用 migrate_pages()
+============================
+
+1. 从LRU中移除页面。
+
+   要迁移的页面列表是通过扫描页面并把它们移到列表中来生成的。这是通过调用 isolate_lru_page()
+   来完成的。调用isolate_lru_page()增加了对该页的引用,这样在页面迁移发生时它就不会
+   消失。它还可以防止交换器或其他扫描器遇到该页。
+
+
+2. 我们需要有一个new_page_t类型的函数,可以传递给migrate_pages()。这个函数应该计算
+   出如何在给定的旧页面中分配正确的新页面。
+
+3. migrate_pages()函数被调用,它试图进行迁移。它将调用该函数为每个被考虑迁移的页面分
+   配新的页面。
+
+migrate_pages()如何工作
+=======================
+
+migrate_pages()对它的页面列表进行了多次处理。如果当时对一个页面的所有引用都可以被移除,
+那么这个页面就会被移动。该页已经通过isolate_lru_page()从LRU中移除,并且refcount被
+增加,以便在页面迁移发生时不释放该页。
+
+步骤:
+
+1. 锁定要迁移的页面。
+
+2. 确保回写已经完成。
+
+3. 锁定我们要迁移到的新页面。锁定它是为了在迁移过程中立即阻止对这个(尚未更新的)页面的
+   访问。
+
+4. 所有对该页的页表引用都被转换为迁移条目。这就减少了一个页面的mapcount。如果产生的
+   mapcount不是零,那么我们就不迁移该页。所有试图访问该页的用户空间进程现在将等待页
+   面锁或者等待迁移页表项被移除。
+
+5. i_pages的锁被持有。这将导致所有试图通过映射访问该页的进程在自旋锁上阻塞。
+
+6. 检查该页的Refcount,如果还有引用,我们就退出。否则,我们知道我们是唯一引用这个页
+   面的人。
+
+7. 检查基数树,如果它不包含指向这个页面的指针,那么我们就退出,因为其他人修改了基数树。
+
+8. 新的页面要用旧的页面的一些设置进行预处理,这样访问新的页面就会发现一个具有正确设置
+   的页面。
+
+9. 基数树被改变以指向新的页面。
+
+10. 旧页的引用计数被删除,因为地址空间的引用已经消失。对新页的引用被建立,因为新页被
+    地址空间引用。
+
+11. i_pages锁被放弃。这样一来,在映射中的查找又变得可能了。进程将从在锁上自旋到在
+    被锁的新页上睡眠。
+
+12. 页面内容被复制到新的页面上。
+
+13. 剩余的页面标志被复制到新的页面上。
+
+14. 旧的页面标志被清除,以表明该页面不再提供任何信息。
+
+15. 新页面上的回写队列被触发了。
+
+16. 如果迁移条目被插入到页表中,那么就用真正的ptes替换它们。这样做将使那些尚未等待页
+    锁的用户空间进程能够访问。
+
+17. 页面锁从新旧页面上被撤销。等待页锁的进程将重做他们的缺页异常,并将到达新的页面。
+
+18. 新的页面被移到LRU中,可以被交换器等再次扫描。
+
+非LRU页面迁移
+=============
+
+尽管迁移最初的目的是为了减少NUMA的内存访问延迟,但压缩也使用迁移来创建高阶页面。
+
+目前实现的问题是,它被设计为只迁移*LRU*页。然而,有一些潜在的非LRU页面可以在驱动中
+被迁移,例如,zsmalloc,virtio-balloon页面。
+
+对于virtio-balloon页面,迁移代码路径的某些部分已经被钩住,并添加了virtio-balloon
+的特定函数来拦截迁移逻辑。这对一个驱动来说太特殊了,所以其他想让自己的页面可移动的驱
+动就必须在迁移路径中添加自己的特定钩子。
+
+为了克服这个问题,VM支持非LRU页面迁移,它为非LRU可移动页面提供了通用函数,而在迁移
+路径中没有特定的驱动程序钩子。
+
+如果一个驱动程序想让它的页面可移动,它应该定义三个函数,这些函数是
+struct address_space_operations的函数指针。
+
+1. ``bool (*isolate_page) (struct page *page, isolate_mode_t mode);``
+
+   VM对驱动的isolate_page()函数的期望是,如果驱动成功隔离了该页,则返回*true*。
+   返回true后,VM会将该页标记为PG_isolated,这样多个CPU的并发隔离就会跳过该
+   页进行隔离。如果驱动程序不能隔离该页,它应该返回*false*。
+
+   一旦页面被成功隔离,VM就会使用page.lru字段,因此驱动程序不应期望保留这些字段的值。
+
+2. ``int (*migratepage) (struct address_space *mapping,``
+|      ``struct page *newpage, struct page *oldpage, enum migrate_mode);``
+
+   隔离后,虚拟机用隔离的页面调用驱动的migratepage()。migratepage()的功能是将旧页
+   的内容移动到新页,并设置struct page newpage的字段。请记住,如果你成功迁移了旧页
+   并返回MIGRATEPAGE_SUCCESS,你应该通过page_lock下的__ClearPageMovable()向虚
+   拟机表明旧页不再可移动。如果驱动暂时不能迁移该页,驱动可以返回-EAGAIN。在-EAGAIN
+   时,VM会在短时间内重试页面迁移,因为VM将-EAGAIN理解为 "临时迁移失败"。在返回除
+   -EAGAIN以外的任何错误时,VM将放弃页面迁移而不重试。
+
+   在migratepage()函数中,驱动程序不应该接触page.lru字段。
+
+3. ``void (*putback_page)(struct page *);``
+
+   如果在隔离页上迁移失败,VM应该将隔离页返回给驱动,因此VM用隔离页调用驱动的
+   putback_page()。在这个函数中,驱动应该把隔离页放回自己的数据结构中。
+
+非LRU可移动页标志
+
+   有两个页面标志用于支持非LRU可移动页面。
+
+   * PG_movable
+
+     驱动应该使用下面的函数来使页面在page_lock下可移动。::
+
+       void __SetPageMovable(struct page *page, struct address_space *mapping)
+
+     它需要address_space的参数来注册将被VM调用的migration family函数。确切地说,
+     PG_movable不是struct page的一个真正的标志。相反,VM复用了page->mapping的低
+     位来表示它::
+
+       #define PAGE_MAPPING_MOVABLE 0x2
+       page->mapping = page->mapping | PAGE_MAPPING_MOVABLE;
+
+     所以驱动不应该直接访问page->mapping。相反,驱动应该使用page_mapping(),它可
+     以在页面锁下屏蔽掉page->mapping的低2位,从而获得正确的struct address_space。
+
+     对于非LRU可移动页面的测试,VM支持__PageMovable()函数。然而,它并不能保证识别
+     非LRU可移动页面,因为page->mapping字段与struct page中的其他变量是统一的。如
+     果驱动程序在被虚拟机隔离后释放了页面,尽管page->mapping设置了PAGE_MAPPING_MOVABLE,
+     但它并没有一个稳定的值(看看__ClearPageMovable)。但是__PageMovable()在页
+     面被隔离后,无论页面是LRU还是非LRU可移动的,调用它开销都很低,因为LRU页面在
+     page->mapping中不可能有PAGE_MAPPING_MOVABLE设置。在用pfn扫描中的lock_page()
+     进行更大开销的检查来选择受害者之前,它也很适合只是瞥一眼来测试非LRU可移动的页面。
+
+     为了保证非LRU的可移动页面,VM提供了PageMovable()函数。与__PageMovable()不
+     同,PageMovable()在lock_page()下验证page->mapping和
+     mapping->a_ops->isolate_page。lock_page()可以防止突然破坏page->mapping。
+
+     使用__SetPageMovable()的驱动应该在释放页面之前通过page_lock()下的
+     __ClearMovablePage()清除该标志。
+
+   * PG_isolated
+
+     为了防止几个CPU同时进行隔离,VM在lock_page()下将隔离的页面标记为PG_isolated。
+     因此,如果一个CPU遇到PG_isolated非LRU可移动页面,它可以跳过它。驱动程序不需要
+     操作这个标志,因为VM会自动设置/清除它。请记住,如果驱动程序看到PG_isolated页,
+     这意味着该页已经被VM隔离,所以它不应该碰page.lru字段。PG_isolated标志与
+     PG_reclaim标志是同义的,所以驱动程序不应该为自己的目的使用PG_isolated。
+
+监测迁移
+========
+
+以下事件(计数器)可用于监控页面迁移。
+
+1. PGMIGRATE_SUCCESS: 正常的页面迁移成功。每个计数器意味着一个页面被迁移了。如果该
+   页是一个非THP和非hugetlb页,那么这个计数器会增加1。如果该页面是一个THP或hugetlb
+   页面,那么这个计数器会随着THP或hugetlb子页面的数量而增加。例如,迁移一个有4KB大小
+   的基础页(子页)的2MB THP,将导致这个计数器增加512。
+
+2. PGMIGRATE_FAIL: 正常的页面迁移失败。与上面PGMIGRATE_SUCCESS的计数规则相同:如
+   果是THP或hugetlb,这个计数将被子页的数量增加。
+
+3. THP_MIGRATION_SUCCESS: 一个THP被迁移而没有被分割。
+
+4. THP_MIGRATION_FAIL: 一个THP不能被迁移,也不能被分割。
+
+5. THP_MIGRATION_SPLIT: 一个THP被迁移了,但不是这样的:首先,这个THP必须被分割。
+   在拆分之后,对它的子页面进行了迁移重试。
+
+THP_MIGRATION_* 事件也会更新相应的PGMIGRATE_SUCCESS或PGMIGRATE_FAIL事件。
+例如,一个THP迁移失败将导致THP_MIGRATION_FAIL和PGMIGRATE_FAIL增加。
+
+Christoph Lameter,2006年5月8日。
+
+Minchan Kim,2016年3月28日。
diff --git a/Documentation/translations/zh_CN/mm/page_owner.rst b/Documentation/translations/zh_CN/mm/page_owner.rst
new file mode 100644 (file)
index 0000000..b7f81d7
--- /dev/null
@@ -0,0 +1,177 @@
+:Original: Documentation/mm/page_owner.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+
+================================
+page owner: 跟踪谁分配的每个页面
+================================
+
+概述
+====
+
+page owner是用来追踪谁分配的每一个页面。它可以用来调试内存泄漏或找到内存占用者。
+当分配发生时,有关分配的信息,如调用堆栈和页面的顺序被存储到每个页面的特定存储中。
+当我们需要了解所有页面的状态时,我们可以获得并分析这些信息。
+
+尽管我们已经有了追踪页面分配/释放的tracepoint,但用它来分析谁分配的每个页面是
+相当复杂的。我们需要扩大跟踪缓冲区,以防止在用户空间程序启动前出现重叠。而且,启
+动的程序会不断地将跟踪缓冲区转出,供以后分析,这将会改变系统的行为,会产生更多的
+可能性,而不是仅仅保留在内存中,所以不利于调试。
+
+页面所有者也可以用于各种目的。例如,可以通过每个页面的gfp标志信息获得精确的碎片
+统计。如果启用了page owner,它就已经实现并激活了。我们非常欢迎其他用途。
+
+page owner在默认情况下是禁用的。所以,如果你想使用它,你需要在你的启动cmdline
+中加入"page_owner=on"。如果内核是用page owner构建的,并且由于没有启用启动
+选项而在运行时禁用page owner,那么运行时的开销是很小的。如果在运行时禁用,它不
+需要内存来存储所有者信息,所以没有运行时内存开销。而且,页面所有者在页面分配器的
+热路径中只插入了两个不可能的分支,如果不启用,那么分配就会像没有页面所有者的内核
+一样进行。这两个不可能的分支应该不会影响到分配的性能,特别是在静态键跳转标签修补
+功能可用的情况下。以下是由于这个功能而导致的内核代码大小的变化。
+
+- 没有page owner::
+
+   text    data     bss     dec     hex filename
+   48392   2333     644   51369    c8a9 mm/page_alloc.o
+
+- 有page owner::
+
+   text    data     bss     dec     hex filename
+   48800   2445     644   51889    cab1 mm/page_alloc.o
+   6662     108      29    6799    1a8f mm/page_owner.o
+   1025       8       8    1041     411 mm/page_ext.o
+
+虽然总共增加了8KB的代码,但page_alloc.o增加了520字节,其中不到一半是在hotpath
+中。构建带有page owner的内核,并在需要时打开它,将是调试内核内存问题的最佳选择。
+
+有一个问题是由实现细节引起的。页所有者将信息存储到struct page扩展的内存中。这
+个内存的初始化时间比稀疏内存系统中的页面分配器启动的时间要晚一些,所以,在初始化
+之前,许多页面可以被分配,但它们没有所有者信息。为了解决这个问题,这些早期分配的
+页面在初始化阶段被调查并标记为分配。虽然这并不意味着它们有正确的所有者信息,但至
+少,我们可以更准确地判断该页是否被分配。在2GB内存的x86-64虚拟机上,有13343
+个早期分配的页面被捕捉和标记,尽管它们大部分是由结构页扩展功能分配的。总之,在这
+之后,没有任何页面处于未追踪状态。
+
+使用方法
+========
+
+1) 构建用户空间的帮助::
+
+       cd tools/vm
+       make page_owner_sort
+
+2) 启用page owner: 添加 "page_owner=on" 到 boot cmdline.
+
+3) 做你想调试的工作。
+
+4) 分析来自页面所有者的信息::
+
+       cat /sys/kernel/debug/page_owner > page_owner_full.txt
+       ./page_owner_sort page_owner_full.txt sorted_page_owner.txt
+
+   ``page_owner_full.txt`` 的一般输出情况如下(输出信息无翻译价值)::
+
+       Page allocated via order XXX, ...
+       PFN XXX ...
+       // Detailed stack
+
+       Page allocated via order XXX, ...
+       PFN XXX ...
+       // Detailed stack
+
+   ``page_owner_sort`` 工具忽略了 ``PFN`` 行,将剩余的行放在buf中,使用regexp提
+   取页序值,计算buf的次数和页数,最后根据参数进行排序。
+
+   在 ``sorted_page_owner.txt`` 中可以看到关于谁分配了每个页面的结果。一般输出::
+
+       XXX times, XXX pages:
+       Page allocated via order XXX, ...
+       // Detailed stack
+
+   默认情况下, ``page_owner_sort`` 是根据buf的时间来排序的。如果你想
+   按buf的页数排序,请使用-m参数。详细的参数是:
+
+   基本函数::
+
+       排序:
+               -a              按内存分配时间排序
+               -m              按总内存排序
+               -p              按pid排序。
+               -P              按tgid排序。
+               -n              按任务命令名称排序。
+               -r              按内存释放时间排序。
+               -s              按堆栈跟踪排序。
+               -t              按时间排序(默认)。
+       --sort <order> 指定排序顺序。排序的语法是[+|-]key[,[+|-]key[,...]]。从
+       **标准格式指定器**那一节选择一个键。"+"是可选的,因为默认的方向是数字或
+       词法的增加。允许混合使用缩写和完整格式的键。
+
+        例子:
+                               ./page_owner_sort <input> <output> --sort=n,+pid,-tgid
+                               ./page_owner_sort <input> <output> --sort=at
+
+    其它函数::
+
+       剔除:
+               --cull <rules>
+                       指定剔除规则。剔除的语法是key[,key[,...]]。从**标准格式指定器**
+                               部分选择一个多字母键。
+               <rules>是一个以逗号分隔的列表形式的单一参数,它提供了一种指定单个剔除规则的
+               方法。 识别的关键字在下面的**标准格式指定器**部分有描述。<规则>可以通过键的
+               序列k1,k2,...来指定,在下面的标准排序键部分有描述。允许混合使用简写和完整形
+               式的键。
+
+               Examples:
+                               ./page_owner_sort <input> <output> --cull=stacktrace
+                               ./page_owner_sort <input> <output> --cull=st,pid,name
+                               ./page_owner_sort <input> <output> --cull=n,f
+
+       过滤:
+               -f              过滤掉内存已被释放的块的信息。
+
+       选择:
+               --pid <pidlist>         按pid选择。这将选择进程ID号出现在<pidlist>中的块。
+               --tgid <tgidlist>       按tgid选择。这将选择其线程组ID号出现在<tgidlist>
+                                   中的块。
+               --name <cmdlist>        按任务命令名称选择。这将选择其任务命令名称出现在
+                                   <cmdlist>中的区块。
+
+               <pidlist>, <tgidlist>, <cmdlist>是以逗号分隔的列表形式的单个参数,
+               它提供了一种指定单个选择规则的方法。
+
+
+               例子:
+                               ./page_owner_sort <input> <output> --pid=1
+                               ./page_owner_sort <input> <output> --tgid=1,2,3
+                               ./page_owner_sort <input> <output> --name name1,name2
+
+标准格式指定器
+==============
+::
+
+  --sort的选项:
+
+       短键          长键          描述
+       p               pid             进程ID
+       tg              tgid            线程组ID
+       n               name            任务命令名称
+       st              stacktrace      页面分配的堆栈跟踪
+       T               txt             块的全文
+       ft              free_ts         页面释放时的时间戳
+       at              alloc_ts        页面被分配时的时间戳
+       ator            allocator       页面的内存分配器
+
+  --curl的选项:
+
+       短键          长键          描述
+       p               pid             进程ID
+       tg              tgid            线程组ID
+       n               name            任务命令名称
+       f               free            该页是否已经释放
+       st              stacktrace      页面分配的堆栈跟踪
+       ator            allocator       页面的内存分配器
diff --git a/Documentation/translations/zh_CN/mm/page_table_check.rst b/Documentation/translations/zh_CN/mm/page_table_check.rst
new file mode 100644 (file)
index 0000000..e807731
--- /dev/null
@@ -0,0 +1,56 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+:Original: Documentation/mm/page_table_check.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+
+========
+页表检查
+========
+
+概述
+====
+
+页表检查允许通过确保防止某些类型的内存损坏来强化内核。
+
+当新的页面可以从用户空间访问时,页表检查通过将它们的页表项(PTEs PMD等)添加到页表中来执行额外
+的验证。
+
+在检测到损坏的情况下,内核会被崩溃。页表检查有一个小的性能和内存开销。因此,它在默认情况下是禁用
+的,但是在额外的加固超过性能成本的系统上,可以选择启用。另外,由于页表检查是同步的,它可以帮助调
+试双映射内存损坏问题,在错误的映射发生时崩溃内核,而不是在内存损坏错误发生后内核崩溃。
+
+双重映射检测逻辑
+================
+
++-------------------+-------------------+-------------------+------------------+
+| Current Mapping   | New mapping       | Permissions       | Rule             |
++===================+===================+===================+==================+
+| Anonymous         | Anonymous         | Read              | Allow            |
++-------------------+-------------------+-------------------+------------------+
+| Anonymous         | Anonymous         | Read / Write      | Prohibit         |
++-------------------+-------------------+-------------------+------------------+
+| Anonymous         | Named             | Any               | Prohibit         |
++-------------------+-------------------+-------------------+------------------+
+| Named             | Anonymous         | Any               | Prohibit         |
++-------------------+-------------------+-------------------+------------------+
+| Named             | Named             | Any               | Allow            |
++-------------------+-------------------+-------------------+------------------+
+
+启用页表检查
+============
+
+用以下方法构建内核:
+
+- PAGE_TABLE_CHECK=y
+  注意,它只能在ARCH_SUPPORTS_PAGE_TABLE_CHECK可用的平台上启用。
+
+- 使用 "page_table_check=on" 内核参数启动。
+
+可以选择用PAGE_TABLE_CHECK_ENFORCED来构建内核,以便在没有额外的内核参数的情况下获得页表
+支持。
diff --git a/Documentation/translations/zh_CN/mm/remap_file_pages.rst b/Documentation/translations/zh_CN/mm/remap_file_pages.rst
new file mode 100644 (file)
index 0000000..31e0c54
--- /dev/null
@@ -0,0 +1,32 @@
+:Original: Documentation/mm/remap_file_pages.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+
+==============================
+remap_file_pages()系统调用
+==============================
+
+remap_file_pages()系统调用被用来创建一个非线性映射,也就是说,在这个映射中,
+文件的页面被无序映射到内存中。使用remap_file_pages()比重复调用mmap(2)的好
+处是,前者不需要内核创建额外的VMA(虚拟内存区)数据结构。
+
+支持非线性映射需要在内核虚拟内存子系统中编写大量的non-trivial的代码,包括热
+路径。另外,为了使非线性映射工作,内核需要一种方法来区分正常的页表项和带有文件
+偏移的项(pte_file)。内核为达到这个目的在PTE中保留了标志。PTE标志是稀缺资
+源,特别是在某些CPU架构上。如果能腾出这个标志用于其他用途就更好了。
+
+幸运的是,在生活中并没有很多remap_file_pages()的用户。只知道有一个企业的RDBMS
+实现在32位系统上使用这个系统调用来映射比32位虚拟地址空间线性尺寸更大的文件。
+由于64位系统的广泛使用,这种使用情况已经不重要了。
+
+syscall被废弃了,现在用一个模拟来代替它。仿真会创建新的VMA,而不是非线性映射。
+对于remap_file_pages()的少数用户来说,它的工作速度会变慢,但ABI被保留了。
+
+仿真的一个副作用(除了性能之外)是,由于额外的VMA,用户可以更容易达到
+vm.max_map_count的限制。关于限制的更多细节,请参见DEFAULT_MAX_MAP_COUNT
+的注释。
diff --git a/Documentation/translations/zh_CN/mm/split_page_table_lock.rst b/Documentation/translations/zh_CN/mm/split_page_table_lock.rst
new file mode 100644 (file)
index 0000000..4fb7aa6
--- /dev/null
@@ -0,0 +1,96 @@
+:Original: Documentation/mm/split_page_table_lock.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+
+=================================
+分页表锁(split page table lock)
+=================================
+
+最初,mm->page_table_lock spinlock保护了mm_struct的所有页表。但是这种方
+法导致了多线程应用程序的缺页异常可扩展性差,因为对锁的争夺很激烈。为了提高可扩
+展性,我们引入了分页表锁。
+
+有了分页表锁,我们就有了单独的每张表锁来顺序化对表的访问。目前,我们对PTE和
+PMD表使用分页锁。对高层表的访问由mm->page_table_lock保护。
+
+有一些辅助工具来锁定/解锁一个表和其他访问器函数:
+
+ - pte_offset_map_lock()
+       映射pte并获取PTE表锁,返回所取锁的指针;
+ - pte_unmap_unlock()
+       解锁和解映射PTE表;
+ - pte_alloc_map_lock()
+       如果需要的话,分配PTE表并获取锁,如果分配失败,返回已获取的锁的指针
+       或NULL;
+ - pte_lockptr()
+       返回指向PTE表锁的指针;
+ - pmd_lock()
+       取得PMD表锁,返回所取锁的指针。
+ - pmd_lockptr()
+       返回指向PMD表锁的指针;
+
+如果CONFIG_SPLIT_PTLOCK_CPUS(通常为4)小于或等于NR_CPUS,则在编译
+时启用PTE表的分页表锁。如果分页锁被禁用,所有的表都由mm->page_table_lock
+来保护。
+
+如果PMD表启用了分页锁,并且架构支持它,那么PMD表的分页锁就会被启用(见
+下文)。
+
+Hugetlb 和分页表锁
+==================
+
+Hugetlb可以支持多种页面大小。我们只对PMD级别使用分页锁,但不对PUD使用。
+
+Hugetlb特定的辅助函数:
+
+ - huge_pte_lock()
+       对PMD_SIZE页面采取pmd分割锁,否则mm->page_table_lock;
+ - huge_pte_lockptr()
+       返回指向表锁的指针。
+
+架构对分页表锁的支持
+====================
+
+没有必要特别启用PTE分页表锁:所有需要的东西都由pgtable_pte_page_ctor()
+和pgtable_pte_page_dtor()完成,它们必须在PTE表分配/释放时被调用。
+
+确保架构不使用slab分配器来分配页表:slab使用page->slab_cache来分配其页
+面。这个区域与page->ptl共享存储。
+
+PMD分页锁只有在你有两个以上的页表级别时才有意义。
+
+启用PMD分页锁需要在PMD表分配时调用pgtable_pmd_page_ctor(),在释放时调
+用pgtable_pmd_page_dtor()。
+
+分配通常发生在pmd_alloc_one()中,释放发生在pmd_free()和pmd_free_tlb()
+中,但要确保覆盖所有的PMD表分配/释放路径:即X86_PAE在pgd_alloc()中预先
+分配一些PMD。
+
+一切就绪后,你可以设置CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK。
+
+注意:pgtable_pte_page_ctor()和pgtable_pmd_page_ctor()可能失败--必
+须正确处理。
+
+page->ptl
+=========
+
+page->ptl用于访问分割页表锁,其中'page'是包含该表的页面struct page。它
+与page->private(以及union中的其他几个字段)共享存储。
+
+为了避免增加struct page的大小并获得最佳性能,我们使用了一个技巧:
+
+ - 如果spinlock_t适合于long,我们使用page->ptr作为spinlock,这样我们
+   就可以避免间接访问并节省一个缓存行。
+ - 如果spinlock_t的大小大于long的大小,我们使用page->ptl作为spinlock_t
+   的指针并动态分配它。这允许在启用DEBUG_SPINLOCK或DEBUG_LOCK_ALLOC的
+   情况下使用分页锁,但由于间接访问而多花了一个缓存行。
+
+PTE表的spinlock_t分配在pgtable_pte_page_ctor()中,PMD表的spinlock_t
+分配在pgtable_pmd_page_ctor()中。
+
+请不要直接访问page->ptl - -使用适当的辅助函数。
diff --git a/Documentation/translations/zh_CN/mm/vmalloced-kernel-stacks.rst b/Documentation/translations/zh_CN/mm/vmalloced-kernel-stacks.rst
new file mode 100644 (file)
index 0000000..d02a23f
--- /dev/null
@@ -0,0 +1,133 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/mm/vmalloced-kernel-stacks.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+====================
+支持虚拟映射的内核栈
+====================
+
+:作者: Shuah Khan <skhan@linuxfoundation.org>
+
+.. contents:: :local:
+
+概览
+----
+
+这是介绍 `虚拟映射内核栈功能 <https://lwn.net/Articles/694348/>` 的代码
+和原始补丁系列的信息汇总。
+
+简介
+----
+
+内核堆栈溢出通常难以调试,并使内核容易被(恶意)利用。问题可能在稍后的时间出现,使其难以
+隔离和究其根本原因。
+
+带有保护页的虚拟映射内核堆栈如果溢出,会被立即捕获,而不会放任其导致难以诊断的损
+坏。
+
+HAVE_ARCH_VMAP_STACK和VMAP_STACK配置选项能够支持带有保护页的虚拟映射堆栈。
+当堆栈溢出时,这个特性会引发可靠的异常。溢出后堆栈跟踪的可用性以及对溢出本身的
+响应取决于架构。
+
+.. note::
+        截至本文撰写时, arm64, powerpc, riscv, s390, um, 和 x86 支持VMAP_STACK。
+
+HAVE_ARCH_VMAP_STACK
+--------------------
+
+能够支持虚拟映射内核栈的架构应该启用这个bool配置选项。要求是:
+
+- vmalloc空间必须大到足以容纳许多内核堆栈。这可能排除了许多32位架构。
+- vmalloc空间的堆栈需要可靠地工作。例如,如果vmap页表是按需创建的,当堆栈指向
+  具有未填充页表的虚拟地址时,这种机制需要工作,或者架构代码(switch_to()和
+  switch_mm(),很可能)需要确保堆栈的页表项在可能未填充的堆栈上运行之前已经填
+  充。
+- 如果堆栈溢出到一个保护页,就应该发生一些合理的事情。“合理”的定义是灵活的,但
+  在没有记录任何东西的情况下立即重启是不友好的。
+
+VMAP_STACK
+----------
+
+VMAP_STACK bool配置选项在启用时分配虚拟映射的任务栈。这个选项依赖于
+HAVE_ARCH_VMAP_STACK。
+
+- 如果你想使用带有保护页的虚拟映射的内核堆栈,请启用该选项。这将导致内核栈溢出
+  被立即捕获,而不是难以诊断的损坏。
+
+.. note::
+
+        使用KASAN的这个功能需要架构支持用真实的影子内存来支持虚拟映射,并且
+        必须启用KASAN_VMALLOC。
+
+.. note::
+
+        启用VMAP_STACK时,无法在堆栈分配的数据上运行DMA。
+
+内核配置选项和依赖性不断变化。请参考最新的代码库:
+
+`Kconfig <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/Kconfig>`
+
+分配方法
+--------
+
+当一个新的内核线程被创建时,线程堆栈是由页级分配器分配的虚拟连续的内存页组成。这
+些页面被映射到有PAGE_KERNEL保护的连续的内核虚拟空间。
+
+alloc_thread_stack_node()调用__vmalloc_node_range()来分配带有PAGE_KERNEL
+保护的栈。
+
+- 分配的堆栈被缓存起来,以后会被新的线程重用,所以在分配/释放堆栈给任务时,要手动
+  进行memcg核算。因此,__vmalloc_node_range被调用时没有__GFP_ACCOUNT。
+- vm_struct被缓存起来,以便能够找到在中断上下文中启动的空闲线程。 free_thread_stack()
+  可以在中断上下文中调用。
+- 在arm64上,所有VMAP的堆栈都需要有相同的对齐方式,以确保VMAP的堆栈溢出检测正常
+  工作。架构特定的vmap堆栈分配器照顾到了这个细节。
+- 这并不涉及中断堆栈--参考原始补丁
+
+线程栈分配是由clone()、fork()、vfork()、kernel_thread()通过kernel_clone()
+启动的。留点提示在这,以便搜索代码库,了解线程栈何时以及如何分配。
+
+大量的代码是在:
+`kernel/fork.c <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/fork.c>`.
+
+task_struct中的stack_vm_area指针可以跟踪虚拟分配的堆栈,一个非空的stack_vm_area
+指针可以表明虚拟映射的内核堆栈已经启用。
+
+::
+
+        struct vm_struct *stack_vm_area;
+
+堆栈溢出处理
+------------
+
+前守护页和后守护页有助于检测堆栈溢出。当堆栈溢出到守护页时,处理程序必须小心不要再
+次溢出堆栈。当处理程序被调用时,很可能只留下很少的堆栈空间。
+
+在x86上,这是通过处理表明内核堆栈溢出的双异常堆栈的缺页异常来实现的。
+
+用守护页测试VMAP分配
+--------------------
+
+我们如何确保VMAP_STACK在分配时确实有前守护页和后守护页的保护?下面的 lkdtm 测试
+可以帮助检测任何回归。
+
+::
+
+        void lkdtm_STACK_GUARD_PAGE_LEADING()
+        void lkdtm_STACK_GUARD_PAGE_TRAILING()
+
+结论
+----
+
+- vmalloced堆栈的percpu缓存似乎比高阶堆栈分配要快一些,至少在缓存命中时是这样。
+- THREAD_INFO_IN_TASK完全摆脱了arch-specific thread_info,并简单地将
+  thread_info(仅包含标志)和'int cpu'嵌入task_struct中。
+- 一旦任务死亡,线程栈就可以被释放(无需等待RCU),然后,如果使用vmapped栈,就
+  可以将整个栈缓存起来,以便在同一cpu上重复使用。
diff --git a/Documentation/translations/zh_CN/mm/z3fold.rst b/Documentation/translations/zh_CN/mm/z3fold.rst
new file mode 100644 (file)
index 0000000..9569a6d
--- /dev/null
@@ -0,0 +1,31 @@
+:Original: Documentation/mm/z3fold.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+
+======
+z3fold
+======
+
+z3fold是一个专门用于存储压缩页的分配器。它被设计为每个物理页最多可以存储三个压缩页。
+它是zbud的衍生物,允许更高的压缩率,保持其前辈的简单性和确定性。
+
+z3fold和zbud的主要区别是:
+
+* 与zbud不同的是,z3fold允许最大的PAGE_SIZE分配。
+* z3fold在其页面中最多可以容纳3个压缩页面
+* z3fold本身没有输出任何API,因此打算通过zpool的API来使用
+
+为了保持确定性和简单性,z3fold,就像zbud一样,总是在每页存储一个整数的压缩页,但是
+它最多可以存储3页,不像zbud最多可以存储2页。因此压缩率达到2.7倍左右,而zbud的压缩
+率是1.7倍左右。
+
+不像zbud(但也像zsmalloc),z3fold_alloc()那样不返回一个可重复引用的指针。相反,它
+返回一个无符号长句柄,它编码了被分配对象的实际位置。
+
+保持有效的压缩率接近于zsmalloc,z3fold不依赖于MMU的启用,并提供更可预测的回收行
+为,这使得它更适合于小型和反应迅速的系统。
diff --git a/Documentation/translations/zh_CN/mm/zsmalloc.rst b/Documentation/translations/zh_CN/mm/zsmalloc.rst
new file mode 100644 (file)
index 0000000..4c8c9b1
--- /dev/null
@@ -0,0 +1,78 @@
+:Original: Documentation/mm/zsmalloc.rst
+
+:翻译:
+
+ 司延腾 Yanteng Si <siyanteng@loongson.cn>
+
+:校译:
+
+========
+zsmalloc
+========
+
+这个分配器是为与zram一起使用而设计的。因此,该分配器应该在低内存条件下工作良好。特别是,
+它从未尝试过higher order页面的分配,这在内存压力下很可能会失败。另一方面,如果我们只
+是使用单(0-order)页,它将遭受非常高的碎片化 - 任何大小为PAGE_SIZE/2或更大的对象将
+占据整个页面。这是其前身(xvmalloc)的主要问题之一。
+
+为了克服这些问题,zsmalloc分配了一堆0-order页面,并使用各种"struct page"字段将它
+们链接起来。这些链接的页面作为一个单一的higher order页面,即一个对象可以跨越0-order
+页面的边界。代码将这些链接的页面作为一个实体,称为zspage。
+
+为了简单起见,zsmalloc只能分配大小不超过PAGE_SIZE的对象,因为这满足了所有当前用户的
+要求(在最坏的情况下,页面是不可压缩的,因此以"原样"即未压缩的形式存储)。对于大于这
+个大小的分配请求,会返回失败(见zs_malloc)。
+
+此外,zs_malloc()并不返回一个可重复引用的指针。相反,它返回一个不透明的句柄(无符号
+长),它编码了被分配对象的实际位置。这种间接性的原因是zsmalloc并不保持zspages的永久
+映射,因为这在32位系统上会导致问题,因为内核空间映射的VA区域非常小。因此,在使用分配
+的内存之前,对象必须使用zs_map_object()进行映射以获得一个可用的指针,随后使用
+zs_unmap_object()解除映射。
+
+stat
+====
+
+通过CONFIG_ZSMALLOC_STAT,我们可以通过 ``/sys/kernel/debug/zsmalloc/<user name>``
+看到zsmalloc内部信息。下面是一个统计输出的例子。::
+
+ # cat /sys/kernel/debug/zsmalloc/zram0/classes
+
+ class  size almost_full almost_empty obj_allocated   obj_used pages_used pages_per_zspage
+    ...
+    ...
+     9   176           0            1           186        129          8                4
+    10   192           1            0          2880       2872        135                3
+    11   208           0            1           819        795         42                2
+    12   224           0            1           219        159         12                4
+    ...
+    ...
+
+
+class
+       索引
+size
+       zspage存储对象大小
+almost_empty
+       ZS_ALMOST_EMPTY zspage的数量(见下文)。
+almost_full
+       ZS_ALMOST_FULL zspage的数量(见下图)
+obj_allocated
+       已分配对象的数量
+obj_used
+       分配给用户的对象的数量
+pages_used
+       为该类分配的页数
+pages_per_zspage
+       组成一个zspage的0-order页面的数量
+
+当n <= N / f时,我们将一个zspage分配给ZS_ALMOST_EMPTYfullness组,其中
+
+* n = 已分配对象的数量
+* N = zspage可以存储的对象总数
+* f = fullness_threshold_frac(即,目前是4个)
+
+同样地,我们将zspage分配给:
+
+* ZS_ALMOST_FULL  when n > N / f
+* ZS_EMPTY        when n == 0
+* ZS_FULL         when n == N
diff --git a/Documentation/translations/zh_CN/vm/active_mm.rst b/Documentation/translations/zh_CN/vm/active_mm.rst
deleted file mode 100644 (file)
index 366609e..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-.. include:: ../disclaimer-zh_CN.rst
-
-:Original: Documentation/vm/active_mm.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-
-=========
-Active MM
-=========
-
-这是一封linux之父回复开发者的一封邮件,所以翻译时我尽量保持邮件格式的完整。
-
-::
-
- List:       linux-kernel
- Subject:    Re: active_mm
- From:       Linus Torvalds <torvalds () transmeta ! com>
- Date:       1999-07-30 21:36:24
-
- 因为我并不经常写解释,所以已经抄送到linux-kernel邮件列表,而当我做这些,
- 且更多的人在阅读它们时,我觉得棒极了。
-
- 1999年7月30日 星期五, David Mosberger 写道:
- >
- > 是否有一个简短的描述,说明task_struct中的
- >  "mm" 和 "active_mm"应该如何使用? (如果
- > 这个问题在邮件列表中讨论过,我表示歉意--我刚
- > 刚度假回来,有一段时间没能关注linux-kernel了)。
-
- 基本上,新的设定是:
-
-  - 我们有“真实地址空间”和“匿名地址空间”。区别在于,匿名地址空间根本不关心用
-    户级页表,所以当我们做上下文切换到匿名地址空间时,我们只是让以前的地址空间
-    处于活动状态。
-
-    一个“匿名地址空间”的明显用途是任何不需要任何用户映射的线程--所有的内核线
-    程基本上都属于这一类,但即使是“真正的”线程也可以暂时说在一定时间内它们不
-    会对用户空间感兴趣,调度器不妨试着避免在切换VM状态上浪费时间。目前只有老
-    式的bdflush sync能做到这一点。
-
-  - “tsk->mm” 指向 “真实地址空间”。对于一个匿名进程来说,tsk->mm将是NULL,
-    其逻辑原因是匿名进程实际上根本就 “没有” 真正的地址空间。
-
-  - 然而,我们显然需要跟踪我们为这样的匿名用户“偷用”了哪个地址空间。为此,我们
-    有 “tsk->active_mm”,它显示了当前活动的地址空间是什么。
-
-    规则是,对于一个有真实地址空间的进程(即tsk->mm是 non-NULL),active_mm
-    显然必须与真实的mm相同。
-
-    对于一个匿名进程,tsk->mm == NULL,而tsk->active_mm是匿名进程运行时
-    “借用”的mm。当匿名进程被调度走时,借用的地址空间被返回并清除。
-
- 为了支持所有这些,“struct mm_struct”现在有两个计数器:一个是 “mm_users”
- 计数器,即有多少 “真正的地址空间用户”,另一个是 “mm_count”计数器,即 “lazy”
- 用户(即匿名用户)的数量,如果有任何真正的用户,则加1。
-
- 通常情况下,至少有一个真正的用户,但也可能是真正的用户在另一个CPU上退出,而
- 一个lazy的用户仍在活动,所以你实际上得到的情况是,你有一个地址空间 **只**
- 被lazy的用户使用。这通常是一个短暂的生命周期状态,因为一旦这个线程被安排给一
- 个真正的线程,这个 “僵尸” mm就会被释放,因为 “mm_count”变成了零。
-
- 另外,一个新的规则是,**没有人** 再把 “init_mm” 作为一个真正的MM了。
- “init_mm”应该被认为只是一个 “没有其他上下文时的lazy上下文”,事实上,它主
- 要是在启动时使用,当时还没有真正的VM被创建。因此,用来检查的代码
-
-   if (current->mm == &init_mm)
-
- 一般来说,应该用
-
-   if (!current->mm)
-
- 取代上面的写法(这更有意义--测试基本上是 “我们是否有一个用户环境”,并且通常
- 由缺页异常处理程序和类似的东西来完成)。
-
- 总之,我刚才在ftp.kernel.org上放了一个pre-patch-2.3.13-1,因为它稍微改
- 变了接口以适配alpha(谁会想到呢,但alpha体系结构上下文切换代码实际上最终是
- 最丑陋的之一--不像其他架构的MM和寄存器状态是分开的,alpha的PALcode将两者
- 连接起来,你需要同时切换两者)。
-
- (文档来源 http://marc.info/?l=linux-kernel&m=93337278602211&w=2)
diff --git a/Documentation/translations/zh_CN/vm/balance.rst b/Documentation/translations/zh_CN/vm/balance.rst
deleted file mode 100644 (file)
index e98a47e..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-.. include:: ../disclaimer-zh_CN.rst
-
-:Original: Documentation/vm/balance.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-
-========
-内存平衡
-========
-
-2000年1月开始,作者:Kanoj Sarcar <kanoj@sgi.com>
-
-对于 !__GFP_HIGH 和 !__GFP_KSWAPD_RECLAIM 以及非 __GFP_IO 的分配,需要进行
-内存平衡。
-
-调用者避免回收的第一个原因是调用者由于持有自旋锁或处于中断环境中而无法睡眠。第二个
-原因可能是,调用者愿意在不产生页面回收开销的情况下分配失败。这可能发生在有0阶回退
-选项的机会主义高阶分配请求中。在这种情况下,调用者可能也希望避免唤醒kswapd。
-
-__GFP_IO分配请求是为了防止文件系统死锁。
-
-在没有非睡眠分配请求的情况下,做平衡似乎是有害的。页面回收可以被懒散地启动,也就是
-说,只有在需要的时候(也就是区域的空闲内存为0),而不是让它成为一个主动的过程。
-
-也就是说,内核应该尝试从直接映射池中满足对直接映射页的请求,而不是回退到dma池中,
-这样就可以保持dma池为dma请求(不管是不是原子的)所填充。类似的争论也适用于高内存
-和直接映射的页面。相反,如果有很多空闲的dma页,最好是通过从dma池中分配一个来满足
-常规的内存请求,而不是产生常规区域平衡的开销。
-
-在2.2中,只有当空闲页总数低于总内存的1/64时,才会启动内存平衡/页面回收。如果dma
-和常规内存的比例合适,即使dma区完全空了,也很可能不会进行平衡。2.2已经在不同内存
-大小的生产机器上运行,即使有这个问题存在,似乎也做得不错。在2.3中,由于HIGHMEM的
-存在,这个问题变得更加严重。
-
-在2.3中,区域平衡可以用两种方式之一来完成:根据区域的大小(可能是低级区域的大小),
-我们可以在初始化阶段决定在平衡任何区域时应该争取多少空闲页。好的方面是,在平衡的时
-候,我们不需要看低级区的大小,坏的方面是,我们可能会因为忽略低级区可能较低的使用率
-而做过于频繁的平衡。另外,只要对分配程序稍作修改,就有可能将memclass()宏简化为一
-个简单的等式。
-
-另一个可能的解决方案是,我们只在一个区 **和** 其所有低级区的空闲内存低于该区及其
-低级区总内存的1/64时进行平衡。这就解决了2.2的平衡问题,并尽可能地保持了与2.2行为
-的接近。另外,平衡算法在各种架构上的工作方式也是一样的,这些架构有不同数量和类型的
-内存区。如果我们想变得更花哨一点,我们可以在未来为不同区域的自由页面分配不同的权重。
-
-请注意,如果普通区的大小与dma区相比是巨大的,那么在决定是否平衡普通区的时候,考虑
-空闲的dma页就变得不那么重要了。那么第一个解决方案就变得更有吸引力。
-
-所附的补丁实现了第二个解决方案。它还 “修复”了两个问题:首先,在低内存条件下,kswapd
-被唤醒,就像2.2中的非睡眠分配。第二,HIGHMEM区也被平衡了,以便给replace_with_highmem()
-一个争取获得HIGHMEM页的机会,同时确保HIGHMEM分配不会落回普通区。这也确保了HIGHMEM
-页不会被泄露(例如,在一个HIGHMEM页在交换缓存中但没有被任何人使用的情况下)。
-
-kswapd还需要知道它应该平衡哪些区。kswapd主要是在无法进行平衡的情况下需要的,可能
-是因为所有的分配请求都来自中断上下文,而所有的进程上下文都在睡眠。对于2.3,
-kswapd并不真正需要平衡高内存区,因为中断上下文并不请求高内存页。kswapd看zone
-结构体中的zone_wake_kswapd字段来决定一个区是否需要平衡。
-
-如果从进程内存和shm中偷取页面可以减轻该页面节点中任何区的内存压力,而该区的内存压力
-已经低于其水位,则会进行偷取。
-
-watemark[WMARK_MIN/WMARK_LOW/WMARK_HIGH]/low_on_memory/zone_wake_kswapd:
-这些是每个区的字段,用于确定一个区何时需要平衡。当页面数低于水位[WMARK_MIN]时,
-hysteric 的字段low_on_memory被设置。这个字段会一直被设置,直到空闲页数变成水位
-[WMARK_HIGH]。当low_on_memory被设置时,页面分配请求将尝试释放该区域的一些页面(如果
-请求中设置了GFP_WAIT)。与此相反的是,决定唤醒kswapd以释放一些区的页。这个决定不是基于
-hysteresis 的,而是当空闲页的数量低于watermark[WMARK_LOW]时就会进行;在这种情况下,
-zone_wake_kswapd也被设置。
-
-
-我所听到的(超棒的)想法:
-
-1. 动态经历应该影响平衡:可以跟踪一个区的失败请求的数量,并反馈到平衡方案中(jalvo@mbay.net)。
-
-2. 实现一个类似于replace_with_highmem()的replace_with_regular(),以保留dma页面。
-   (lkd@tantalophile.demon.co.uk)
diff --git a/Documentation/translations/zh_CN/vm/damon/api.rst b/Documentation/translations/zh_CN/vm/damon/api.rst
deleted file mode 100644 (file)
index 21143ee..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-:Original: Documentation/vm/damon/api.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-
-=======
-API参考
-=======
-
-内核空间的程序可以使用下面的API来使用DAMON的每个功能。你所需要做的就是引用 ``damon.h`` ,
-它位于源代码树的include/linux/。
-
-结构体
-======
-
-该API在以下内核代码中:
-
-include/linux/damon.h
-
-
-函数
-====
-
-该API在以下内核代码中:
-
-mm/damon/core.c
diff --git a/Documentation/translations/zh_CN/vm/damon/design.rst b/Documentation/translations/zh_CN/vm/damon/design.rst
deleted file mode 100644 (file)
index 46128b7..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-:Original: Documentation/vm/damon/design.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-
-====
-设计
-====
-
-可配置的层
-==========
-
-DAMON提供了数据访问监控功能,同时使其准确性和开销可控。基本的访问监控需要依赖于目标地址空间
-并为之优化的基元。另一方面,作为DAMON的核心,准确性和开销的权衡机制是在纯逻辑空间中。DAMON
-将这两部分分离在不同的层中,并定义了它的接口,以允许各种低层次的基元实现与核心逻辑的配置。
-
-由于这种分离的设计和可配置的接口,用户可以通过配置核心逻辑和适当的低级基元实现来扩展DAMON的
-任何地址空间。如果没有提供合适的,用户可以自己实现基元。
-
-例如,物理内存、虚拟内存、交换空间、那些特定的进程、NUMA节点、文件和支持的内存设备将被支持。
-另外,如果某些架构或设备支持特殊的优化访问检查基元,这些基元将很容易被配置。
-
-
-特定地址空间基元的参考实现
-==========================
-
-基本访问监测的低级基元被定义为两部分。:
-
-1. 确定地址空间的监测目标地址范围
-2. 目标空间中特定地址范围的访问检查。
-
-DAMON目前为物理和虚拟地址空间提供了基元的实现。下面两个小节描述了这些工作的方式。
-
-
-基于VMA的目标地址范围构造
--------------------------
-
-这仅仅是针对虚拟地址空间基元的实现。对于物理地址空间,只是要求用户手动设置监控目标地址范围。
-
-在进程的超级巨大的虚拟地址空间中,只有小部分被映射到物理内存并被访问。因此,跟踪未映射的地
-址区域只是一种浪费。然而,由于DAMON可以使用自适应区域调整机制来处理一定程度的噪声,所以严
-格来说,跟踪每一个映射并不是必须的,但在某些情况下甚至会产生很高的开销。也就是说,监测目标
-内部过于巨大的未映射区域应该被移除,以不占用自适应机制的时间。
-
-出于这个原因,这个实现将复杂的映射转换为三个不同的区域,覆盖地址空间的每个映射区域。这三个
-区域之间的两个空隙是给定地址空间中两个最大的未映射区域。这两个最大的未映射区域是堆和最上面
-的mmap()区域之间的间隙,以及在大多数情况下最下面的mmap()区域和堆之间的间隙。因为这些间隙
-在通常的地址空间中是异常巨大的,排除这些间隙就足以做出合理的权衡。下面详细说明了这一点::
-
-    <heap>
-    <BIG UNMAPPED REGION 1>
-    <uppermost mmap()-ed region>
-    (small mmap()-ed regions and munmap()-ed regions)
-    <lowermost mmap()-ed region>
-    <BIG UNMAPPED REGION 2>
-    <stack>
-
-
-基于PTE访问位的访问检查
------------------------
-
-物理和虚拟地址空间的实现都使用PTE Accessed-bit进行基本访问检查。唯一的区别在于从地址中
-找到相关的PTE访问位的方式。虚拟地址的实现是为该地址的目标任务查找页表,而物理地址的实现则
-是查找与该地址有映射关系的每一个页表。通过这种方式,实现者找到并清除下一个采样目标地址的位,
-并检查该位是否在一个采样周期后再次设置。这可能会干扰其他使用访问位的内核子系统,即空闲页跟
-踪和回收逻辑。为了避免这种干扰,DAMON使其与空闲页面跟踪相互排斥,并使用 ``PG_idle`` 和
-``PG_young`` 页面标志来解决与回收逻辑的冲突,就像空闲页面跟踪那样。
-
-
-独立于地址空间的核心机制
-========================
-
-下面四个部分分别描述了DAMON的核心机制和五个监测属性,即 ``采样间隔`` 、 ``聚集间隔`` 、
-``更新间隔`` 、 ``最小区域数`` 和 ``最大区域数`` 。
-
-
-访问频率监测
-------------
-
-DAMON的输出显示了在给定的时间内哪些页面的访问频率是多少。访问频率的分辨率是通过设置
-``采样间隔`` 和 ``聚集间隔`` 来控制的。详细地说,DAMON检查每个 ``采样间隔`` 对每
-个页面的访问,并将结果汇总。换句话说,计算每个页面的访问次数。在每个 ``聚合间隔`` 过
-去后,DAMON调用先前由用户注册的回调函数,以便用户可以阅读聚合的结果,然后再清除这些结
-果。这可以用以下简单的伪代码来描述::
-
-    while monitoring_on:
-        for page in monitoring_target:
-            if accessed(page):
-                nr_accesses[page] += 1
-        if time() % aggregation_interval == 0:
-            for callback in user_registered_callbacks:
-                callback(monitoring_target, nr_accesses)
-            for page in monitoring_target:
-                nr_accesses[page] = 0
-        sleep(sampling interval)
-
-这种机制的监测开销将随着目标工作负载规模的增长而任意增加。
-
-
-基于区域的抽样调查
-------------------
-
-为了避免开销的无限制增加,DAMON将假定具有相同访问频率的相邻页面归入一个区域。只要保持
-这个假设(一个区域内的页面具有相同的访问频率),该区域内就只需要检查一个页面。因此,对
-于每个 ``采样间隔`` ,DAMON在每个区域中随机挑选一个页面,等待一个 ``采样间隔`` ,检
-查该页面是否同时被访问,如果被访问则增加该区域的访问频率。因此,监测开销是可以通过设置
-区域的数量来控制的。DAMON允许用户设置最小和最大的区域数量来进行权衡。
-
-然而,如果假设没有得到保证,这个方案就不能保持输出的质量。
-
-
-适应性区域调整
---------------
-
-即使最初的监测目标区域被很好地构建以满足假设(同一区域内的页面具有相似的访问频率),数
-据访问模式也会被动态地改变。这将导致监测质量下降。为了尽可能地保持假设,DAMON根据每个
-区域的访问频率自适应地进行合并和拆分。
-
-对于每个 ``聚集区间`` ,它比较相邻区域的访问频率,如果频率差异较小,就合并这些区域。
-然后,在它报告并清除每个区域的聚合接入频率后,如果区域总数不超过用户指定的最大区域数,
-它将每个区域拆分为两个或三个区域。
-
-通过这种方式,DAMON提供了其最佳的质量和最小的开销,同时保持了用户为其权衡设定的界限。
-
-
-动态目标空间更新处理
---------------------
-
-监测目标地址范围可以动态改变。例如,虚拟内存可以动态地被映射和解映射。物理内存可以被
-热插拔。
-
-由于在某些情况下变化可能相当频繁,DAMON允许监控操作检查动态变化,包括内存映射变化,
-并仅在用户指定的时间间隔( ``更新间隔`` )中的每个时间段,将其应用于监控操作相关的
-数据结构,如抽象的监控目标内存区。
\ No newline at end of file
diff --git a/Documentation/translations/zh_CN/vm/damon/faq.rst b/Documentation/translations/zh_CN/vm/damon/faq.rst
deleted file mode 100644 (file)
index 07b4ac1..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-:Original: Documentation/vm/damon/faq.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-
-========
-常见问题
-========
-
-为什么是一个新的子系统,而不是扩展perf或其他用户空间工具?
-==========================================================
-
-首先,因为它需要尽可能的轻量级,以便可以在线使用,所以应该避免任何不必要的开销,如内核-用户
-空间的上下文切换成本。第二,DAMON的目标是被包括内核在内的其他程序所使用。因此,对特定工具
-(如perf)的依赖性是不可取的。这就是DAMON在内核空间实现的两个最大的原因。
-
-
-“闲置页面跟踪” 或 “perf mem” 可以替代DAMON吗?
-==============================================
-
-闲置页跟踪是物理地址空间访问检查的一个低层次的原始方法。“perf mem”也是类似的,尽管它可以
-使用采样来减少开销。另一方面,DAMON是一个更高层次的框架,用于监控各种地址空间。它专注于内
-存管理优化,并提供复杂的精度/开销处理机制。因此,“空闲页面跟踪” 和 “perf mem” 可以提供
-DAMON输出的一个子集,但不能替代DAMON。
-
-
-DAMON是否只支持虚拟内存?
-=========================
-
-不,DAMON的核心是独立于地址空间的。用户可以在DAMON核心上实现和配置特定地址空间的低级原始
-部分,包括监测目标区域的构造和实际的访问检查。通过这种方式,DAMON用户可以用任何访问检查技
-术来监测任何地址空间。
-
-尽管如此,DAMON默认为虚拟内存和物理内存提供了基于vma/rmap跟踪和PTE访问位检查的地址空间
-相关功能的实现,以供参考和方便使用。
-
-
-我可以简单地监测页面的粒度吗?
-==============================
-
-是的,你可以通过设置 ``min_nr_regions`` 属性高于工作集大小除以页面大小的值来实现。
-因为监视目标区域的大小被强制为 ``>=page size`` ,所以区域分割不会产生任何影响。
diff --git a/Documentation/translations/zh_CN/vm/damon/index.rst b/Documentation/translations/zh_CN/vm/damon/index.rst
deleted file mode 100644 (file)
index 84d36d9..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-:Original: Documentation/vm/damon/index.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-
-==========================
-DAMON:数据访问监视器
-==========================
-
-DAMON是Linux内核的一个数据访问监控框架子系统。DAMON的核心机制使其成为
-(该核心机制详见(Documentation/translations/zh_CN/vm/damon/design.rst))
-
- - *准确度* (监测输出对DRAM级别的内存管理足够有用;但可能不适合CPU Cache级别),
- - *轻量级* (监控开销低到可以在线应用),以及
- - *可扩展* (无论目标工作负载的大小,开销的上限值都在恒定范围内)。
-
-因此,利用这个框架,内核的内存管理机制可以做出高级决策。会导致高数据访问监控开销的实
-验性内存管理优化工作可以再次进行。同时,在用户空间,有一些特殊工作负载的用户可以编写
-个性化的应用程序,以便更好地了解和优化他们的工作负载和系统。
-
-.. toctree::
-   :maxdepth: 2
-
-   faq
-   design
-   api
-
diff --git a/Documentation/translations/zh_CN/vm/free_page_reporting.rst b/Documentation/translations/zh_CN/vm/free_page_reporting.rst
deleted file mode 100644 (file)
index 14336a3..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-.. include:: ../disclaimer-zh_CN.rst
-
-:Original: Documentation/vm/free_page_reporting.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-==========
-空闲页报告
-==========
-
-空闲页报告是一个API,设备可以通过它来注册接收系统当前未使用的页面列表。这在虚拟
-化的情况下是很有用的,客户机能够使用这些数据来通知管理器它不再使用内存中的某些页
-面。
-
-对于驱动,通常是气球驱动要使用这个功能,它将分配和初始化一个page_reporting_dev_info
-结构体。它要填充的结构体中的字段是用于处理散点列表的 "report" 函数指针。它还必
-须保证每次调用该函数时能处理至少相当于PAGE_REPORTING_CAPACITY的散点列表条目。
-假设没有其他页面报告设备已经注册, 对page_reporting_register的调用将向报告框
-架注册页面报告接口。
-
-一旦注册,页面报告API将开始向驱动报告成批的页面。API将在接口被注册后2秒开始报告
-页面,并在任何足够高的页面被释放之后2秒继续报告。
-
-报告的页面将被存储在传递给报告函数的散列表中,最后一个条目的结束位被设置在条目
-nent-1中。 当页面被报告函数处理时,分配器将无法访问它们。一旦报告函数完成,这些
-页将被返回到它们所获得的自由区域。
-
-在移除使用空闲页报告的驱动之前,有必要调用page_reporting_unregister,以移除
-目前被空闲页报告使用的page_reporting_dev_info结构体。这样做将阻止进一步的报
-告通过该接口发出。如果另一个驱动或同一驱动被注册,它就有可能恢复前一个驱动在报告
-空闲页方面的工作。
-
-
-Alexander Duyck, 2019年12月04日
diff --git a/Documentation/translations/zh_CN/vm/frontswap.rst b/Documentation/translations/zh_CN/vm/frontswap.rst
deleted file mode 100644 (file)
index 98aa6f5..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-:Original: Documentation/vm/free_page_reporting.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-=========
-Frontswap
-=========
-
-Frontswap为交换页提供了一个 “transcendent memory” 的接口。在一些环境中,由
-于交换页被保存在RAM(或类似RAM的设备)中,而不是交换磁盘,因此可以获得巨大的性能
-节省(提高)。
-
-.. _Transcendent memory in a nutshell: https://lwn.net/Articles/454795/
-
-Frontswap之所以这么命名,是因为它可以被认为是与swap设备的“back”存储相反。存
-储器被认为是一个同步并发安全的面向页面的“伪RAM设备”,符合transcendent memory
-(如Xen的“tmem”,或内核内压缩内存,又称“zcache”,或未来的类似RAM的设备)的要
-求;这个伪RAM设备不能被内核直接访问或寻址,其大小未知且可能随时间变化。驱动程序通过
-调用frontswap_register_ops将自己与frontswap链接起来,以适当地设置frontswap_ops
-的功能,它提供的功能必须符合某些策略,如下所示:
-
-一个 “init” 将设备准备好接收与指定的交换设备编号(又称“类型”)相关的frontswap
-交换页。一个 “store” 将把该页复制到transcendent memory,并与该页的类型和偏移
-量相关联。一个 “load” 将把该页,如果找到的话,从transcendent memory复制到内核
-内存,但不会从transcendent memory中删除该页。一个 “invalidate_page” 将从
-transcendent memory中删除该页,一个 “invalidate_area” 将删除所有与交换类型
-相关的页(例如,像swapoff)并通知 “device” 拒绝进一步存储该交换类型。
-
-一旦一个页面被成功存储,在该页面上的匹配加载通常会成功。因此,当内核发现自己处于需
-要交换页面的情况时,它首先尝试使用frontswap。如果存储的结果是成功的,那么数据就已
-经成功的保存到了transcendent memory中,并且避免了磁盘写入,如果后来再读回数据,
-也避免了磁盘读取。如果存储返回失败,transcendent memory已经拒绝了该数据,且该页
-可以像往常一样被写入交换空间。
-
-请注意,如果一个页面被存储,而该页面已经存在于transcendent memory中(一个 “重复”
-的存储),要么存储成功,数据被覆盖,要么存储失败,该页面被废止。这确保了旧的数据永远
-不会从frontswap中获得。
-
-如果配置正确,对frontswap的监控是通过 `/sys/kernel/debug/frontswap` 目录下的
-debugfs完成的。frontswap的有效性可以通过以下方式测量(在所有交换设备中):
-
-``failed_stores``
-       有多少次存储的尝试是失败的
-
-``loads``
-       尝试了多少次加载(应该全部成功)
-
-``succ_stores``
-       有多少次存储的尝试是成功的
-
-``invalidates``
-       尝试了多少次作废
-
-后台实现可以提供额外的指标。
-
-经常问到的问题
-==============
-
-* 价值在哪里?
-
-当一个工作负载开始交换时,性能就会下降。Frontswap通过提供一个干净的、动态的接口来
-读取和写入交换页到 “transcendent memory”,从而大大增加了许多这样的工作负载的性
-能,否则内核是无法直接寻址的。当数据被转换为不同的形式和大小(比如压缩)或者被秘密
-移动(对于一些类似RAM的设备来说,这可能对写平衡很有用)时,这个接口是理想的。交换
-页(和被驱逐的页面缓存页)是这种比RAM慢但比磁盘快得多的“伪RAM设备”的一大用途。
-
-Frontswap对内核的影响相当小,为各种系统配置中更动态、更灵活的RAM利用提供了巨大的
-灵活性:
-
-在单一内核的情况下,又称“zcache”,页面被压缩并存储在本地内存中,从而增加了可以安
-全保存在RAM中的匿名页面总数。Zcache本质上是用压缩/解压缩的CPU周期换取更好的内存利
-用率。Benchmarks测试显示,当内存压力较低时,几乎没有影响,而在高内存压力下的一些
-工作负载上,则有明显的性能改善(25%以上)。
-
-“RAMster” 在zcache的基础上增加了对集群系统的 “peer-to-peer” transcendent memory
-的支持。Frontswap页面像zcache一样被本地压缩,但随后被“remotified” 到另一个系
-统的RAM。这使得RAM可以根据需要动态地来回负载平衡,也就是说,当系统A超载时,它可以
-交换到系统B,反之亦然。RAMster也可以被配置成一个内存服务器,因此集群中的许多服务器
-可以根据需要动态地交换到配置有大量内存的单一服务器上......而不需要预先配置每个客户
-有多少内存可用
-
-在虚拟情况下,虚拟化的全部意义在于统计地将物理资源在多个虚拟机的不同需求之间进行复
-用。对于RAM来说,这真的很难做到,而且在不改变内核的情况下,要做好这一点的努力基本上
-是失败的(除了一些广为人知的特殊情况下的工作负载)。具体来说,Xen Transcendent Memory
-后端允许管理器拥有的RAM “fallow”,不仅可以在多个虚拟机之间进行“time-shared”,
-而且页面可以被压缩和重复利用,以优化RAM的利用率。当客户操作系统被诱导交出未充分利用
-的RAM时(如 “selfballooning”),突然出现的意外内存压力可能会导致交换;frontswap
-允许这些页面被交换到管理器RAM中或从管理器RAM中交换(如果整体主机系统内存条件允许),
-从而减轻计划外交换可能带来的可怕的性能影响。
-
-一个KVM的实现正在进行中,并且已经被RFC'ed到lkml。而且,利用frontswap,对NVM作为
-内存扩展技术的调查也在进行中。
-
-* 当然,在某些情况下可能有性能上的优势,但frontswap的空间/时间开销是多少?
-
-如果 CONFIG_FRONTSWAP 被禁用,每个 frontswap 钩子都会编译成空,唯一的开销是每
-个 swapon'ed swap 设备的几个额外字节。如果 CONFIG_FRONTSWAP 被启用,但没有
-frontswap的 “backend” 寄存器,每读或写一个交换页就会有一个额外的全局变量,而不
-是零。如果 CONFIG_FRONTSWAP 被启用,并且有一个frontswap的backend寄存器,并且
-后端每次 “store” 请求都失败(即尽管声称可能,但没有提供内存),CPU 的开销仍然可以
-忽略不计 - 因为每次frontswap失败都是在交换页写到磁盘之前,系统很可能是 I/O 绑定
-的,无论如何使用一小部分的 CPU 都是不相关的。
-
-至于空间,如果CONFIG_FRONTSWAP被启用,并且有一个frontswap的backend注册,那么
-每个交换设备的每个交换页都会被分配一个比特。这是在内核已经为每个交换设备的每个交换
-页分配的8位(在2.6.34之前是16位)上增加的。(Hugh Dickins观察到,frontswap可能
-会偷取现有的8个比特,但是我们以后再来担心这个小的优化问题)。对于标准的4K页面大小的
-非常大的交换盘(这很罕见),这是每32GB交换盘1MB开销。
-
-当交换页存储在transcendent memory中而不是写到磁盘上时,有一个副作用,即这可能会
-产生更多的内存压力,有可能超过其他的优点。一个backend,比如zcache,必须实现策略
-来仔细(但动态地)管理内存限制,以确保这种情况不会发生。
-
-* 好吧,那就用内核骇客能理解的术语来快速概述一下这个frontswap补丁的作用如何?
-
-我们假设在内核初始化过程中,一个frontswap 的 “backend” 已经注册了;这个注册表
-明这个frontswap 的 “backend” 可以访问一些不被内核直接访问的“内存”。它到底提
-供了多少内存是完全动态和随机的。
-
-每当一个交换设备被交换时,就会调用frontswap_init(),把交换设备的编号(又称“类
-型”)作为一个参数传给它。这就通知了frontswap,以期待 “store” 与该号码相关的交
-换页的尝试。
-
-每当交换子系统准备将一个页面写入交换设备时(参见swap_writepage()),就会调用
-frontswap_store。Frontswap与frontswap backend协商,如果backend说它没有空
-间,frontswap_store返回-1,内核就会照常把页换到交换设备上。注意,来自frontswap
-backend的响应对内核来说是不可预测的;它可能选择从不接受一个页面,可能接受每九个
-页面,也可能接受每一个页面。但是如果backend确实接受了一个页面,那么这个页面的数
-据已经被复制并与类型和偏移量相关联了,而且backend保证了数据的持久性。在这种情况
-下,frontswap在交换设备的“frontswap_map” 中设置了一个位,对应于交换设备上的
-页面偏移量,否则它就会将数据写入该设备。
-
-当交换子系统需要交换一个页面时(swap_readpage()),它首先调用frontswap_load(),
-检查frontswap_map,看这个页面是否早先被frontswap backend接受。如果是,该页
-的数据就会从frontswap后端填充,换入就完成了。如果不是,正常的交换代码将被执行,
-以便从真正的交换设备上获得这一页的数据。
-
-所以每次frontswap backend接受一个页面时,交换设备的读取和(可能)交换设备的写
-入都被 “frontswap backend store” 和(可能)“frontswap backend loads”
-所取代,这可能会快得多。
-
-* frontswap不能被配置为一个 “特殊的” 交换设备,它的优先级要高于任何真正的交换
-  设备(例如像zswap,或者可能是swap-over-nbd/NFS)?
-
-首先,现有的交换子系统不允许有任何种类的交换层次结构。也许它可以被重写以适应层次
-结构,但这将需要相当大的改变。即使它被重写,现有的交换子系统也使用了块I/O层,它
-假定交换设备是固定大小的,其中的任何页面都是可线性寻址的。Frontswap几乎没有触
-及现有的交换子系统,而是围绕着块I/O子系统的限制,提供了大量的灵活性和动态性。
-
-例如,frontswap backend对任何交换页的接受是完全不可预测的。这对frontswap backend
-的定义至关重要,因为它赋予了backend完全动态的决定权。在zcache中,人们无法预
-先知道一个页面的可压缩性如何。可压缩性 “差” 的页面会被拒绝,而 “差” 本身也可
-以根据当前的内存限制动态地定义。
-
-此外,frontswap是完全同步的,而真正的交换设备,根据定义,是异步的,并且使用
-块I/O。块I/O层不仅是不必要的,而且可能进行 “优化”,这对面向RAM的设备来说是
-不合适的,包括将一些页面的写入延迟相当长的时间。同步是必须的,以确保后端的动
-态性,并避免棘手的竞争条件,这将不必要地大大增加frontswap和/或块I/O子系统的
-复杂性。也就是说,只有最初的 “store” 和 “load” 操作是需要同步的。一个独立
-的异步线程可以自由地操作由frontswap存储的页面。例如,RAMster中的 “remotification”
-线程使用标准的异步内核套接字,将压缩的frontswap页面移动到远程机器。同样,
-KVM的客户方实现可以进行客户内压缩,并使用 “batched” hypercalls。
-
-在虚拟化环境中,动态性允许管理程序(或主机操作系统)做“intelligent overcommit”。
-例如,它可以选择只接受页面,直到主机交换可能即将发生,然后强迫客户机做他们
-自己的交换。
-
-transcendent memory规格的frontswap有一个坏处。因为任何 “store” 都可
-能失败,所以必须在一个真正的交换设备上有一个真正的插槽来交换页面。因此,
-frontswap必须作为每个交换设备的 “影子” 来实现,它有可能容纳交换设备可能
-容纳的每一个页面,也有可能根本不容纳任何页面。这意味着frontswap不能包含比
-swap设备总数更多的页面。例如,如果在某些安装上没有配置交换设备,frontswap
-就没有用。无交换设备的便携式设备仍然可以使用frontswap,但是这种设备的
-backend必须配置某种 “ghost” 交换设备,并确保它永远不会被使用。
-
-
-* 为什么会有这种关于 “重复存储” 的奇怪定义?如果一个页面以前被成功地存储过,
-  难道它不能总是被成功地覆盖吗?
-
-几乎总是可以的,不,有时不能。考虑一个例子,数据被压缩了,原来的4K页面被压
-缩到了1K。现在,有人试图用不可压缩的数据覆盖该页,因此会占用整个4K。但是
-backend没有更多的空间了。在这种情况下,这个存储必须被拒绝。每当frontswap
-拒绝一个会覆盖的存储时,它也必须使旧的数据作废,并确保它不再被访问。因为交
-换子系统会把新的数据写到读交换设备上,这是确保一致性的正确做法。
-
-* 为什么frontswap补丁会创建新的头文件swapfile.h?
-
-frontswap代码依赖于一些swap子系统内部的数据结构,这些数据结构多年来一直
-在静态和全局之间来回移动。这似乎是一个合理的妥协:将它们定义为全局,但在一
-个新的包含文件中声明它们,该文件不被包含swap.h的大量源文件所包含。
-
-Dan Magenheimer,最后更新于2012年4月9日
diff --git a/Documentation/translations/zh_CN/vm/highmem.rst b/Documentation/translations/zh_CN/vm/highmem.rst
deleted file mode 100644 (file)
index 2003217..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-.. include:: ../disclaimer-zh_CN.rst
-
-:Original: Documentation/vm/highmem.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-==========
-高内存处理
-==========
-
-作者: Peter Zijlstra <a.p.zijlstra@chello.nl>
-
-.. contents:: :local:
-
-高内存是什么?
-==============
-
-当物理内存的大小接近或超过虚拟内存的最大大小时,就会使用高内存(highmem)。在这一点上,内
-核不可能在任何时候都保持所有可用的物理内存的映射。这意味着内核需要开始使用它想访问的物理内
-存的临时映射。
-
-没有被永久映射覆盖的那部分(物理)内存就是我们所说的 "高内存"。对于这个边界的确切位置,有
-各种架构上的限制。
-
-例如,在i386架构中,我们选择将内核映射到每个进程的虚拟空间,这样我们就不必为内核的进入/退
-出付出全部的TLB作废代价。这意味着可用的虚拟内存空间(i386上为4GiB)必须在用户和内核空间之
-间进行划分。
-
-使用这种方法的架构的传统分配方式是3:1,3GiB用于用户空间,顶部的1GiB用于内核空间。::
-
-               +--------+ 0xffffffff
-               | Kernel |
-               +--------+ 0xc0000000
-               |        |
-               | User   |
-               |        |
-               +--------+ 0x00000000
-
-这意味着内核在任何时候最多可以映射1GiB的物理内存,但是由于我们需要虚拟地址空间来做其他事
-情--包括访问其余物理内存的临时映射--实际的直接映射通常会更少(通常在~896MiB左右)。
-
-其他有mm上下文标签的TLB的架构可以有独立的内核和用户映射。然而,一些硬件(如一些ARM)在使
-用mm上下文标签时,其虚拟空间有限。
-
-
-临时虚拟映射
-============
-
-内核包含几种创建临时映射的方法。下面的列表按照使用的优先顺序显示了它们。
-
-* kmap_local_page()。这个函数是用来要求短期映射的。它可以从任何上下文(包括中断)中调用,
-  但是映射只能在获取它们的上下文中使用。
-
-  在可行的情况下,这个函数应该比其他所有的函数优先使用。
-
-  这些映射是线程本地和CPU本地的,这意味着映射只能从这个线程中访问,并且当映射处于活动状
-  态时,该线程与CPU绑定。即使线程被抢占了(因为抢占永远不会被函数禁用),CPU也不能通过
-  CPU-hotplug从系统中拔出,直到映射被处理掉。
-
-  在本地的kmap区域中采取pagefaults是有效的,除非获取本地映射的上下文由于其他原因不允许
-  这样做。
-
-  kmap_local_page()总是返回一个有效的虚拟地址,并且假定kunmap_local()不会失败。
-
-  嵌套kmap_local_page()和kmap_atomic()映射在一定程度上是允许的(最多到KMAP_TYPE_NR),
-  但是它们的调用必须严格排序,因为映射的实现是基于堆栈的。关于如何管理嵌套映射的细节,
-  请参见kmap_local_page() kdocs(包含在 "函数 "部分)。
-
-* kmap_atomic().  这允许对单个页面进行非常短的时间映射。由于映射被限制在发布它的CPU上,
-  它表现得很好,但发布的任务因此被要求留在该CPU上直到它完成,以免其他任务取代它的映射。
-
-  kmap_atomic()也可以被中断上下文使用,因为它不睡眠,调用者也可能在调用kunmap_atomic()
-  后才睡眠。
-
-  内核中对kmap_atomic()的每次调用都会创建一个不可抢占的段,并禁用缺页异常。这可能是
-  未预期延迟的来源之一。因此用户应该选择kmap_local_page()而不是kmap_atomic()。
-
-  假设k[un]map_atomic()不会失败。
-
-* kmap()。这应该被用来对单个页面进行短时间的映射,对抢占或迁移没有限制。它会带来开销,
-  因为映射空间是受限制的,并且受到全局锁的保护,以实现同步。当不再需要映射时,必须用
-  kunmap()释放该页被映射的地址。
-
-  映射变化必须广播到所有CPU(核)上,kmap()还需要在kmap的池被回绕(TLB项用光了,需要从第
-   一项复用)时进行全局TLB无效化,当映射空间被完全利用时,它可能会阻塞,直到有一个可用的
-   槽出现。因此,kmap()只能从可抢占的上下文中调用。
-
-  如果一个映射必须持续相对较长的时间,上述所有的工作都是必要的,但是内核中大部分的
-  高内存映射都是短暂的,而且只在一个地方使用。这意味着在这种情况下,kmap()的成本大
-  多被浪费了。kmap()并不是为长期映射而设计的,但是它已经朝着这个方向发展了,在较新
-  的代码中强烈不鼓励使用它,前面的函数集应该是首选。
-
-  在64位系统中,调用kmap_local_page()、kmap_atomic()和kmap()没有实际作用,因为64位
-  地址空间足以永久映射所有物理内存页面。
-
-* vmap()。这可以用来将多个物理页长期映射到一个连续的虚拟空间。它需要全局同步来解除
-  映射。
-
-临时映射的成本
-==============
-
-创建临时映射的代价可能相当高。体系架构必须操作内核的页表、数据TLB和/或MMU的寄存器。
-
-如果CONFIG_HIGHMEM没有被设置,那么内核会尝试用一点计算来创建映射,将页面结构地址转换成
-指向页面内容的指针,而不是去捣鼓映射。在这种情况下,解映射操作可能是一个空操作。
-
-如果CONFIG_MMU没有被设置,那么就不可能有临时映射和高内存。在这种情况下,也将使用计算方法。
-
-
-i386 PAE
-========
-
-在某些情况下,i386 架构将允许你在 32 位机器上安装多达 64GiB 的内存。但这有一些后果:
-
-* Linux需要为系统中的每个页面建立一个页帧结构,而且页帧需要驻在永久映射中,这意味着:
-
-* 你最多可以有896M/sizeof(struct page)页帧;由于页结构体是32字节的,所以最终会有
-  112G的页;然而,内核需要在内存中存储更多的页帧......
-
-* PAE使你的页表变大--这使系统变慢,因为更多的数据需要在TLB填充等方面被访问。一个好处
-  是,PAE有更多的PTE位,可以提供像NX和PAT这样的高级功能。
-
-一般的建议是,你不要在32位机器上使用超过8GiB的空间--尽管更多的空间可能对你和你的工作
-量有用,但你几乎是靠你自己--不要指望内核开发者真的会很关心事情的进展情况。
-
-函数
-====
-
-该API在以下内核代码中:
-
-include/linux/highmem.h
-
-include/linux/highmem-internal.h
diff --git a/Documentation/translations/zh_CN/vm/hmm.rst b/Documentation/translations/zh_CN/vm/hmm.rst
deleted file mode 100644 (file)
index 2379df9..0000000
+++ /dev/null
@@ -1,361 +0,0 @@
-.. include:: ../disclaimer-zh_CN.rst
-
-:Original: Documentation/vm/hmm.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-==================
-异构内存管理 (HMM)
-==================
-
-提供基础设施和帮助程序以将非常规内存(设备内存,如板上 GPU 内存)集成到常规内核路径中,其
-基石是此类内存的专用struct page(请参阅本文档的第 5 至 7 节)。
-
-HMM 还为 SVM(共享虚拟内存)提供了可选的帮助程序,即允许设备透明地访问与 CPU 一致的程序
-地址,这意味着 CPU 上的任何有效指针也是该设备的有效指针。这对于简化高级异构计算的使用变得
-必不可少,其中 GPU、DSP 或 FPGA 用于代表进程执行各种计算。
-
-本文档分为以下部分:在第一部分中,我揭示了与使用特定于设备的内存分配器相关的问题。在第二
-部分中,我揭示了许多平台固有的硬件限制。第三部分概述了 HMM 设计。第四部分解释了 CPU 页
-表镜像的工作原理以及 HMM 在这种情况下的目的。第五部分处理内核中如何表示设备内存。最后,
-最后一节介绍了一个新的迁移助手,它允许利用设备 DMA 引擎。
-
-.. contents:: :local:
-
-使用特定于设备的内存分配器的问题
-================================
-
-具有大量板载内存(几 GB)的设备(如 GPU)历来通过专用驱动程序特定 API 管理其内存。这会
-造成设备驱动程序分配和管理的内存与常规应用程序内存(私有匿名、共享内存或常规文件支持内存)
-之间的隔断。从这里开始,我将把这个方面称为分割的地址空间。我使用共享地址空间来指代相反的情况:
-即,设备可以透明地使用任何应用程序内存区域。
-
-分割的地址空间的发生是因为设备只能访问通过设备特定 API 分配的内存。这意味着从设备的角度来
-看,程序中的所有内存对象并不平等,这使得依赖于广泛的库的大型程序变得复杂。
-
-具体来说,这意味着想要利用像 GPU 这样的设备的代码需要在通用分配的内存(malloc、mmap
-私有、mmap 共享)和通过设备驱动程序 API 分配的内存之间复制对象(这仍然以 mmap 结束,
-但是是设备文件)。
-
-对于平面数据集(数组、网格、图像……),这并不难实现,但对于复杂数据集(列表、树……),
-很难做到正确。复制一个复杂的数据集需要重新映射其每个元素之间的所有指针关系。这很容易出错,
-而且由于数据集和地址的重复,程序更难调试。
-
-分割地址空间也意味着库不能透明地使用它们从核心程序或另一个库中获得的数据,因此每个库可能
-不得不使用设备特定的内存分配器来重复其输入数据集。大型项目会因此受到影响,并因为各种内存
-拷贝而浪费资源。
-
-复制每个库的API以接受每个设备特定分配器分配的内存作为输入或输出,并不是一个可行的选择。
-这将导致库入口点的组合爆炸。
-
-最后,随着高级语言结构(在 C++ 中,当然也在其他语言中)的进步,编译器现在有可能在没有程
-序员干预的情况下利用 GPU 和其他设备。某些编译器识别的模式仅适用于共享地址空间。对所有
-其他模式,使用共享地址空间也更合理。
-
-
-I/O 总线、设备内存特性
-======================
-
-由于一些限制,I/O 总线削弱了共享地址空间。大多数 I/O 总线只允许从设备到主内存的基本
-内存访问;甚至缓存一致性通常是可选的。从 CPU 访问设备内存甚至更加有限。通常情况下,它
-不是缓存一致的。
-
-如果我们只考虑 PCIE 总线,那么设备可以访问主内存(通常通过 IOMMU)并与 CPU 缓存一
-致。但是,它只允许设备对主存储器进行一组有限的原子操作。这在另一个方向上更糟:CPU
-只能访问有限范围的设备内存,而不能对其执行原子操作。因此,从内核的角度来看,设备内存不
-能被视为与常规内存等同。
-
-另一个严重的因素是带宽有限(约 32GBytes/s,PCIE 4.0 和 16 通道)。这比最快的 GPU
-内存 (1 TBytes/s) 慢 33 倍。最后一个限制是延迟。从设备访问主内存的延迟比设备访问自
-己的内存时高一个数量级。
-
-一些平台正在开发新的 I/O 总线或对 PCIE 的添加/修改以解决其中一些限制
-(OpenCAPI、CCIX)。它们主要允许 CPU 和设备之间的双向缓存一致性,并允许架构支持的所
-有原子操作。遗憾的是,并非所有平台都遵循这一趋势,并且一些主要架构没有针对这些问题的硬
-件解决方案。
-
-因此,为了使共享地址空间有意义,我们不仅必须允许设备访问任何内存,而且还必须允许任何内
-存在设备使用时迁移到设备内存(在迁移时阻止 CPU 访问)。
-
-
-共享地址空间和迁移
-==================
-
-HMM 打算提供两个主要功能。第一个是通过复制cpu页表到设备页表中来共享地址空间,因此对
-于进程地址空间中的任何有效主内存地址,相同的地址指向相同的物理内存。
-
-为了实现这一点,HMM 提供了一组帮助程序来填充设备页表,同时跟踪 CPU 页表更新。设备页表
-更新不像 CPU 页表更新那么容易。要更新设备页表,您必须分配一个缓冲区(或使用预先分配的
-缓冲区池)并在其中写入 GPU 特定命令以执行更新(取消映射、缓存失效和刷新等)。这不能通
-过所有设备的通用代码来完成。因此,为什么HMM提供了帮助器,在把硬件的具体细节留给设备驱
-动程序的同时,把一切可以考虑的因素都考虑进去了。
-
-HMM 提供的第二种机制是一种新的 ZONE_DEVICE 内存,它允许为设备内存的每个页面分配一个
-struct page。这些页面很特殊,因为 CPU 无法映射它们。然而,它们允许使用现有的迁移机
-制将主内存迁移到设备内存,从 CPU 的角度来看,一切看起来都像是换出到磁盘的页面。使用
-struct page可以与现有的 mm 机制进行最简单、最干净的集成。再次,HMM 仅提供帮助程序,
-首先为设备内存热插拔新的 ZONE_DEVICE 内存,然后执行迁移。迁移内容和时间的策略决定留
-给设备驱动程序。
-
-请注意,任何 CPU 对设备页面的访问都会触发缺页异常并迁移回主内存。例如,当支持给定CPU
-地址 A 的页面从主内存页面迁移到设备页面时,对地址 A 的任何 CPU 访问都会触发缺页异常
-并启动向主内存的迁移。
-
-凭借这两个特性,HMM 不仅允许设备镜像进程地址空间并保持 CPU 和设备页表同步,而且还通
-过迁移设备正在使用的数据集部分来利用设备内存。
-
-
-地址空间镜像实现和API
-=====================
-
-地址空间镜像的主要目标是允许将一定范围的 CPU 页表复制到一个设备页表中;HMM 有助于
-保持两者同步。想要镜像进程地址空间的设备驱动程序必须从注册 mmu_interval_notifier
-开始::
-
- int mmu_interval_notifier_insert(struct mmu_interval_notifier *interval_sub,
-                                 struct mm_struct *mm, unsigned long start,
-                                 unsigned long length,
-                                 const struct mmu_interval_notifier_ops *ops);
-
-在 ops->invalidate() 回调期间,设备驱动程序必须对范围执行更新操作(将范围标记为只
-读,或完全取消映射等)。设备必须在驱动程序回调返回之前完成更新。
-
-当设备驱动程序想要填充一个虚拟地址范围时,它可以使用::
-
-  int hmm_range_fault(struct hmm_range *range);
-
-如果请求写访问,它将在丢失或只读条目上触发缺页异常(见下文)。缺页异常使用通用的 mm 缺
-页异常代码路径,就像 CPU 缺页异常一样。
-
-这两个函数都将 CPU 页表条目复制到它们的 pfns 数组参数中。该数组中的每个条目对应于虚拟
-范围中的一个地址。HMM 提供了一组标志来帮助驱动程序识别特殊的 CPU 页表项。
-
-在 sync_cpu_device_pagetables() 回调中锁定是驱动程序必须尊重的最重要的方面,以保
-持事物正确同步。使用模式是::
-
- int driver_populate_range(...)
- {
-      struct hmm_range range;
-      ...
-
-      range.notifier = &interval_sub;
-      range.start = ...;
-      range.end = ...;
-      range.hmm_pfns = ...;
-
-      if (!mmget_not_zero(interval_sub->notifier.mm))
-          return -EFAULT;
-
- again:
-      range.notifier_seq = mmu_interval_read_begin(&interval_sub);
-      mmap_read_lock(mm);
-      ret = hmm_range_fault(&range);
-      if (ret) {
-          mmap_read_unlock(mm);
-          if (ret == -EBUSY)
-                 goto again;
-          return ret;
-      }
-      mmap_read_unlock(mm);
-
-      take_lock(driver->update);
-      if (mmu_interval_read_retry(&ni, range.notifier_seq) {
-          release_lock(driver->update);
-          goto again;
-      }
-
-      /* Use pfns array content to update device page table,
-       * under the update lock */
-
-      release_lock(driver->update);
-      return 0;
- }
-
-driver->update 锁与驱动程序在其 invalidate() 回调中使用的锁相同。该锁必须在调用
-mmu_interval_read_retry() 之前保持,以避免与并发 CPU 页表更新发生任何竞争。
-
-利用 default_flags 和 pfn_flags_mask
-====================================
-
-hmm_range 结构有 2 个字段,default_flags 和 pfn_flags_mask,它们指定整个范围
-的故障或快照策略,而不必为 pfns 数组中的每个条目设置它们。
-
-例如,如果设备驱动程序需要至少具有读取权限的范围的页面,它会设置::
-
-    range->default_flags = HMM_PFN_REQ_FAULT;
-    range->pfn_flags_mask = 0;
-
-并如上所述调用 hmm_range_fault()。这将填充至少具有读取权限的范围内的所有页面。
-
-现在假设驱动程序想要做同样的事情,除了它想要拥有写权限的范围内的一页。现在驱动程序设
-置::
-
-    range->default_flags = HMM_PFN_REQ_FAULT;
-    range->pfn_flags_mask = HMM_PFN_REQ_WRITE;
-    range->pfns[index_of_write] = HMM_PFN_REQ_WRITE;
-
-有了这个,HMM 将在至少读取(即有效)的所有页面中异常,并且对于地址
-== range->start + (index_of_write << PAGE_SHIFT) 它将异常写入权限,即,如果
-CPU pte 没有设置写权限,那么HMM将调用handle_mm_fault()。
-
-hmm_range_fault 完成后,标志位被设置为页表的当前状态,即 HMM_PFN_VALID | 如果页
-面可写,将设置 HMM_PFN_WRITE。
-
-
-从核心内核的角度表示和管理设备内存
-==================================
-
-尝试了几种不同的设计来支持设备内存。第一个使用特定于设备的数据结构来保存有关迁移内存
-的信息,HMM 将自身挂接到 mm 代码的各个位置,以处理对设备内存支持的地址的任何访问。
-事实证明,这最终复制了 struct page 的大部分字段,并且还需要更新许多内核代码路径才
-能理解这种新的内存类型。
-
-大多数内核代码路径从不尝试访问页面后面的内存,而只关心struct page的内容。正因为如此,
-HMM 切换到直接使用 struct page 用于设备内存,这使得大多数内核代码路径不知道差异。
-我们只需要确保没有人试图从 CPU 端映射这些页面。
-
-移入和移出设备内存
-==================
-
-由于 CPU 无法直接访问设备内存,因此设备驱动程序必须使用硬件 DMA 或设备特定的加载/存
-储指令来迁移数据。migrate_vma_setup()、migrate_vma_pages() 和
-migrate_vma_finalize() 函数旨在使驱动程序更易于编写并集中跨驱动程序的通用代码。
-
-在将页面迁移到设备私有内存之前,需要创建特殊的设备私有 ``struct page`` 。这些将用
-作特殊的“交换”页表条目,以便 CPU 进程在尝试访问已迁移到设备专用内存的页面时会发生异常。
-
-这些可以通过以下方式分配和释放::
-
-    struct resource *res;
-    struct dev_pagemap pagemap;
-
-    res = request_free_mem_region(&iomem_resource, /* number of bytes */,
-                                  "name of driver resource");
-    pagemap.type = MEMORY_DEVICE_PRIVATE;
-    pagemap.range.start = res->start;
-    pagemap.range.end = res->end;
-    pagemap.nr_range = 1;
-    pagemap.ops = &device_devmem_ops;
-    memremap_pages(&pagemap, numa_node_id());
-
-    memunmap_pages(&pagemap);
-    release_mem_region(pagemap.range.start, range_len(&pagemap.range));
-
-还有devm_request_free_mem_region(), devm_memremap_pages(),
-devm_memunmap_pages() 和 devm_release_mem_region() 当资源可以绑定到 ``struct device``.
-
-整体迁移步骤类似于在系统内存中迁移 NUMA 页面(see :ref:`Page migration <page_migration>`) ,
-但这些步骤分为设备驱动程序特定代码和共享公共代码:
-
-1. ``mmap_read_lock()``
-
-   设备驱动程序必须将 ``struct vm_area_struct`` 传递给migrate_vma_setup(),
-   因此需要在迁移期间保留 mmap_read_lock() 或 mmap_write_lock()。
-
-2. ``migrate_vma_setup(struct migrate_vma *args)``
-
-   设备驱动初始化了 ``struct migrate_vma`` 的字段,并将该指针传递给
-   migrate_vma_setup()。``args->flags`` 字段是用来过滤哪些源页面应该被迁移。
-   例如,设置 ``MIGRATE_VMA_SELECT_SYSTEM`` 将只迁移系统内存,设置
-   ``MIGRATE_VMA_SELECT_DEVICE_PRIVATE`` 将只迁移驻留在设备私有内存中的页
-   面。如果后者被设置, ``args->pgmap_owner`` 字段被用来识别驱动所拥有的设备
-   私有页。这就避免了试图迁移驻留在其他设备中的设备私有页。目前,只有匿名的私有VMA
-   范围可以被迁移到系统内存和设备私有内存。
-
-   migrate_vma_setup()所做的第一步是用 ``mmu_notifier_invalidate_range_start()``
-   和 ``mmu_notifier_invalidate_range_end()`` 调用来遍历设备周围的页表,使
-   其他设备的MMU无效,以便在 ``args->src`` 数组中填写要迁移的PFN。
-   ``invalidate_range_start()`` 回调传递给一个``struct mmu_notifier_range`` ,
-   其 ``event`` 字段设置为MMU_NOTIFY_MIGRATE, ``owner`` 字段设置为传递给
-   migrate_vma_setup()的 ``args->pgmap_owner`` 字段。这允许设备驱动跳过无
-   效化回调,只无效化那些实际正在迁移的设备私有MMU映射。这一点将在下一节详细解释。
-
-
-   在遍历页表时,一个 ``pte_none()`` 或 ``is_zero_pfn()`` 条目导致一个有效
-   的  “zero” PFN 存储在 ``args->src`` 阵列中。这让驱动分配设备私有内存并清
-   除它,而不是复制一个零页。到系统内存或设备私有结构页的有效PTE条目将被
-   ``lock_page()``锁定,与LRU隔离(如果系统内存和设备私有页不在LRU上),从进
-   程中取消映射,并插入一个特殊的迁移PTE来代替原来的PTE。 migrate_vma_setup()
-   还清除了 ``args->dst`` 数组。
-
-3. 设备驱动程序分配目标页面并将源页面复制到目标页面。
-
-   驱动程序检查每个 ``src`` 条目以查看该 ``MIGRATE_PFN_MIGRATE`` 位是否已
-   设置并跳过未迁移的条目。设备驱动程序还可以通过不填充页面的 ``dst`` 数组来选
-   择跳过页面迁移。
-
-   然后,驱动程序分配一个设备私有 struct page 或一个系统内存页,用 ``lock_page()``
-   锁定该页,并将 ``dst`` 数组条目填入::
-
-     dst[i] = migrate_pfn(page_to_pfn(dpage));
-
-   现在驱动程序知道这个页面正在被迁移,它可以使设备私有 MMU 映射无效并将设备私有
-   内存复制到系统内存或另一个设备私有页面。由于核心 Linux 内核会处理 CPU 页表失
-   效,因此设备驱动程序只需使其自己的 MMU 映射失效。
-
-   驱动程序可以使用 ``migrate_pfn_to_page(src[i])`` 来获取源设备的
-   ``struct page`` 面,并将源页面复制到目标设备上,如果指针为 ``NULL`` ,意
-   味着源页面没有被填充到系统内存中,则清除目标设备的私有内存。
-
-4. ``migrate_vma_pages()``
-
-   这一步是实际“提交”迁移的地方。
-
-   如果源页是 ``pte_none()`` 或 ``is_zero_pfn()`` 页,这时新分配的页会被插
-   入到CPU的页表中。如果一个CPU线程在同一页面上发生异常,这可能会失败。然而,页
-   表被锁定,只有一个新页会被插入。如果它失去了竞争,设备驱动将看到
-   ``MIGRATE_PFN_MIGRATE`` 位被清除。
-
-   如果源页被锁定、隔离等,源 ``struct page`` 信息现在被复制到目标
-   ``struct page`` ,最终完成CPU端的迁移。
-
-5. 设备驱动为仍在迁移的页面更新设备MMU页表,回滚未迁移的页面。
-
-   如果 ``src`` 条目仍然有 ``MIGRATE_PFN_MIGRATE`` 位被设置,设备驱动可以
-   更新设备MMU,如果 ``MIGRATE_PFN_WRITE`` 位被设置,则设置写启用位。
-
-6. ``migrate_vma_finalize()``
-
-   这一步用新页的页表项替换特殊的迁移页表项,并释放对源和目的 ``struct page``
-   的引用。
-
-7. ``mmap_read_unlock()``
-
-   现在可以释放锁了。
-
-独占访问存储器
-==============
-
-一些设备具有诸如原子PTE位的功能,可以用来实现对系统内存的原子访问。为了支持对一
-个共享的虚拟内存页的原子操作,这样的设备需要对该页的访问是排他的,而不是来自CPU
-的任何用户空间访问。  ``make_device_exclusive_range()`` 函数可以用来使一
-个内存范围不能从用户空间访问。
-
-这将用特殊的交换条目替换给定范围内的所有页的映射。任何试图访问交换条目的行为都会
-导致一个异常,该异常会通过用原始映射替换该条目而得到恢复。驱动程序会被通知映射已
-经被MMU通知器改变,之后它将不再有对该页的独占访问。独占访问被保证持续到驱动程序
-放弃页面锁和页面引用为止,这时页面上的任何CPU异常都可以按所述进行。
-
-内存 cgroup (memcg) 和 rss 统计
-===============================
-
-目前,设备内存被视为 rss 计数器中的任何常规页面(如果设备页面用于匿名,则为匿名,
-如果设备页面用于文件支持页面,则为文件,如果设备页面用于共享内存,则为 shmem)。
-这是为了保持现有应用程序的故意选择,这些应用程序可能在不知情的情况下开始使用设备
-内存,运行不受影响。
-
-一个缺点是 OOM 杀手可能会杀死使用大量设备内存而不是大量常规系统内存的应用程序,
-因此不会释放太多系统内存。在决定以不同方式计算设备内存之前,我们希望收集更多关
-于应用程序和系统在存在设备内存的情况下在内存压力下如何反应的实际经验。
-
-对内存 cgroup 做出了相同的决定。设备内存页面根据相同的内存 cgroup 计算,常规
-页面将被计算在内。这确实简化了进出设备内存的迁移。这也意味着从设备内存迁移回常规
-内存不会失败,因为它会超过内存 cgroup 限制。一旦我们对设备内存的使用方式及其对
-内存资源控制的影响有了更多的了解,我们可能会在后面重新考虑这个选择。
-
-请注意,设备内存永远不能由设备驱动程序或通过 GUP 固定,因此此类内存在进程退出时
-总是被释放的。或者在共享内存或文件支持内存的情况下,当删除最后一个引用时。
diff --git a/Documentation/translations/zh_CN/vm/hugetlbfs_reserv.rst b/Documentation/translations/zh_CN/vm/hugetlbfs_reserv.rst
deleted file mode 100644 (file)
index c6d471c..0000000
+++ /dev/null
@@ -1,436 +0,0 @@
-.. include:: ../disclaimer-zh_CN.rst
-
-:Original: Documentation/vm/hugetlbfs_reserv.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-==============
-Hugetlbfs 预留
-==============
-
-概述
-====
-
-:ref:`hugetlbpage` 中描述的巨页通常是预先分配给应用程序使用的。如果VMA指
-示要使用巨页,这些巨页会在缺页异常时被实例化到任务的地址空间。如果在缺页异常
-时没有巨页存在,任务就会被发送一个SIGBUS,并经常不高兴地死去。在加入巨页支
-持后不久,人们决定,在mmap()时检测巨页的短缺情况会更好。这个想法是,如果
-没有足够的巨页来覆盖映射,mmap()将失败。这首先是在mmap()时在代码中做一个
-简单的检查,以确定是否有足够的空闲巨页来覆盖映射。就像内核中的大多数东西一
-样,代码随着时间的推移而不断发展。然而,基本的想法是在mmap()时 “预留”
-巨页,以确保巨页可以用于该映射中的缺页异常。下面的描述试图描述在v4.10内核
-中是如何进行巨页预留处理的。
-
-
-读者
-====
-这个描述主要是针对正在修改hugetlbfs代码的内核开发者。
-
-
-数据结构
-========
-
-resv_huge_pages
-       这是一个全局的(per-hstate)预留的巨页的计数。预留的巨页只对预留它们的任
-       务可用。因此,一般可用的巨页的数量被计算为(``free_huge_pages - resv_huge_pages``)。
-Reserve Map
-       预留映射由以下结构体描述::
-
-               struct resv_map {
-                       struct kref refs;
-                       spinlock_t lock;
-                       struct list_head regions;
-                       long adds_in_progress;
-                       struct list_head region_cache;
-                       long region_cache_count;
-               };
-
-       系统中每个巨页映射都有一个预留映射。resv_map中的regions列表描述了映射中的
-       区域。一个区域被描述为::
-
-               struct file_region {
-                       struct list_head link;
-                       long from;
-                       long to;
-               };
-
-       file_region结构体的 ‘from’ 和 ‘to’ 字段是进入映射的巨页索引。根据映射的类型,在
-       reserv_map 中的一个区域可能表示该范围存在预留,或预留不存在。
-Flags for MAP_PRIVATE Reservations
-       这些被存储在预留的映射指针的底部。
-
-       ``#define HPAGE_RESV_OWNER    (1UL << 0)``
-               表示该任务是与该映射相关的预留的所有者。
-       ``#define HPAGE_RESV_UNMAPPED (1UL << 1)``
-               表示最初映射此范围(并创建储备)的任务由于COW失败而从该任务(子任务)中取消映
-               射了一个页面。
-Page Flags
-       PagePrivate页面标志是用来指示在释放巨页时必须恢复巨页的预留。更多细节将在
-       “释放巨页” 一节中讨论。
-
-
-预留映射位置(私有或共享)
-==========================
-
-一个巨页映射或段要么是私有的,要么是共享的。如果是私有的,它通常只对一个地址空间
-(任务)可用。如果是共享的,它可以被映射到多个地址空间(任务)。对于这两种类型的映射,
-预留映射的位置和语义是明显不同的。位置的差异是:
-
-- 对于私有映射,预留映射挂在VMA结构体上。具体来说,就是vma->vm_private_data。这个保
-  留映射是在创建映射(mmap(MAP_PRIVATE))时创建的。
-- 对于共享映射,预留映射挂在inode上。具体来说,就是inode->i_mapping->private_data。
-  由于共享映射总是由hugetlbfs文件系统中的文件支持,hugetlbfs代码确保每个节点包含一个预
-  留映射。因此,预留映射在创建节点时被分配。
-
-
-创建预留
-========
-当创建一个巨大的有页面支持的共享内存段(shmget(SHM_HUGETLB))或通过mmap(MAP_HUGETLB)
-创建一个映射时,就会创建预留。这些操作会导致对函数hugetlb_reserve_pages()的调用::
-
-       int hugetlb_reserve_pages(struct inode *inode,
-                                 long from, long to,
-                                 struct vm_area_struct *vma,
-                                 vm_flags_t vm_flags)
-
-hugetlb_reserve_pages()做的第一件事是检查在调用shmget()或mmap()时是否指定了NORESERVE
-标志。如果指定了NORESERVE,那么这个函数立即返回,因为不需要预留。
-
-参数'from'和'to'是映射或基础文件的巨页索引。对于shmget(),'from'总是0,'to'对应于段/映射
-的长度。对于mmap(),offset参数可以用来指定进入底层文件的偏移量。在这种情况下,'from'和'to'
-参数已经被这个偏移量所调整。
-
-PRIVATE和SHARED映射之间的一个很大的区别是预留在预留映射中的表示方式。
-
-- 对于共享映射,预留映射中的条目表示对应页面的预留存在或曾经存在。当预留被消耗时,预留映射不被
-  修改。
-- 对于私有映射,预留映射中没有条目表示相应页面存在预留。随着预留被消耗,条目被添加到预留映射中。
-  因此,预留映射也可用于确定哪些预留已被消耗。
-
-对于私有映射,hugetlb_reserve_pages()创建预留映射并将其挂在VMA结构体上。此外,
-HPAGE_RESV_OWNER标志被设置,以表明该VMA拥有预留。
-
-预留映射被查阅以确定当前映射/段需要多少巨页预留。对于私有映射,这始终是一个值(to - from)。
-然而,对于共享映射来说,一些预留可能已经存在于(to - from)的范围内。关于如何实现这一点的细节,
-请参见 :ref:`预留映射的修改 <resv_map_modifications>` 一节。
-
-该映射可能与一个子池(subpool)相关联。如果是这样,将查询子池以确保有足够的空间用于映射。子池
-有可能已经预留了可用于映射的预留空间。更多细节请参见 :ref: `子池预留 <sub_pool_resv>`
-一节。
-
-在咨询了预留映射和子池之后,就知道了需要的新预留数量。hugetlb_acct_memory()函数被调用以检查
-并获取所要求的预留数量。hugetlb_acct_memory()调用到可能分配和调整剩余页数的函数。然而,在这
-些函数中,代码只是检查以确保有足够的空闲的巨页来容纳预留。如果有的话,全局预留计数resv_huge_pages
-会被调整,如下所示::
-
-       if (resv_needed <= (resv_huge_pages - free_huge_pages))
-               resv_huge_pages += resv_needed;
-
-注意,在检查和调整这些计数器时,全局锁hugetlb_lock会被预留。
-
-如果有足够的空闲的巨页,并且全局计数resv_huge_pages被调整,那么与映射相关的预留映射被修改以
-反映预留。在共享映射的情况下,将存在一个file_region,包括'from'-'to'范围。对于私有映射,
-不对预留映射进行修改,因为没有条目表示存在预留。
-
-如果hugetlb_reserve_pages()成功,全局预留数和与映射相关的预留映射将根据需要被修改,以确保
-在'from'-'to'范围内存在预留。
-
-消耗预留/分配一个巨页
-===========================
-
-当与预留相关的巨页在相应的映射中被分配和实例化时,预留就被消耗了。该分配是在函数alloc_huge_page()
-中进行的::
-
-       struct page *alloc_huge_page(struct vm_area_struct *vma,
-                                    unsigned long addr, int avoid_reserve)
-
-alloc_huge_page被传递给一个VMA指针和一个虚拟地址,因此它可以查阅预留映射以确定是否存在预留。
-此外,alloc_huge_page需要一个参数avoid_reserve,该参数表示即使看起来已经为指定的地址预留了
-预留,也不应该使用预留。avoid_reserve参数最常被用于写时拷贝和页面迁移的情况下,即现有页面的额
-外拷贝被分配。
-
-
-调用辅助函数vma_needs_reservation()来确定是否存在对映射(vma)中地址的预留。关于这个函数的详
-细内容,请参见 :ref:`预留映射帮助函数 <resv_map_helpers>` 一节。从
-vma_needs_reservation()返回的值通常为0或1。如果该地址存在预留,则为0,如果不存在预留,则为1。
-如果不存在预留,并且有一个与映射相关联的子池,则查询子池以确定它是否包含预留。如果子池包含预留,
-则可将其中一个用于该分配。然而,在任何情况下,avoid_reserve参数都会优先考虑为分配使用预留。在
-确定预留是否存在并可用于分配后,调用dequeue_huge_page_vma()函数。这个函数需要两个与预留有关
-的参数:
-
-- avoid_reserve,这是传递给alloc_huge_page()的同一个值/参数。
-- chg,尽管这个参数的类型是long,但只有0或1的值被传递给dequeue_huge_page_vma。如果该值为0,
-  则表明存在预留(关于可能的问题,请参见 “预留和内存策略” 一节)。如果值
-  为1,则表示不存在预留,如果可能的话,必须从全局空闲池中取出该页。
-
-与VMA的内存策略相关的空闲列表被搜索到一个空闲页。如果找到了一个页面,当该页面从空闲列表中移除时,
-free_huge_pages的值被递减。如果有一个与该页相关的预留,将进行以下调整::
-
-       SetPagePrivate(page);   /* 表示分配这个页面消耗了一个预留,
-                                * 如果遇到错误,以至于必须释放这个页面,预留将被
-                                * 恢复。 */
-       resv_huge_pages--;      /* 减少全局预留计数 */
-
-注意,如果找不到满足VMA内存策略的巨页,将尝试使用伙伴分配器分配一个。这就带来了超出预留范围
-的剩余巨页和超额分配的问题。即使分配了一个多余的页面,也会进行与上面一样的基于预留的调整:
-SetPagePrivate(page) 和 resv_huge_pages--.
-
-在获得一个新的巨页后,(page)->private被设置为与该页面相关的子池的值,如果它存在的话。当页
-面被释放时,这将被用于子池的计数。
-
-然后调用函数vma_commit_reservation(),根据预留的消耗情况调整预留映射。一般来说,这涉及
-到确保页面在区域映射的file_region结构体中被表示。对于预留存在的共享映射,预留映射中的条目
-已经存在,所以不做任何改变。然而,如果共享映射中没有预留,或者这是一个私有映射,则必须创建一
-个新的条目。
-
-注意,如果找不到满足VMA内存策略的巨页,将尝试使用伙伴分配器分配一个。这就带来了超出预留范围
-的剩余巨页和过度分配的问题。即使分配了一个多余的页面,也会进行与上面一样的基于预留的调整。
-SetPagePrivate(page)和resv_huge_pages-。
-
-在获得一个新的巨页后,(page)->private被设置为与该页面相关的子池的值,如果它存在的话。当页
-面被释放时,这将被用于子池的计数。
-
-然后调用函数vma_commit_reservation(),根据预留的消耗情况调整预留映射。一般来说,这涉及
-到确保页面在区域映射的file_region结构体中被表示。对于预留存在的共享映射,预留映射中的条目
-已经存在,所以不做任何改变。然而,如果共享映射中没有预留,或者这是一个私有映射,则必须创建
-一个新的条目。
-
-在alloc_huge_page()开始调用vma_needs_reservation()和页面分配后调用
-vma_commit_reservation()之间,预留映射有可能被改变。如果hugetlb_reserve_pages在共
-享映射中为同一页面被调用,这将是可能的。在这种情况下,预留计数和子池空闲页计数会有一个偏差。
-这种罕见的情况可以通过比较vma_needs_reservation和vma_commit_reservation的返回值来
-识别。如果检测到这种竞争,子池和全局预留计数将被调整以进行补偿。关于这些函数的更多信息,请
-参见 :ref:`预留映射帮助函数 <resv_map_helpers>` 一节。
-
-
-实例化巨页
-==========
-
-在巨页分配之后,页面通常被添加到分配任务的页表中。在此之前,共享映射中的页面被添加到页面缓
-存中,私有映射中的页面被添加到匿名反向映射中。在这两种情况下,PagePrivate标志被清除。因此,
-当一个已经实例化的巨页被释放时,不会对全局预留计数(resv_huge_pages)进行调整。
-
-
-释放巨页
-========
-
-巨页释放是由函数free_huge_page()执行的。这个函数是hugetlbfs复合页的析构器。因此,它只传
-递一个指向页面结构体的指针。当一个巨页被释放时,可能需要进行预留计算。如果该页与包含保
-留的子池相关联,或者该页在错误路径上被释放,必须恢复全局预留计数,就会出现这种情况。
-
-page->private字段指向与该页相关的任何子池。如果PagePrivate标志被设置,它表明全局预留计数
-应该被调整(关于如何设置这些标志的信息,请参见
-:ref: `消耗预留/分配一个巨页 <consume_resv>` )。
-
-
-该函数首先调用hugepage_subpool_put_pages()来处理该页。如果这个函数返回一个0的值(不等于
-传递的1的值),它表明预留与子池相关联,这个新释放的页面必须被用来保持子池预留的数量超过最小值。
-因此,在这种情况下,全局resv_huge_pages计数器被递增。
-
-如果页面中设置了PagePrivate标志,那么全局resv_huge_pages计数器将永远被递增。
-
-子池预留
-========
-
-有一个结构体hstate与每个巨页尺寸相关联。hstate跟踪所有指定大小的巨页。一个子池代表一
-个hstate中的页面子集,它与一个已挂载的hugetlbfs文件系统相关
-
-当一个hugetlbfs文件系统被挂载时,可以指定min_size选项,它表示文件系统所需的最小的巨页数量。
-如果指定了这个选项,与min_size相对应的巨页的数量将被预留给文件系统使用。这个数字在结构体
-hugepage_subpool的min_hpages字段中被跟踪。在挂载时,hugetlb_acct_memory(min_hpages)
-被调用以预留指定数量的巨页。如果它们不能被预留,挂载就会失败。
-
-当从子池中获取或释放页面时,会调用hugepage_subpool_get/put_pages()函数。
-hugepage_subpool_get/put_pages被传递给巨页数量,以此来调整子池的 “已用页面” 计数
-(get为下降,put为上升)。通常情况下,如果子池中没有足够的页面,它们会返回与传递的相同的值或
-一个错误。
-
-然而,如果预留与子池相关联,可能会返回一个小于传递值的返回值。这个返回值表示必须进行的额外全局
-池调整的数量。例如,假设一个子池包含3个预留的巨页,有人要求5个。与子池相关的3个预留页可以用来
-满足部分请求。但是,必须从全局池中获得2个页面。为了向调用者转达这一信息,将返回值2。然后,调用
-者要负责从全局池中获取另外两个页面。
-
-
-COW和预留
-==========
-
-由于共享映射都指向并使用相同的底层页面,COW最大的预留问题是私有映射。在这种情况下,两个任务可
-以指向同一个先前分配的页面。一个任务试图写到该页,所以必须分配一个新的页,以便每个任务都指向它
-自己的页。
-
-当该页最初被分配时,该页的预留被消耗了。当由于COW而试图分配一个新的页面时,有可能没有空闲的巨
-页,分配会失败。
-
-当最初创建私有映射时,通过设置所有者的预留映射指针中的HPAGE_RESV_OWNER位来标记映射的所有者。
-由于所有者创建了映射,所有者拥有与映射相关的所有预留。因此,当一个写异常发生并且没有可用的页面
-时,对预留的所有者和非所有者采取不同的行动。
-
-在发生异常的任务不是所有者的情况下,异常将失败,该任务通常会收到一个SIGBUS。
-
-如果所有者是发生异常的任务,我们希望它能够成功,因为它拥有原始的预留。为了达到这个目的,该页被
-从非所有者任务中解映射出来。这样一来,唯一的引用就是来自拥有者的任务。此外,HPAGE_RESV_UNMAPPED
-位被设置在非拥有任务的预留映射指针中。如果非拥有者任务后来在一个不存在的页面上发生异常,它可能
-会收到一个SIGBUS。但是,映射/预留的原始拥有者的行为将与预期一致。
-
-预留映射的修改
-==============
-
-以下低级函数用于对预留映射进行修改。通常情况下,这些函数不会被直接调用。而是调用一个预留映射辅
-助函数,该函数调用这些低级函数中的一个。这些低级函数在源代码(mm/hugetlb.c)中得到了相当好的
-记录。这些函数是::
-
-       long region_chg(struct resv_map *resv, long f, long t);
-       long region_add(struct resv_map *resv, long f, long t);
-       void region_abort(struct resv_map *resv, long f, long t);
-       long region_count(struct resv_map *resv, long f, long t);
-
-在预留映射上的操作通常涉及两个操作:
-
-1) region_chg()被调用来检查预留映射,并确定在指定的范围[f, t]内有多少页目前没有被代表。
-
-   调用代码执行全局检查和分配,以确定是否有足够的巨页使操作成功。
-
-2)
-  a) 如果操作能够成功,regi_add()将被调用,以实际修改先前传递给regi_chg()的相同范围
-     [f, t]的预留映射。
-  b) 如果操作不能成功,region_abort被调用,在相同的范围[f, t]内中止操作。
-
-注意,这是一个两步的过程, region_add()和 region_abort()在事先调用 region_chg()后保证
-成功。 region_chg()负责预先分配任何必要的数据结构以确保后续操作(特别是 region_add())的
-成功。
-
-如上所述,region_chg()确定该范围内当前没有在映射中表示的页面的数量。region_add()返回添加
-到映射中的范围内的页数。在大多数情况下, region_add() 的返回值与 region_chg() 的返回值相
-同。然而,在共享映射的情况下,有可能在调用 region_chg() 和 region_add() 之间对预留映射进
-行更改。在这种情况下,regi_add()的返回值将与regi_chg()的返回值不符。在这种情况下,全局计数
-和子池计数很可能是不正确的,需要调整。检查这种情况并进行适当的调整是调用者的责任。
-
-函数region_del()被调用以从预留映射中移除区域。
-它通常在以下情况下被调用:
-
-- 当hugetlbfs文件系统中的一个文件被删除时,该节点将被释放,预留映射也被释放。在释放预留映射
-  之前,所有单独的file_region结构体必须被释放。在这种情况下,region_del的范围是[0, LONG_MAX]。
-- 当一个hugetlbfs文件正在被截断时。在这种情况下,所有在新文件大小之后分配的页面必须被释放。
-  此外,预留映射中任何超过新文件大小的file_region条目必须被删除。在这种情况下,region_del
-  的范围是[new_end_of_file, LONG_MAX]。
-- 当在一个hugetlbfs文件中打洞时。在这种情况下,巨页被一次次从文件的中间移除。当这些页被移除
-  时,region_del()被调用以从预留映射中移除相应的条目。在这种情况下,region_del被传递的范
-  围是[page_idx, page_idx + 1]。
-
-在任何情况下,region_del()都会返回从预留映射中删除的页面数量。在非常罕见的情况下,region_del()
-会失败。这只能发生在打洞的情况下,即它必须分割一个现有的file_region条目,而不能分配一个新的
-结构体。在这种错误情况下,region_del()将返回-ENOMEM。这里的问题是,预留映射将显示对该页有
-预留。然而,子池和全局预留计数将不反映该预留。为了处理这种情况,调用函数hugetlb_fix_reserve_counts()
-来调整计数器,使其与不能被删除的预留映射条目相对应。
-
-region_count()在解除私有巨页映射时被调用。在私有映射中,预留映射中没有条目表明存在一个预留。
-因此,通过计算预留映射中的条目数,我们知道有多少预留被消耗了,有多少预留是未完成的
-(Outstanding = (end - start) - region_count(resv, start, end))。由于映射正在消
-失,子池和全局预留计数被未完成的预留数量所减去。
-
-预留映射帮助函数
-================
-
-有几个辅助函数可以查询和修改预留映射。这些函数只对特定的巨页的预留感兴趣,所以它们只是传入一个
-地址而不是一个范围。此外,它们还传入相关的VMA。从VMA中,可以确定映射的类型(私有或共享)和预留
-映射的位置(inode或VMA)。这些函数只是调用 “预留映射的修改” 一节中描述的基础函数。然而,
-它们确实考虑到了私有和共享映射的预留映射条目的 “相反” 含义,并向调用者隐藏了这个细节::
-
-       long vma_needs_reservation(struct hstate *h,
-                                  struct vm_area_struct *vma,
-                                  unsigned long addr)
-
-该函数为指定的页面调用 region_chg()。如果不存在预留,则返回1。如果存在预留,则返回0::
-
-       long vma_commit_reservation(struct hstate *h,
-                                   struct vm_area_struct *vma,
-                                   unsigned long addr)
-
-这将调用 region_add(),用于指定的页面。与region_chg和region_add的情况一样,该函数应在
-先前调用的vma_needs_reservation后调用。它将为该页添加一个预留条目。如果预留被添加,它将
-返回1,如果没有则返回0。返回值应与之前调用vma_needs_reservation的返回值进行比较。如果出
-现意外的差异,说明在两次调用之间修改了预留映射::
-
-       void vma_end_reservation(struct hstate *h,
-                                struct vm_area_struct *vma,
-                                unsigned long addr)
-
-这将调用指定页面的 region_abort()。与region_chg和region_abort的情况一样,该函数应在
-先前调用的vma_needs_reservation后被调用。它将中止/结束正在进行的预留添加操作::
-
-       long vma_add_reservation(struct hstate *h,
-                                struct vm_area_struct *vma,
-                                unsigned long addr)
-
-这是一个特殊的包装函数,有助于在错误路径上清理预留。它只从repare_reserve_on_error()函数
-中调用。该函数与vma_needs_reservation一起使用,试图将一个预留添加到预留映射中。它考虑到
-了私有和共享映射的不同预留映射语义。因此,region_add被调用用于共享映射(因为映射中的条目表
-示预留),而region_del被调用用于私有映射(因为映射中没有条目表示预留)。关于在错误路径上需
-要做什么的更多信息,请参见  “错误路径中的预留清理”  。
-
-
-错误路径中的预留清理
-====================
-
-正如在:ref:`预留映射帮助函数<resv_map_helpers>` 一节中提到的,预留的修改分两步进行。首
-先,在分配页面之前调用vma_needs_reservation。如果分配成功,则调用vma_commit_reservation。
-如果不是,则调用vma_end_reservation。全局和子池的预留计数根据操作的成功或失败进行调整,
-一切都很好。
-
-此外,在一个巨页被实例化后,PagePrivate标志被清空,这样,当页面最终被释放时,计数是
-正确的。
-
-然而,有几种情况是,在一个巨页被分配后,但在它被实例化之前,就遇到了错误。在这种情况下,
-页面分配已经消耗了预留,并进行了适当的子池、预留映射和全局计数调整。如果页面在这个时候被释放
-(在实例化和清除PagePrivate之前),那么free_huge_page将增加全局预留计数。然而,预留映射
-显示报留被消耗了。这种不一致的状态将导致预留的巨页的 “泄漏” 。全局预留计数将比它原本的要高,
-并阻止分配一个预先分配的页面。
-
-函数 restore_reserve_on_error() 试图处理这种情况。它有相当完善的文档。这个函数的目的
-是将预留映射恢复到页面分配前的状态。通过这种方式,预留映射的状态将与页面释放后的全局预留计
-数相对应。
-
-函数restore_reserve_on_error本身在试图恢复预留映射条目时可能会遇到错误。在这种情况下,
-它将简单地清除该页的PagePrivate标志。这样一来,当页面被释放时,全局预留计数将不会被递增。
-然而,预留映射将继续看起来像预留被消耗了一样。一个页面仍然可以被分配到该地址,但它不会像最
-初设想的那样使用一个预留页。
-
-有一些代码(最明显的是userfaultfd)不能调用restore_reserve_on_error。在这种情况下,
-它简单地修改了PagePrivate,以便在释放巨页时不会泄露预留。
-
-
-预留和内存策略
-==============
-当git第一次被用来管理Linux代码时,每个节点的巨页列表就存在于hstate结构中。预留的概念是
-在一段时间后加入的。当预留被添加时,没有尝试将内存策略考虑在内。虽然cpusets与内存策略不
-完全相同,但hugetlb_acct_memory中的这个注释总结了预留和cpusets/内存策略之间的相互作
-用::
-
-
-       /*
-        * 当cpuset被配置时,它打破了严格的hugetlb页面预留,因为计数是在一个全局变量上完
-        * 成的。在有cpuset的情况下,这样的预留完全是垃圾,因为预留没有根据当前cpuset的
-        * 页面可用性来检查。在任务所在的cpuset中缺乏空闲的htlb页面时,应用程序仍然有可能
-        * 被内核OOM'ed。试图用cpuset来执行严格的计数几乎是不可能的(或者说太难看了),因
-        * 为cpuset太不稳定了,任务或内存节点可以在cpuset之间动态移动。与cpuset共享
-        * hugetlb映射的语义变化是不可取的。然而,为了预留一些语义,我们退回到检查当前空闲
-        * 页的可用性,作为一种最好的尝试,希望能将cpuset改变语义的影响降到最低。
-        */
-
-添加巨页预留是为了防止在缺页异常时出现意外的页面分配失败(OOM)。然而,如果一个应用
-程序使用cpusets或内存策略,就不能保证在所需的节点上有巨页可用。即使有足够数量的全局
-预留,也是如此。
-
-Hugetlbfs回归测试
-=================
-
-最完整的hugetlb测试集在libhugetlbfs仓库。如果你修改了任何hugetlb相关的代码,请使用
-libhugetlbfs测试套件来检查回归情况。此外,如果你添加了任何新的hugetlb功能,请在
-libhugetlbfs中添加适当的测试。
-
---
-Mike Kravetz,2017年4月7日
diff --git a/Documentation/translations/zh_CN/vm/hwpoison.rst b/Documentation/translations/zh_CN/vm/hwpoison.rst
deleted file mode 100644 (file)
index c6e1e7b..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-
-:Original: Documentation/vm/hwpoison.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-
-========
-hwpoison
-========
-
-什么是hwpoison?
-===============
-
-
-即将推出的英特尔CPU支持从一些内存错误中恢复( ``MCA恢复`` )。这需要操作系统宣布
-一个页面"poisoned",杀死与之相关的进程,并避免在未来使用它。
-
-这个补丁包在虚拟机中实现了必要的(编程)框架。
-
-引用概述中的评论::
-
-       高级机器的检查与处理。处理方法是损坏的页面被硬件报告,通常是由于2位ECC内
-       存或高速缓存故障。
-
-       这主要是针对在后台检测到的损坏的页面。当当前的CPU试图访问它时,当前运行的进程
-       可以直接被杀死。因为还没有访问损坏的页面, 如果错误由于某种原因不能被处理,就可
-       以安全地忽略它. 而不是用另外一个机器检查去处理它。
-
-       处理不同状态的页面缓存页。这里棘手的部分是,相对于其他虚拟内存用户, 我们可以异
-       步访问任何页面。因为内存故障可能随时随地发生,可能违反了他们的一些假设。这就是
-       为什么这段代码必须非常小心。一般来说,它试图使用正常的锁规则,如获得标准锁,即使
-       这意味着错误处理可能需要很长的时间。
-
-       这里的一些操作有点低效,并且具有非线性的算法复杂性,因为数据结构没有针对这种情
-       况进行优化。特别是从vma到进程的映射就是这种情况。由于这种情况大概率是罕见的,所
-       以我们希望我们可以摆脱这种情况。
-
-该代码由mm/memory-failure.c中的高级处理程序、一个新的页面poison位和虚拟机中的
-各种检查组成,用来处理poison的页面。
-
-现在主要目标是KVM客户机,但它适用于所有类型的应用程序。支持KVM需要最近的qemu-kvm
-版本。
-
-对于KVM的使用,需要一个新的信号类型,这样KVM就可以用适当的地址将机器检查注入到客户
-机中。这在理论上也允许其他应用程序处理内存故障。我们的期望是,所有的应用程序都不要这
-样做,但一些非常专业的应用程序可能会这样做。
-
-故障恢复模式
-============
-
-有两种(实际上是三种)模式的内存故障恢复可以在。
-
-vm.memory_failure_recovery sysctl 置零:
-       所有的内存故障都会导致panic。请不要尝试恢复。
-
-早期处理
-       (可以在全局和每个进程中控制) 一旦检测到错误,立即向应用程序发送SIGBUS这允许
-       应用程序以温和的方式处理内存错误(例如,放弃受影响的对象) 这是KVM qemu使用的
-       模式。
-
-推迟处理
-       当应用程序运行到损坏的页面时,发送SIGBUS。这对不知道内存错误的应用程序来说是
-       最好的,默认情况下注意一些页面总是被当作late kill处理。
-
-用户控制
-========
-
-vm.memory_failure_recovery
-       参阅 sysctl.txt
-
-vm.memory_failure_early_kill
-       全局启用early kill
-
-PR_MCE_KILL
-       设置early/late kill mode/revert 到系统默认值。
-
-       arg1: PR_MCE_KILL_CLEAR:
-               恢复到系统默认值
-       arg1: PR_MCE_KILL_SET:
-               arg2定义了线程特定模式
-
-               PR_MCE_KILL_EARLY:
-                       Early kill
-               PR_MCE_KILL_LATE:
-                       Late kill
-               PR_MCE_KILL_DEFAULT
-                       使用系统全局默认值
-
-       注意,如果你想有一个专门的线程代表进程处理SIGBUS(BUS_MCEERR_AO),你应该在
-       指定线程上调用prctl(PR_MCE_KILL_EARLY)。否则,SIGBUS将被发送到主线程。
-
-PR_MCE_KILL_GET
-       返回当前模式
-
-测试
-====
-
-* madvise(MADV_HWPOISON, ....) (as root) - 在测试过程中Poison一个页面
-
-* 通过debugfs ``/sys/kernel/debug/hwpoison/`` hwpoison-inject模块
-
-  corrupt-pfn
-       在PFN处注入hwpoison故障,并echoed到这个文件。这做了一些早期过滤,以避
-       免在测试套件中损坏非预期页面。
-  unpoison-pfn
-       在PFN的Software-unpoison页面对应到这个文件。这样,一个页面可以再次被
-       复用。这只对Linux注入的故障起作用,对真正的内存故障不起作用。
-
-  注意这些注入接口并不稳定,可能会在不同的内核版本中发生变化
-
-  corrupt-filter-dev-major, corrupt-filter-dev-minor
-       只处理与块设备major/minor定义的文件系统相关的页面的内存故障。-1U是通
-       配符值。这应该只用于人工注入的测试。
-
-  corrupt-filter-memcg
-       限制注入到memgroup拥有的页面。由memcg的inode号指定。
-
-       Example::
-
-               mkdir /sys/fs/cgroup/mem/hwpoison
-
-               usemem -m 100 -s 1000 &
-               echo `jobs -p` > /sys/fs/cgroup/mem/hwpoison/tasks
-
-               memcg_ino=$(ls -id /sys/fs/cgroup/mem/hwpoison | cut -f1 -d' ')
-               echo $memcg_ino > /debug/hwpoison/corrupt-filter-memcg
-
-               page-types -p `pidof init`   --hwpoison  # shall do nothing
-               page-types -p `pidof usemem` --hwpoison  # poison its pages
-
-  corrupt-filter-flags-mask, corrupt-filter-flags-value
-       当指定时,只有在((page_flags & mask) == value)的情况下才会poison页面。
-       这允许对许多种类的页面进行压力测试。page_flags与/proc/kpageflags中的相
-       同。这些标志位在include/linux/kernel-page-flags.h中定义,并在
-       Documentation/admin-guide/mm/pagemap.rst中记录。
-
-* 架构特定的MCE注入器
-
-  x86 有 mce-inject, mce-test
-
-  在mce-test中的一些便携式hwpoison测试程序,见下文。
-
-引用
-====
-
-http://halobates.de/mce-lc09-2.pdf
-       09年LinuxCon的概述演讲
-
-git://git.kernel.org/pub/scm/utils/cpu/mce/mce-test.git
-       测试套件(在tsrc中的hwpoison特定可移植测试)。
-
-git://git.kernel.org/pub/scm/utils/cpu/mce/mce-inject.git
-       x86特定的注入器
-
-
-限制
-====
-- 不是所有的页面类型都被支持,而且永远不会。大多数内核内部对象不能被恢
-  复,目前只有LRU页。
-
----
-Andi Kleen, 2009年10月
diff --git a/Documentation/translations/zh_CN/vm/index.rst b/Documentation/translations/zh_CN/vm/index.rst
deleted file mode 100644 (file)
index c77a565..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-.. include:: ../disclaimer-zh_CN.rst
-
-:Original: Documentation/vm/index.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-=================
-Linux内存管理文档
-=================
-
-这是一份关于了解Linux的内存管理子系统的指南。如果你正在寻找关于简单分配内存的
-建议,请参阅内存分配指南
-(Documentation/translations/zh_CN/core-api/memory-allocation.rst)。
-关于控制和调整的指南,请看管理指南
-(Documentation/translations/zh_CN/admin-guide/mm/index.rst)。
-
-
-.. toctree::
-   :maxdepth: 1
-
-   highmem
-
-该处剩余文档待原始文档有内容后翻译。
-
-
-遗留文档
-========
-
-这是一个关于Linux内存管理(MM)子系统内部的旧文档的集合,其中有不同层次的细节,
-包括注释和邮件列表的回复,用于阐述数据结构和算法的描述。它应该被很好地整合到上述
-结构化的文档中,如果它已经完成了它的使命,可以删除。
-
-.. toctree::
-   :maxdepth: 1
-
-   active_mm
-   balance
-   damon/index
-   free_page_reporting
-   ksm
-   frontswap
-   hmm
-   hwpoison
-   hugetlbfs_reserv
-   memory-model
-   mmu_notifier
-   numa
-   overcommit-accounting
-   page_frags
-   page_migration
-   page_owner
-   page_table_check
-   remap_file_pages
-   split_page_table_lock
-   vmalloced-kernel-stacks
-   z3fold
-   zsmalloc
-
-TODOLIST:
-* arch_pgtable_helpers
-* free_page_reporting
-* hugetlbfs_reserv
-* slub
-* transhuge
-* unevictable-lru
diff --git a/Documentation/translations/zh_CN/vm/ksm.rst b/Documentation/translations/zh_CN/vm/ksm.rst
deleted file mode 100644 (file)
index 83b0c73..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-.. include:: ../disclaimer-zh_CN.rst
-
-:Original: Documentation/vm/ksm.rst
-
-:翻译:
-
-   徐鑫 xu xin <xu.xin16@zte.com.cn>
-
-============
-内核同页合并
-============
-
-KSM 是一种节省内存的数据去重功能,由CONFIG_KSM=y启用,并在2.6.32版本时被添加
-到Linux内核。详见 ``mm/ksm.c`` 的实现,以及http://lwn.net/Articles/306704和
-https://lwn.net/Articles/330589
-
-KSM的用户空间的接口在Documentation/translations/zh_CN/admin-guide/mm/ksm.rst
-文档中有描述。
-
-设计
-====
-
-概述
-----
-
-概述内容请见mm/ksm.c文档中的“DOC: Overview”
-
-逆映射
-------
-KSM维护着稳定树中的KSM页的逆映射信息。
-
-当KSM页面的共享数小于 ``max_page_sharing`` 的虚拟内存区域(VMAs)时,则代表了
-KSM页的稳定树其中的节点指向了一个rmap_item结构体类型的列表。同时,这个KSM页
-的 ``page->mapping`` 指向了该稳定树节点。
-
-如果共享数超过了阈值,KSM将给稳定树添加第二个维度。稳定树就变成链接一个或多
-个稳定树"副本"的"链"。每个副本都保留KSM页的逆映射信息,其中 ``page->mapping``
-指向该"副本"。
-
-每个链以及链接到该链中的所有"副本"强制不变的是,它们代表了相同的写保护内存
-内容,尽管任中一个"副本"是由同一片内存区的不同的KSM复制页所指向的。
-
-这样一来,相比与无限的逆映射链表,稳定树的查找计算复杂性不受影响。但在稳定树
-本身中不能有重复的KSM页面内容仍然是强制要求。
-
-由 ``max_page_sharing`` 强制决定的数据去重限制是必要的,以此来避免虚拟内存
-rmap链表变得过大。rmap的遍历具有O(N)的复杂度,其中N是共享页面的rmap_项(即
-虚拟映射)的数量,而这个共享页面的节点数量又被 ``max_page_sharing`` 所限制。
-因此,这有效地将线性O(N)计算复杂度从rmap遍历中分散到不同的KSM页面上。ksmd进
-程在稳定节点"链"上的遍历也是O(N),但这个N是稳定树"副本"的数量,而不是rmap项
-的数量,因此它对ksmd性能没有显著影响。实际上,最佳稳定树"副本"的候选节点将
-保留在"副本"列表的开头。
-
-``max_page_sharing`` 的值设置得高了会促使更快的内存合并(因为将有更少的稳定
-树副本排队进入稳定节点chain->hlist)和更高的数据去重系数,但代价是在交换、压
-缩、NUMA平衡和页面迁移过程中可能导致KSM页的最大rmap遍历速度较慢。
-
-``stable_node_dups/stable_node_chains`` 的比值还受 ``max_page_sharing`` 调控
-的影响,高比值可能意味着稳定节点dup中存在碎片,这可以通过在ksmd中引入碎片算
-法来解决,该算法将rmap项从一个稳定节点dup重定位到另一个稳定节点dup,以便释放
-那些仅包含极少rmap项的稳定节点"dup",但这可能会增加ksmd进程的CPU使用率,并可
-能会减慢应用程序在KSM页面上的只读计算。
-
-KSM会定期扫描稳定节点"链"中链接的所有稳定树"副本",以便删减过时了的稳定节点。
-这种扫描的频率由 ``stable_node_chains_prune_millisecs`` 这个sysfs 接口定义。
-
-参考
-====
-内核代码请见mm/ksm.c。
-涉及的函数(mm_slot  ksm_scan  stable_node  rmap_item)。
diff --git a/Documentation/translations/zh_CN/vm/memory-model.rst b/Documentation/translations/zh_CN/vm/memory-model.rst
deleted file mode 100644 (file)
index 013e30c..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-:Original: Documentation/vm/memory-model.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-
-============
-物理内存模型
-============
-
-系统中的物理内存可以用不同的方式进行寻址。最简单的情况是,物理内存从地址0开
-始,跨越一个连续的范围,直到最大的地址。然而,这个范围可能包含CPU无法访问的
-小孔隙。那么,在完全不同的地址可能有几个连续的范围。而且,别忘了NUMA,即不
-同的内存库连接到不同的CPU。
-
-Linux使用两种内存模型中的一种对这种多样性进行抽象。FLATMEM和SPARSEM。每
-个架构都定义了它所支持的内存模型,默认的内存模型是什么,以及是否有可能手动
-覆盖该默认值。
-
-所有的内存模型都使用排列在一个或多个数组中的 `struct page` 来跟踪物理页
-帧的状态。
-
-无论选择哪种内存模型,物理页框号(PFN)和相应的 `struct page` 之间都存
-在一对一的映射关系。
-
-每个内存模型都定义了 :c:func:`pfn_to_page` 和 :c:func:`page_to_pfn`
-帮助函数,允许从PFN到 `struct page` 的转换,反之亦然。
-
-FLATMEM
-=======
-
-最简单的内存模型是FLATMEM。这个模型适用于非NUMA系统的连续或大部分连续的
-物理内存。
-
-在FLATMEM内存模型中,有一个全局的 `mem_map` 数组来映射整个物理内存。对
-于大多数架构,孔隙在 `mem_map` 数组中都有条目。与孔洞相对应的 `struct page`
-对象从未被完全初始化。
-
-为了分配 `mem_map` 数组,架构特定的设置代码应该调用free_area_init()函数。
-然而,在调用memblock_free_all()函数之前,映射数组是不能使用的,该函数
-将所有的内存交给页分配器。
-
-一个架构可能会释放 `mem_map` 数组中不包括实际物理页的部分。在这种情况下,特
-定架构的 :c:func:`pfn_valid` 实现应该考虑到 `mem_map` 中的孔隙。
-
-使用FLATMEM,PFN和 `struct page` 之间的转换是直接的。 `PFN - ARCH_PFN_OFFSET`
-是 `mem_map` 数组的一个索引。
-
-`ARCH_PFN_OFFSET` 定义了物理内存起始地址不同于0的系统的第一个页框号。
-
-SPARSEMEM
-=========
-
-SPARSEMEM是Linux中最通用的内存模型,它是唯一支持若干高级功能的内存模型,
-如物理内存的热插拔、非易失性内存设备的替代内存图和较大系统的内存图的延迟
-初始化。
-
-SPARSEMEM模型将物理内存显示为一个部分的集合。一个区段用mem_section结构
-体表示,它包含 `section_mem_map` ,从逻辑上讲,它是一个指向 `struct page`
-阵列的指针。然而,它被存储在一些其他的magic中,以帮助分区管理。区段的大小
-和最大区段数是使用 `SECTION_SIZE_BITS` 和 `MAX_PHYSMEM_BITS` 常量
-来指定的,这两个常量是由每个支持SPARSEMEM的架构定义的。 `MAX_PHYSMEM_BITS`
-是一个架构所支持的物理地址的实际宽度,而 `SECTION_SIZE_BITS` 是一个任
-意的值。
-
-最大的段数表示为 `NR_MEM_SECTIONS` ,定义为
-
-.. math::
-
-   NR\_MEM\_SECTIONS = 2 ^ {(MAX\_PHYSMEM\_BITS - SECTION\_SIZE\_BITS)}
-
-`mem_section` 对象被安排在一个叫做 `mem_sections` 的二维数组中。这个数组的
-大小和位置取决于 `CONFIG_SPARSEM_EXTREME` 和可能的最大段数:
-
-* 当 `CONFIG_SPARSEMEM_EXTREME` 被禁用时, `mem_sections` 数组是静态的,有
-  `NR_MEM_SECTIONS` 行。每一行持有一个 `mem_section` 对象。
-* 当 `CONFIG_SPARSEMEM_EXTREME` 被启用时, `mem_sections` 数组被动态分配。
-  每一行包含价值 `PAGE_SIZE` 的 `mem_section` 对象,行数的计算是为了适应所有的
-  内存区。
-
-架构设置代码应该调用sparse_init()来初始化内存区和内存映射。
-
-通过SPARSEMEM,有两种可能的方式将PFN转换为相应的 `struct page` --"classic sparse"和
- "sparse vmemmap"。选择是在构建时进行的,它由 `CONFIG_SPARSEMEM_VMEMMAP` 的
- 值决定。
-
-Classic sparse在page->flags中编码了一个页面的段号,并使用PFN的高位来访问映射该页
-框的段。在一个区段内,PFN是指向页数组的索引。
-
-Sparse vmemmapvmemmap使用虚拟映射的内存映射来优化pfn_to_page和page_to_pfn操
-作。有一个全局的 `struct page *vmemmap` 指针,指向一个虚拟连续的 `struct page`
-对象阵列。PFN是该数组的一个索引,`struct page` 从 `vmemmap` 的偏移量是该页的PFN。
-
-为了使用vmemmap,一个架构必须保留一个虚拟地址的范围,以映射包含内存映射的物理页,并
-确保 `vmemmap`指向该范围。此外,架构应该实现 :c:func:`vmemmap_populate` 方法,
-它将分配物理内存并为虚拟内存映射创建页表。如果一个架构对vmemmap映射没有任何特殊要求,
-它可以使用通用内存管理提供的默认 :c:func:`vmemmap_populate_basepages`。
-
-虚拟映射的内存映射允许将持久性内存设备的 `struct page` 对象存储在这些设备上预先分
-配的存储中。这种存储用vmem_altmap结构表示,最终通过一长串的函数调用传递给
-vmemmap_populate()。vmemmap_populate()实现可以使用 `vmem_altmap` 和
-:c:func:`vmemmap_alloc_block_buf` 助手来分配持久性内存设备上的内存映射。
-
-ZONE_DEVICE
-===========
-`ZONE_DEVICE` 设施建立在 `SPARSEM_VMEMMAP` 之上,为设备驱动识别的物理地址范
-围提供 `struct page` `mem_map` 服务。 `ZONE_DEVICE` 的 "设备" 方面与以下
-事实有关:这些地址范围的页面对象从未被在线标记过,而且必须对设备进行引用,而不仅仅
-是页面,以保持内存被“锁定”以便使用。 `ZONE_DEVICE` ,通过 :c:func:`devm_memremap_pages` ,
-为给定的pfns范围执行足够的内存热插拔来开启 :c:func:`pfn_to_page`,
-:c:func:`page_to_pfn`, ,和 :c:func:`get_user_pages` 服务。由于页面引
-用计数永远不会低于1,所以页面永远不会被追踪为空闲内存,页面的 `struct list_head lru`
-空间被重新利用,用于向映射该内存的主机设备/驱动程序进行反向引用。
-
-虽然 `SPARSEMEM` 将内存作为一个区段的集合,可以选择收集并合成内存块,但
-`ZONE_DEVICE` 用户需要更小的颗粒度来填充 `mem_map` 。鉴于 `ZONE_DEVICE`
-内存从未被在线标记,因此它的内存范围从未通过sysfs内存热插拔api暴露在内存块边界
-上。这个实现依赖于这种缺乏用户接口的约束,允许子段大小的内存范围被指定给
-:c:func:`arch_add_memory` ,即内存热插拔的上半部分。子段支持允许2MB作为
-:c:func:`devm_memremap_pages` 的跨架构通用对齐颗粒度。
-
-`ZONE_DEVICE` 的用户是:
-
-* pmem: 通过DAX映射将平台持久性内存作为直接I/O目标使用。
-
-* hmm: 用 `->page_fault()` 和 `->page_free()` 事件回调扩展 `ZONE_DEVICE` ,
-  以允许设备驱动程序协调与设备内存相关的内存管理事件,通常是GPU内存。参见/vm/hmm.rst。
-
-* p2pdma: 创建 `struct page` 对象,允许PCI/E拓扑结构中的peer设备协调它们之间的
-  直接DMA操作,即绕过主机内存。
diff --git a/Documentation/translations/zh_CN/vm/mmu_notifier.rst b/Documentation/translations/zh_CN/vm/mmu_notifier.rst
deleted file mode 100644 (file)
index b29a37b..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-:Original: Documentation/vm/mmu_notifier.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-
-
-什么时候需要页表锁内通知?
-==========================
-
-当清除一个pte/pmd时,我们可以选择通过在页表锁下(通知版的\*_clear_flush调用
-mmu_notifier_invalidate_range)通知事件。但这种通知并不是在所有情况下都需要的。
-
-对于二级TLB(非CPU TLB),如IOMMU TLB或设备TLB(当设备使用类似ATS/PASID的东西让
-IOMMU走CPU页表来访问进程的虚拟地址空间)。只有两种情况需要在清除pte/pmd时在持有页
-表锁的同时通知这些二级TLB:
-
-  A) 在mmu_notifier_invalidate_range_end()之前,支持页的地址被释放。
-  B) 一个页表项被更新以指向一个新的页面(COW,零页上的写异常,__replace_page(),...)。
-
-情况A很明显,你不想冒风险让设备写到一个现在可能被一些完全不同的任务使用的页面。
-
-情况B更加微妙。为了正确起见,它需要按照以下序列发生:
-
-  - 上页表锁
-  - 清除页表项并通知 ([pmd/pte]p_huge_clear_flush_notify())
-  - 设置页表项以指向新页
-
-如果在设置新的pte/pmd值之前,清除页表项之后没有进行通知,那么你就会破坏设备的C11或
-C++11等内存模型。
-
-考虑以下情况(设备使用类似于ATS/PASID的功能)。
-
-两个地址addrA和addrB,这样|addrA - addrB| >= PAGE_SIZE,我们假设它们是COW的
-写保护(B的其他情况也适用)。
-
-::
-
- [Time N] --------------------------------------------------------------------
- CPU-thread-0  {尝试写到addrA}
- CPU-thread-1  {尝试写到addrB}
- CPU-thread-2  {}
- CPU-thread-3  {}
- DEV-thread-0  {读取addrA并填充设备TLB}
- DEV-thread-2  {读取addrB并填充设备TLB}
- [Time N+1] ------------------------------------------------------------------
- CPU-thread-0  {COW_step0: {mmu_notifier_invalidate_range_start(addrA)}}
- CPU-thread-1  {COW_step0: {mmu_notifier_invalidate_range_start(addrB)}}
- CPU-thread-2  {}
- CPU-thread-3  {}
- DEV-thread-0  {}
- DEV-thread-2  {}
- [Time N+2] ------------------------------------------------------------------
- CPU-thread-0  {COW_step1: {更新页表以指向addrA的新页}}
- CPU-thread-1  {COW_step1: {更新页表以指向addrB的新页}}
- CPU-thread-2  {}
- CPU-thread-3  {}
- DEV-thread-0  {}
- DEV-thread-2  {}
- [Time N+3] ------------------------------------------------------------------
- CPU-thread-0  {preempted}
- CPU-thread-1  {preempted}
- CPU-thread-2  {写入addrA,这是对新页面的写入}
- CPU-thread-3  {}
- DEV-thread-0  {}
- DEV-thread-2  {}
- [Time N+3] ------------------------------------------------------------------
- CPU-thread-0  {preempted}
- CPU-thread-1  {preempted}
- CPU-thread-2  {}
- CPU-thread-3  {写入addrB,这是一个写入新页的过程}
- DEV-thread-0  {}
- DEV-thread-2  {}
- [Time N+4] ------------------------------------------------------------------
- CPU-thread-0  {preempted}
- CPU-thread-1  {COW_step3: {mmu_notifier_invalidate_range_end(addrB)}}
- CPU-thread-2  {}
- CPU-thread-3  {}
- DEV-thread-0  {}
- DEV-thread-2  {}
- [Time N+5] ------------------------------------------------------------------
- CPU-thread-0  {preempted}
- CPU-thread-1  {}
- CPU-thread-2  {}
- CPU-thread-3  {}
- DEV-thread-0  {从旧页中读取addrA}
- DEV-thread-2  {从新页面读取addrB}
-
-所以在这里,因为在N+2的时候,清空页表项没有和通知一起作废二级TLB,设备在看到addrA的新值之前
-就看到了addrB的新值。这就破坏了设备的总内存序。
-
-当改变一个pte的写保护或指向一个新的具有相同内容的写保护页(KSM)时,将mmu_notifier_invalidate_range
-调用延迟到页表锁外的mmu_notifier_invalidate_range_end()是可以的。即使做页表更新的线程
-在释放页表锁后但在调用mmu_notifier_invalidate_range_end()前被抢占,也是如此。
diff --git a/Documentation/translations/zh_CN/vm/numa.rst b/Documentation/translations/zh_CN/vm/numa.rst
deleted file mode 100644 (file)
index 6af412b..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-:Original: Documentation/vm/numa.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-
-始于1999年11月,作者: <kanoj@sgi.com>
-
-==========================
-何为非统一内存访问(NUMA)?
-==========================
-
-这个问题可以从几个视角来回答:硬件观点和Linux软件视角。
-
-从硬件角度看,NUMA系统是一个由多个组件或装配组成的计算机平台,每个组件可能包含0个或更多的CPU、
-本地内存和/或IO总线。为了简洁起见,并将这些物理组件/装配的硬件视角与软件抽象区分开来,我们在
-本文中称这些组件/装配为“单元”。
-
-每个“单元”都可以看作是系统的一个SMP[对称多处理器]子集——尽管独立的SMP系统所需的一些组件可能
-不会在任何给定的单元上填充。NUMA系统的单元通过某种系统互连连接在一起——例如,交叉开关或点对点
-链接是NUMA系统互连的常见类型。这两种类型的互连都可以聚合起来,以创建NUMA平台,其中的单元与其
-他单元有多个距离。
-
-对于Linux,感兴趣的NUMA平台主要是所谓的缓存相干NUMA--简称ccNUMA系统系统。在ccNUMA系统中,
-所有的内存都是可见的,并且可以从连接到任何单元的任何CPU中访问,缓存一致性是由处理器缓存和/或
-系统互连在硬件中处理。
-
-内存访问时间和有效的内存带宽取决于包含CPU的单元或进行内存访问的IO总线距离包含目标内存的单元
-有多远。例如,连接到同一单元的CPU对内存的访问将比访问其他远程单元的内存经历更快的访问时间和
-更高的带宽。 NUMA平台可以在任何给定单元上访问多种远程距离的(其他)单元。
-
-平台供应商建立NUMA系统并不只是为了让软件开发人员的生活变得有趣。相反,这种架构是提供可扩展
-内存带宽的一种手段。然而,为了实现可扩展的内存带宽,系统和应用软件必须安排大部分的内存引用
-[cache misses]到“本地”内存——同一单元的内存,如果有的话——或者到最近的有内存的单元。
-
-这就自然而然有了Linux软件对NUMA系统的视角:
-
-Linux将系统的硬件资源划分为多个软件抽象,称为“节点”。Linux将节点映射到硬件平台的物理单元
-上,对一些架构的细节进行了抽象。与物理单元一样,软件节点可能包含0或更多的CPU、内存和/或IO
-总线。同样,对“较近”节点的内存访问——映射到较近单元的节点——通常会比对较远单元的访问经历更快
-的访问时间和更高的有效带宽。
-
-对于一些架构,如x86,Linux将“隐藏”任何代表没有内存连接的物理单元的节点,并将连接到该单元
-的任何CPU重新分配到代表有内存的单元的节点上。因此,在这些架构上,我们不能假设Linux将所有
-的CPU与一个给定的节点相关联,会看到相同的本地内存访问时间和带宽。
-
-此外,对于某些架构,同样以x86为例,Linux支持对额外节点的仿真。对于NUMA仿真,Linux会将现
-有的节点或者非NUMA平台的系统内存分割成多个节点。每个模拟的节点将管理底层单元物理内存的一部
-分。NUMA仿真对于在非NUMA平台上测试NUMA内核和应用功能是非常有用的,当与cpusets一起使用时,
-可以作为一种内存资源管理机制。[见 Documentation/admin-guide/cgroup-v1/cpusets.rst]
-
-对于每个有内存的节点,Linux构建了一个独立的内存管理子系统,有自己的空闲页列表、使用中页列表、
-使用统计和锁来调解访问。此外,Linux为每个内存区[DMA、DMA32、NORMAL、HIGH_MEMORY、MOVABLE
-中的一个或多个]构建了一个有序的“区列表”。zonelist指定了当一个选定的区/节点不能满足分配请求
-时要访问的区/节点。当一个区没有可用的内存来满足请求时,这种情况被称为“overflow 溢出”或
-“fallback 回退”。
-
-由于一些节点包含多个包含不同类型内存的区,Linux必须决定是否对区列表进行排序,使分配回退到不同
-节点上的相同区类型,或同一节点上的不同区类型。这是一个重要的考虑因素,因为有些区,如DMA或DMA32,
-代表了相对稀缺的资源。Linux选择了一个默认的Node ordered zonelist。这意味着在使用按NUMA距
-离排序的远程节点之前,它会尝试回退到同一节点的其他分区。
-
-默认情况下,Linux会尝试从执行请求的CPU被分配到的节点中满足内存分配请求。具体来说,Linux将试
-图从请求来源的节点的适当分区列表中的第一个节点进行分配。这被称为“本地分配”。如果“本地”节点不能
-满足请求,内核将检查所选分区列表中其他节点的区域,寻找列表中第一个能满足请求的区域。
-
-本地分配将倾向于保持对分配的内存的后续访问 “本地”的底层物理资源和系统互连——只要内核代表其分配
-一些内存的任务后来不从该内存迁移。Linux调度器知道平台的NUMA拓扑结构——体现在“调度域”数据结构
-中[见 Documentation/scheduler/sched-domains.rst]——并且调度器试图尽量减少任务迁移到遥
-远的调度域中。然而,调度器并没有直接考虑到任务的NUMA足迹。因此,在充分不平衡的情况下,任务可
-以在节点之间迁移,远离其初始节点和内核数据结构。
-
-系统管理员和应用程序设计者可以使用各种CPU亲和命令行接口,如taskset(1)和numactl(1),以及程
-序接口,如sched_setaffinity(2),来限制任务的迁移,以改善NUMA定位。此外,人们可以使用
-Linux NUMA内存策略修改内核的默认本地分配行为。 [见
-:ref:`Documentation/admin-guide/mm/numa_memory_policy.rst <numa_memory_policy>`].
-
-系统管理员可以使用控制组和CPUsets限制非特权用户在调度或NUMA命令和功能中可以指定的CPU和节点
-的内存。 [见 Documentation/admin-guide/cgroup-v1/cpusets.rst]
-
-在不隐藏无内存节点的架构上,Linux会在分区列表中只包括有内存的区域[节点]。这意味着对于一个无
-内存的节点,“本地内存节点”——CPU节点的分区列表中的第一个区域的节点——将不是节点本身。相反,它
-将是内核在建立分区列表时选择的离它最近的有内存的节点。所以,默认情况下,本地分配将由内核提供
-最近的可用内存来完成。这是同一机制的结果,该机制允许这种分配在一个包含内存的节点溢出时回退到
-其他附近的节点。
-
-一些内核分配不希望或不能容忍这种分配回退行为。相反,他们想确保他们从指定的节点获得内存,或者
-得到通知说该节点没有空闲内存。例如,当一个子系统分配每个CPU的内存资源时,通常是这种情况。
-
-一个典型的分配模式是使用内核的numa_node_id()或CPU_to_node()函数获得“当前CPU”所在节点的
-节点ID,然后只从返回的节点ID请求内存。当这样的分配失败时,请求的子系统可以恢复到它自己的回退
-路径。板块内核内存分配器就是这样的一个例子。或者,子系统可以选择在分配失败时禁用或不启用自己。
-内核分析子系统就是这样的一个例子。
-
-如果架构支持——不隐藏无内存节点,那么连接到无内存节点的CPU将总是产生回退路径的开销,或者一些
-子系统如果试图完全从无内存的节点分配内存,将无法初始化。为了透明地支持这种架构,内核子系统可
-以使用numa_mem_id()或cpu_to_mem()函数来定位调用或指定CPU的“本地内存节点”。同样,这是同
-一个节点,默认的本地页分配将从这个节点开始尝试。
diff --git a/Documentation/translations/zh_CN/vm/overcommit-accounting.rst b/Documentation/translations/zh_CN/vm/overcommit-accounting.rst
deleted file mode 100644 (file)
index 8765cb1..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-:Original: Documentation/vm/overcommit-accounting.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-
-
-==============
-超量使用审计
-==============
-
-Linux内核支持下列超量使用处理模式
-
-0
-       启发式超量使用处理。拒绝明显的地址空间超量使用。用于一个典型的系统。
-       它确保严重的疯狂分配失败,同时允许超量使用以减少swap的使用。在这种模式下,
-       允许root分配稍多的内存。这是默认的。
-1
-       总是超量使用。适用于一些科学应用。经典的例子是使用稀疏数组的代码,只是依赖
-       几乎完全由零页组成的虚拟内存
-
-2
-       不超量使用。系统提交的总地址空间不允许超过swap+一个可配置的物理RAM的数量
-       (默认为50%)。根据你使用的数量,在大多数情况下,这意味着一个进程在访问页面时
-       不会被杀死,但会在内存分配上收到相应的错误。
-
-       对于那些想保证他们的内存分配在未来可用而又不需要初始化每一个页面的应用程序来说
-       是很有用的。
-
-超量使用策略是通过sysctl  `vm.overcommit_memory` 设置的。
-
-可以通过 `vm.overcommit_ratio` (百分比)或 `vm.overcommit_kbytes` (绝对值)
-来设置超限数量。这些只有在 `vm.overcommit_memory` 被设置为2时才有效果。
-
-在 ``/proc/meminfo`` 中可以分别以CommitLimit和Committed_AS的形式查看当前
-的超量使用和提交量。
-
-陷阱
-====
-
-C语言的堆栈增长是一个隐含的mremap。如果你想得到绝对的保证,并在接近边缘的地方运行,
-你 **必须** 为你认为你需要的最大尺寸的堆栈进行mmap。对于典型的堆栈使用来说,这并
-不重要,但如果你真的非常关心的话,这就是一个值得关注的案例。
-
-
-在模式2中,MAP_NORESERVE标志被忽略。
-
-
-它是如何工作的
-==============
-
-超量使用是基于以下规则
-
-对于文件映射
-       | SHARED or READ-only   -       0 cost (该文件是映射而不是交换)
-       | PRIVATE WRITABLE      -       每个实例的映射大小
-
-对于匿名或者 ``/dev/zero`` 映射
-       | SHARED                        -       映射的大小
-       | PRIVATE READ-only     -       0 cost (但作用不大)
-       | PRIVATE WRITABLE      -       每个实例的映射大小
-
-额外的计数
-       | 通过mmap制作可写副本的页面
-       | 从同一池中提取的shmfs内存
-
-状态
-====
-
-*      我们核算mmap内存映射
-*      我们核算mprotect在提交中的变化
-*      我们核算mremap的大小变化
-*      我们的审计 brk
-*      审计munmap
-*      我们在/proc中报告commit 状态
-*      核对并检查分叉的情况
-*      审查堆栈处理/执行中的构建
-*      叙述SHMfs的情况
-*      实现实际限制的执行
-
-待续
-====
-*      ptrace 页计数(这很难)。
diff --git a/Documentation/translations/zh_CN/vm/page_frags.rst b/Documentation/translations/zh_CN/vm/page_frags.rst
deleted file mode 100644 (file)
index 38ecddb..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-:Original: Documentation/vm/page_frags.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-
-========
-页面片段
-========
-
-一个页面片段是一个任意长度的任意偏移的内存区域,它位于一个0或更高阶的复合页面中。
-该页中的多个碎片在该页的引用计数器中被单独计算。
-
-page_frag函数,page_frag_alloc和page_frag_free,为页面片段提供了一个简单
-的分配框架。这被网络堆栈和网络设备驱动使用,以提供一个内存的支持区域,作为
-sk_buff->head使用,或者用于skb_shared_info的 “frags” 部分。
-
-为了使用页面片段API,需要一个支持页面片段的缓冲区。这为碎片分配提供了一个中心点,
-并允许多个调用使用一个缓存的页面。这样做的好处是可以避免对get_page的多次调用,
-这在分配时开销可能会很大。然而,由于这种缓存的性质,要求任何对缓存的调用都要受到每
-个CPU的限制,或者每个CPU的限制,并在执行碎片分配时强制禁止中断。
-
-网络堆栈在每个CPU使用两个独立的缓存来处理碎片分配。netdev_alloc_cache被使用
-netdev_alloc_frag和__netdev_alloc_skb调用的调用者使用。napi_alloc_cache
-被调用__napi_alloc_frag和__napi_alloc_skb的调用者使用。这两个调用的主要区别是
-它们可能被调用的环境。“netdev” 前缀的函数可以在任何上下文中使用,因为这些函数
-将禁用中断,而 ”napi“ 前缀的函数只可以在softirq上下文中使用。
-
-许多网络设备驱动程序使用类似的方法来分配页面片段,但页面片段是在环或描述符级别上
-缓存的。为了实现这些情况,有必要提供一种拆解页面缓存的通用方法。出于这个原因,
-__page_frag_cache_drain被实现了。它允许通过一次调用从一个页面释放多个引用。
-这样做的好处是,它允许清理被添加到一个页面的多个引用,以避免每次分配都调用
-get_page。
-
-Alexander Duyck,2016年11月29日。
diff --git a/Documentation/translations/zh_CN/vm/page_migration.rst b/Documentation/translations/zh_CN/vm/page_migration.rst
deleted file mode 100644 (file)
index 566880a..0000000
+++ /dev/null
@@ -1,228 +0,0 @@
-.. include:: ../disclaimer-zh_CN.rst
-
-:Original: Documentation/vm/index.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-========
-页面迁移
-========
-
-页面迁移允许在进程运行时在NUMA系统的节点之间移动页面的物理位置。这意味着进程所看到的虚拟地
-址并没有改变。然而,系统会重新安排这些页面的物理位置。
-
-也可以参见 :ref: `<异构内存管理 (HMM)>` 以了解将页面迁移到设备私有内存或从设备私有内存中迁移。
-
-页面迁移的主要目的是通过将页面移到访问该内存的进程所运行的处理器附近来减少内存访问的延迟。
-
-页面迁移允许进程通过MF_MOVE和MF_MOVE_ALL选项手动重新定位其页面所在的节点,同时通过
-mbind()设置一个新的内存策略。一个进程的页面也可以通过sys_migrate_pages()函数调用从另
-一个进程重新定位。migrate_pages()函数调用接收两组节点,并将一个进程位于旧节点上的页面移
-动到目标节点上。页面迁移功能由Andi Kleen的numactl包提供(需要0.9.3以上的版本,其仓库
-地址https://github.com/numactl/numactl.git)。numactl提供了libnuma,它为页面迁移
-提供了与其他NUMA功能类似的接口。执行 cat ``/proc/<pid>/numa_maps``  允许轻松查看进
-程的页面位置。参见proc(5)手册中的numa_maps文档。
-
-如果调度程序将一个进程重新安置到一个遥远的节点上的处理器,手动迁移是很有用的。批量调度程序
-或管理员可以检测到这种情况,并将进程的页面移到新处理器附近。内核本身只提供手动的页迁移支持。
-自动的页面迁移可以通过用户空间的进程移动页面来实现。一个特殊的函数调用 "move_pages" 允许
-在一个进程中移动单个页面。例如,NUMA分析器可以获得一个显示频繁的节点外访问的日志,并可以使
-用这个结果将页面移动到更有利的位置。
-
-较大型的设备通常使用cpusets将系统分割成若干个节点。Paul Jackson为cpusets配备了当任务被
-转移到另一个cpuset时移动页面的能力(见:ref:`CPUSETS <cpusets>`)。Cpusets允许进程定
-位的自动化。如果一个任务被移到一个新的cpuset上,那么它的所有页面也会随之移动,这样进程的
-性能就不会急剧下降。如果cpuset允许的内存节点发生变化,cpuset中的进程页也会被移动。
-
-页面迁移允许为所有迁移技术保留一组节点中页面的相对位置,这将保留生成的特定内存分配模式即使
-进程已被迁移。为了保留内存延迟,这一点是必要的。迁移后的进程将以类似的性能运行。
-
-页面迁移分几个步骤进行。首先为那些试图从内核中使用migrate_pages()的进程做一个高层次的
-描述(对于用户空间的使用,可以参考上面提到的Andi Kleen的numactl包),然后对低水平的细
-节工作做一个低水平描述。
-
-在内核中使用 migrate_pages()
-============================
-
-1. 从LRU中移除页面。
-
-   要迁移的页面列表是通过扫描页面并把它们移到列表中来生成的。这是通过调用 isolate_lru_page()
-   来完成的。调用isolate_lru_page()增加了对该页的引用,这样在页面迁移发生时它就不会
-   消失。它还可以防止交换器或其他扫描器遇到该页。
-
-
-2. 我们需要有一个new_page_t类型的函数,可以传递给migrate_pages()。这个函数应该计算
-   出如何在给定的旧页面中分配正确的新页面。
-
-3. migrate_pages()函数被调用,它试图进行迁移。它将调用该函数为每个被考虑迁移的页面分
-   配新的页面。
-
-migrate_pages()如何工作
-=======================
-
-migrate_pages()对它的页面列表进行了多次处理。如果当时对一个页面的所有引用都可以被移除,
-那么这个页面就会被移动。该页已经通过isolate_lru_page()从LRU中移除,并且refcount被
-增加,以便在页面迁移发生时不释放该页。
-
-步骤:
-
-1. 锁定要迁移的页面。
-
-2. 确保回写已经完成。
-
-3. 锁定我们要迁移到的新页面。锁定它是为了在迁移过程中立即阻止对这个(尚未更新的)页面的
-   访问。
-
-4. 所有对该页的页表引用都被转换为迁移条目。这就减少了一个页面的mapcount。如果产生的
-   mapcount不是零,那么我们就不迁移该页。所有试图访问该页的用户空间进程现在将等待页
-   面锁或者等待迁移页表项被移除。
-
-5. i_pages的锁被持有。这将导致所有试图通过映射访问该页的进程在自旋锁上阻塞。
-
-6. 检查该页的Refcount,如果还有引用,我们就退出。否则,我们知道我们是唯一引用这个页
-   面的人。
-
-7. 检查基数树,如果它不包含指向这个页面的指针,那么我们就退出,因为其他人修改了基数树。
-
-8. 新的页面要用旧的页面的一些设置进行预处理,这样访问新的页面就会发现一个具有正确设置
-   的页面。
-
-9. 基数树被改变以指向新的页面。
-
-10. 旧页的引用计数被删除,因为地址空间的引用已经消失。对新页的引用被建立,因为新页被
-    地址空间引用。
-
-11. i_pages锁被放弃。这样一来,在映射中的查找又变得可能了。进程将从在锁上自旋到在
-    被锁的新页上睡眠。
-
-12. 页面内容被复制到新的页面上。
-
-13. 剩余的页面标志被复制到新的页面上。
-
-14. 旧的页面标志被清除,以表明该页面不再提供任何信息。
-
-15. 新页面上的回写队列被触发了。
-
-16. 如果迁移条目被插入到页表中,那么就用真正的ptes替换它们。这样做将使那些尚未等待页
-    锁的用户空间进程能够访问。
-
-17. 页面锁从新旧页面上被撤销。等待页锁的进程将重做他们的缺页异常,并将到达新的页面。
-
-18. 新的页面被移到LRU中,可以被交换器等再次扫描。
-
-非LRU页面迁移
-=============
-
-尽管迁移最初的目的是为了减少NUMA的内存访问延迟,但压缩也使用迁移来创建高阶页面。
-
-目前实现的问题是,它被设计为只迁移*LRU*页。然而,有一些潜在的非LRU页面可以在驱动中
-被迁移,例如,zsmalloc,virtio-balloon页面。
-
-对于virtio-balloon页面,迁移代码路径的某些部分已经被钩住,并添加了virtio-balloon
-的特定函数来拦截迁移逻辑。这对一个驱动来说太特殊了,所以其他想让自己的页面可移动的驱
-动就必须在迁移路径中添加自己的特定钩子。
-
-为了克服这个问题,VM支持非LRU页面迁移,它为非LRU可移动页面提供了通用函数,而在迁移
-路径中没有特定的驱动程序钩子。
-
-如果一个驱动程序想让它的页面可移动,它应该定义三个函数,这些函数是
-struct address_space_operations的函数指针。
-
-1. ``bool (*isolate_page) (struct page *page, isolate_mode_t mode);``
-
-   VM对驱动的isolate_page()函数的期望是,如果驱动成功隔离了该页,则返回*true*。
-   返回true后,VM会将该页标记为PG_isolated,这样多个CPU的并发隔离就会跳过该
-   页进行隔离。如果驱动程序不能隔离该页,它应该返回*false*。
-
-   一旦页面被成功隔离,VM就会使用page.lru字段,因此驱动程序不应期望保留这些字段的值。
-
-2. ``int (*migratepage) (struct address_space *mapping,``
-|      ``struct page *newpage, struct page *oldpage, enum migrate_mode);``
-
-   隔离后,虚拟机用隔离的页面调用驱动的migratepage()。migratepage()的功能是将旧页
-   的内容移动到新页,并设置struct page newpage的字段。请记住,如果你成功迁移了旧页
-   并返回MIGRATEPAGE_SUCCESS,你应该通过page_lock下的__ClearPageMovable()向虚
-   拟机表明旧页不再可移动。如果驱动暂时不能迁移该页,驱动可以返回-EAGAIN。在-EAGAIN
-   时,VM会在短时间内重试页面迁移,因为VM将-EAGAIN理解为 "临时迁移失败"。在返回除
-   -EAGAIN以外的任何错误时,VM将放弃页面迁移而不重试。
-
-   在migratepage()函数中,驱动程序不应该接触page.lru字段。
-
-3. ``void (*putback_page)(struct page *);``
-
-   如果在隔离页上迁移失败,VM应该将隔离页返回给驱动,因此VM用隔离页调用驱动的
-   putback_page()。在这个函数中,驱动应该把隔离页放回自己的数据结构中。
-
-非LRU可移动页标志
-
-   有两个页面标志用于支持非LRU可移动页面。
-
-   * PG_movable
-
-     驱动应该使用下面的函数来使页面在page_lock下可移动。::
-
-       void __SetPageMovable(struct page *page, struct address_space *mapping)
-
-     它需要address_space的参数来注册将被VM调用的migration family函数。确切地说,
-     PG_movable不是struct page的一个真正的标志。相反,VM复用了page->mapping的低
-     位来表示它::
-
-       #define PAGE_MAPPING_MOVABLE 0x2
-       page->mapping = page->mapping | PAGE_MAPPING_MOVABLE;
-
-     所以驱动不应该直接访问page->mapping。相反,驱动应该使用page_mapping(),它可
-     以在页面锁下屏蔽掉page->mapping的低2位,从而获得正确的struct address_space。
-
-     对于非LRU可移动页面的测试,VM支持__PageMovable()函数。然而,它并不能保证识别
-     非LRU可移动页面,因为page->mapping字段与struct page中的其他变量是统一的。如
-     果驱动程序在被虚拟机隔离后释放了页面,尽管page->mapping设置了PAGE_MAPPING_MOVABLE,
-     但它并没有一个稳定的值(看看__ClearPageMovable)。但是__PageMovable()在页
-     面被隔离后,无论页面是LRU还是非LRU可移动的,调用它开销都很低,因为LRU页面在
-     page->mapping中不可能有PAGE_MAPPING_MOVABLE设置。在用pfn扫描中的lock_page()
-     进行更大开销的检查来选择受害者之前,它也很适合只是瞥一眼来测试非LRU可移动的页面。
-
-     为了保证非LRU的可移动页面,VM提供了PageMovable()函数。与__PageMovable()不
-     同,PageMovable()在lock_page()下验证page->mapping和
-     mapping->a_ops->isolate_page。lock_page()可以防止突然破坏page->mapping。
-
-     使用__SetPageMovable()的驱动应该在释放页面之前通过page_lock()下的
-     __ClearMovablePage()清除该标志。
-
-   * PG_isolated
-
-     为了防止几个CPU同时进行隔离,VM在lock_page()下将隔离的页面标记为PG_isolated。
-     因此,如果一个CPU遇到PG_isolated非LRU可移动页面,它可以跳过它。驱动程序不需要
-     操作这个标志,因为VM会自动设置/清除它。请记住,如果驱动程序看到PG_isolated页,
-     这意味着该页已经被VM隔离,所以它不应该碰page.lru字段。PG_isolated标志与
-     PG_reclaim标志是同义的,所以驱动程序不应该为自己的目的使用PG_isolated。
-
-监测迁移
-========
-
-以下事件(计数器)可用于监控页面迁移。
-
-1. PGMIGRATE_SUCCESS: 正常的页面迁移成功。每个计数器意味着一个页面被迁移了。如果该
-   页是一个非THP和非hugetlb页,那么这个计数器会增加1。如果该页面是一个THP或hugetlb
-   页面,那么这个计数器会随着THP或hugetlb子页面的数量而增加。例如,迁移一个有4KB大小
-   的基础页(子页)的2MB THP,将导致这个计数器增加512。
-
-2. PGMIGRATE_FAIL: 正常的页面迁移失败。与上面PGMIGRATE_SUCCESS的计数规则相同:如
-   果是THP或hugetlb,这个计数将被子页的数量增加。
-
-3. THP_MIGRATION_SUCCESS: 一个THP被迁移而没有被分割。
-
-4. THP_MIGRATION_FAIL: 一个THP不能被迁移,也不能被分割。
-
-5. THP_MIGRATION_SPLIT: 一个THP被迁移了,但不是这样的:首先,这个THP必须被分割。
-   在拆分之后,对它的子页面进行了迁移重试。
-
-THP_MIGRATION_* 事件也会更新相应的PGMIGRATE_SUCCESS或PGMIGRATE_FAIL事件。
-例如,一个THP迁移失败将导致THP_MIGRATION_FAIL和PGMIGRATE_FAIL增加。
-
-Christoph Lameter,2006年5月8日。
-
-Minchan Kim,2016年3月28日。
diff --git a/Documentation/translations/zh_CN/vm/page_owner.rst b/Documentation/translations/zh_CN/vm/page_owner.rst
deleted file mode 100644 (file)
index 7bd740b..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-:Original: Documentation/vm/page_owner.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-
-================================
-page owner: 跟踪谁分配的每个页面
-================================
-
-概述
-====
-
-page owner是用来追踪谁分配的每一个页面。它可以用来调试内存泄漏或找到内存占用者。
-当分配发生时,有关分配的信息,如调用堆栈和页面的顺序被存储到每个页面的特定存储中。
-当我们需要了解所有页面的状态时,我们可以获得并分析这些信息。
-
-尽管我们已经有了追踪页面分配/释放的tracepoint,但用它来分析谁分配的每个页面是
-相当复杂的。我们需要扩大跟踪缓冲区,以防止在用户空间程序启动前出现重叠。而且,启
-动的程序会不断地将跟踪缓冲区转出,供以后分析,这将会改变系统的行为,会产生更多的
-可能性,而不是仅仅保留在内存中,所以不利于调试。
-
-页面所有者也可以用于各种目的。例如,可以通过每个页面的gfp标志信息获得精确的碎片
-统计。如果启用了page owner,它就已经实现并激活了。我们非常欢迎其他用途。
-
-page owner在默认情况下是禁用的。所以,如果你想使用它,你需要在你的启动cmdline
-中加入"page_owner=on"。如果内核是用page owner构建的,并且由于没有启用启动
-选项而在运行时禁用page owner,那么运行时的开销是很小的。如果在运行时禁用,它不
-需要内存来存储所有者信息,所以没有运行时内存开销。而且,页面所有者在页面分配器的
-热路径中只插入了两个不可能的分支,如果不启用,那么分配就会像没有页面所有者的内核
-一样进行。这两个不可能的分支应该不会影响到分配的性能,特别是在静态键跳转标签修补
-功能可用的情况下。以下是由于这个功能而导致的内核代码大小的变化。
-
-- 没有page owner::
-
-   text    data     bss     dec     hex filename
-   48392   2333     644   51369    c8a9 mm/page_alloc.o
-
-- 有page owner::
-
-   text    data     bss     dec     hex filename
-   48800   2445     644   51889    cab1 mm/page_alloc.o
-   6662     108      29    6799    1a8f mm/page_owner.o
-   1025       8       8    1041     411 mm/page_ext.o
-
-虽然总共增加了8KB的代码,但page_alloc.o增加了520字节,其中不到一半是在hotpath
-中。构建带有page owner的内核,并在需要时打开它,将是调试内核内存问题的最佳选择。
-
-有一个问题是由实现细节引起的。页所有者将信息存储到struct page扩展的内存中。这
-个内存的初始化时间比稀疏内存系统中的页面分配器启动的时间要晚一些,所以,在初始化
-之前,许多页面可以被分配,但它们没有所有者信息。为了解决这个问题,这些早期分配的
-页面在初始化阶段被调查并标记为分配。虽然这并不意味着它们有正确的所有者信息,但至
-少,我们可以更准确地判断该页是否被分配。在2GB内存的x86-64虚拟机上,有13343
-个早期分配的页面被捕捉和标记,尽管它们大部分是由结构页扩展功能分配的。总之,在这
-之后,没有任何页面处于未追踪状态。
-
-使用方法
-========
-
-1) 构建用户空间的帮助::
-
-       cd tools/vm
-       make page_owner_sort
-
-2) 启用page owner: 添加 "page_owner=on" 到 boot cmdline.
-
-3) 做你想调试的工作。
-
-4) 分析来自页面所有者的信息::
-
-       cat /sys/kernel/debug/page_owner > page_owner_full.txt
-       ./page_owner_sort page_owner_full.txt sorted_page_owner.txt
-
-   ``page_owner_full.txt`` 的一般输出情况如下(输出信息无翻译价值)::
-
-       Page allocated via order XXX, ...
-       PFN XXX ...
-       // Detailed stack
-
-       Page allocated via order XXX, ...
-       PFN XXX ...
-       // Detailed stack
-
-   ``page_owner_sort`` 工具忽略了 ``PFN`` 行,将剩余的行放在buf中,使用regexp提
-   取页序值,计算buf的次数和页数,最后根据参数进行排序。
-
-   在 ``sorted_page_owner.txt`` 中可以看到关于谁分配了每个页面的结果。一般输出::
-
-       XXX times, XXX pages:
-       Page allocated via order XXX, ...
-       // Detailed stack
-
-   默认情况下, ``page_owner_sort`` 是根据buf的时间来排序的。如果你想
-   按buf的页数排序,请使用-m参数。详细的参数是:
-
-   基本函数::
-
-       排序:
-               -a              按内存分配时间排序
-               -m              按总内存排序
-               -p              按pid排序。
-               -P              按tgid排序。
-               -n              按任务命令名称排序。
-               -r              按内存释放时间排序。
-               -s              按堆栈跟踪排序。
-               -t              按时间排序(默认)。
-       --sort <order> 指定排序顺序。排序的语法是[+|-]key[,[+|-]key[,...]]。从
-       **标准格式指定器**那一节选择一个键。"+"是可选的,因为默认的方向是数字或
-       词法的增加。允许混合使用缩写和完整格式的键。
-
-        例子:
-                               ./page_owner_sort <input> <output> --sort=n,+pid,-tgid
-                               ./page_owner_sort <input> <output> --sort=at
-
-    其它函数::
-
-       剔除:
-               --cull <rules>
-                       指定剔除规则。剔除的语法是key[,key[,...]]。从**标准格式指定器**
-                               部分选择一个多字母键。
-               <rules>是一个以逗号分隔的列表形式的单一参数,它提供了一种指定单个剔除规则的
-               方法。 识别的关键字在下面的**标准格式指定器**部分有描述。<规则>可以通过键的
-               序列k1,k2,...来指定,在下面的标准排序键部分有描述。允许混合使用简写和完整形
-               式的键。
-
-               Examples:
-                               ./page_owner_sort <input> <output> --cull=stacktrace
-                               ./page_owner_sort <input> <output> --cull=st,pid,name
-                               ./page_owner_sort <input> <output> --cull=n,f
-
-       过滤:
-               -f              过滤掉内存已被释放的块的信息。
-
-       选择:
-               --pid <pidlist>         按pid选择。这将选择进程ID号出现在<pidlist>中的块。
-               --tgid <tgidlist>       按tgid选择。这将选择其线程组ID号出现在<tgidlist>
-                                   中的块。
-               --name <cmdlist>        按任务命令名称选择。这将选择其任务命令名称出现在
-                                   <cmdlist>中的区块。
-
-               <pidlist>, <tgidlist>, <cmdlist>是以逗号分隔的列表形式的单个参数,
-               它提供了一种指定单个选择规则的方法。
-
-
-               例子:
-                               ./page_owner_sort <input> <output> --pid=1
-                               ./page_owner_sort <input> <output> --tgid=1,2,3
-                               ./page_owner_sort <input> <output> --name name1,name2
-
-标准格式指定器
-==============
-::
-
-  --sort的选项:
-
-       短键          长键          描述
-       p               pid             进程ID
-       tg              tgid            线程组ID
-       n               name            任务命令名称
-       st              stacktrace      页面分配的堆栈跟踪
-       T               txt             块的全文
-       ft              free_ts         页面释放时的时间戳
-       at              alloc_ts        页面被分配时的时间戳
-       ator            allocator       页面的内存分配器
-
-  --curl的选项:
-
-       短键          长键          描述
-       p               pid             进程ID
-       tg              tgid            线程组ID
-       n               name            任务命令名称
-       f               free            该页是否已经释放
-       st              stacktrace      页面分配的堆栈跟踪
-       ator            allocator       页面的内存分配器
diff --git a/Documentation/translations/zh_CN/vm/page_table_check.rst b/Documentation/translations/zh_CN/vm/page_table_check.rst
deleted file mode 100644 (file)
index a29fc1b..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-:Original: Documentation/vm/page_table_check.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-
-========
-页表检查
-========
-
-概述
-====
-
-页表检查允许通过确保防止某些类型的内存损坏来强化内核。
-
-当新的页面可以从用户空间访问时,页表检查通过将它们的页表项(PTEs PMD等)添加到页表中来执行额外
-的验证。
-
-在检测到损坏的情况下,内核会被崩溃。页表检查有一个小的性能和内存开销。因此,它在默认情况下是禁用
-的,但是在额外的加固超过性能成本的系统上,可以选择启用。另外,由于页表检查是同步的,它可以帮助调
-试双映射内存损坏问题,在错误的映射发生时崩溃内核,而不是在内存损坏错误发生后内核崩溃。
-
-双重映射检测逻辑
-================
-
-+-------------------+-------------------+-------------------+------------------+
-| Current Mapping   | New mapping       | Permissions       | Rule             |
-+===================+===================+===================+==================+
-| Anonymous         | Anonymous         | Read              | Allow            |
-+-------------------+-------------------+-------------------+------------------+
-| Anonymous         | Anonymous         | Read / Write      | Prohibit         |
-+-------------------+-------------------+-------------------+------------------+
-| Anonymous         | Named             | Any               | Prohibit         |
-+-------------------+-------------------+-------------------+------------------+
-| Named             | Anonymous         | Any               | Prohibit         |
-+-------------------+-------------------+-------------------+------------------+
-| Named             | Named             | Any               | Allow            |
-+-------------------+-------------------+-------------------+------------------+
-
-启用页表检查
-============
-
-用以下方法构建内核:
-
-- PAGE_TABLE_CHECK=y
-  注意,它只能在ARCH_SUPPORTS_PAGE_TABLE_CHECK可用的平台上启用。
-
-- 使用 "page_table_check=on" 内核参数启动。
-
-可以选择用PAGE_TABLE_CHECK_ENFORCED来构建内核,以便在没有额外的内核参数的情况下获得页表
-支持。
diff --git a/Documentation/translations/zh_CN/vm/remap_file_pages.rst b/Documentation/translations/zh_CN/vm/remap_file_pages.rst
deleted file mode 100644 (file)
index af6b7e2..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-:Original: Documentation/vm/remap_file_pages.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-
-==============================
-remap_file_pages()系统调用
-==============================
-
-remap_file_pages()系统调用被用来创建一个非线性映射,也就是说,在这个映射中,
-文件的页面被无序映射到内存中。使用remap_file_pages()比重复调用mmap(2)的好
-处是,前者不需要内核创建额外的VMA(虚拟内存区)数据结构。
-
-支持非线性映射需要在内核虚拟内存子系统中编写大量的non-trivial的代码,包括热
-路径。另外,为了使非线性映射工作,内核需要一种方法来区分正常的页表项和带有文件
-偏移的项(pte_file)。内核为达到这个目的在PTE中保留了标志。PTE标志是稀缺资
-源,特别是在某些CPU架构上。如果能腾出这个标志用于其他用途就更好了。
-
-幸运的是,在生活中并没有很多remap_file_pages()的用户。只知道有一个企业的RDBMS
-实现在32位系统上使用这个系统调用来映射比32位虚拟地址空间线性尺寸更大的文件。
-由于64位系统的广泛使用,这种使用情况已经不重要了。
-
-syscall被废弃了,现在用一个模拟来代替它。仿真会创建新的VMA,而不是非线性映射。
-对于remap_file_pages()的少数用户来说,它的工作速度会变慢,但ABI被保留了。
-
-仿真的一个副作用(除了性能之外)是,由于额外的VMA,用户可以更容易达到
-vm.max_map_count的限制。关于限制的更多细节,请参见DEFAULT_MAX_MAP_COUNT
-的注释。
diff --git a/Documentation/translations/zh_CN/vm/split_page_table_lock.rst b/Documentation/translations/zh_CN/vm/split_page_table_lock.rst
deleted file mode 100644 (file)
index 50694d9..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-:Original: Documentation/vm/split_page_table_lock.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-
-=================================
-分页表锁(split page table lock)
-=================================
-
-最初,mm->page_table_lock spinlock保护了mm_struct的所有页表。但是这种方
-法导致了多线程应用程序的缺页异常可扩展性差,因为对锁的争夺很激烈。为了提高可扩
-展性,我们引入了分页表锁。
-
-有了分页表锁,我们就有了单独的每张表锁来顺序化对表的访问。目前,我们对PTE和
-PMD表使用分页锁。对高层表的访问由mm->page_table_lock保护。
-
-有一些辅助工具来锁定/解锁一个表和其他访问器函数:
-
- - pte_offset_map_lock()
-       映射pte并获取PTE表锁,返回所取锁的指针;
- - pte_unmap_unlock()
-       解锁和解映射PTE表;
- - pte_alloc_map_lock()
-       如果需要的话,分配PTE表并获取锁,如果分配失败,返回已获取的锁的指针
-       或NULL;
- - pte_lockptr()
-       返回指向PTE表锁的指针;
- - pmd_lock()
-       取得PMD表锁,返回所取锁的指针。
- - pmd_lockptr()
-       返回指向PMD表锁的指针;
-
-如果CONFIG_SPLIT_PTLOCK_CPUS(通常为4)小于或等于NR_CPUS,则在编译
-时启用PTE表的分页表锁。如果分页锁被禁用,所有的表都由mm->page_table_lock
-来保护。
-
-如果PMD表启用了分页锁,并且架构支持它,那么PMD表的分页锁就会被启用(见
-下文)。
-
-Hugetlb 和分页表锁
-==================
-
-Hugetlb可以支持多种页面大小。我们只对PMD级别使用分页锁,但不对PUD使用。
-
-Hugetlb特定的辅助函数:
-
- - huge_pte_lock()
-       对PMD_SIZE页面采取pmd分割锁,否则mm->page_table_lock;
- - huge_pte_lockptr()
-       返回指向表锁的指针。
-
-架构对分页表锁的支持
-====================
-
-没有必要特别启用PTE分页表锁:所有需要的东西都由pgtable_pte_page_ctor()
-和pgtable_pte_page_dtor()完成,它们必须在PTE表分配/释放时被调用。
-
-确保架构不使用slab分配器来分配页表:slab使用page->slab_cache来分配其页
-面。这个区域与page->ptl共享存储。
-
-PMD分页锁只有在你有两个以上的页表级别时才有意义。
-
-启用PMD分页锁需要在PMD表分配时调用pgtable_pmd_page_ctor(),在释放时调
-用pgtable_pmd_page_dtor()。
-
-分配通常发生在pmd_alloc_one()中,释放发生在pmd_free()和pmd_free_tlb()
-中,但要确保覆盖所有的PMD表分配/释放路径:即X86_PAE在pgd_alloc()中预先
-分配一些PMD。
-
-一切就绪后,你可以设置CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK。
-
-注意:pgtable_pte_page_ctor()和pgtable_pmd_page_ctor()可能失败--必
-须正确处理。
-
-page->ptl
-=========
-
-page->ptl用于访问分割页表锁,其中'page'是包含该表的页面struct page。它
-与page->private(以及union中的其他几个字段)共享存储。
-
-为了避免增加struct page的大小并获得最佳性能,我们使用了一个技巧:
-
- - 如果spinlock_t适合于long,我们使用page->ptr作为spinlock,这样我们
-   就可以避免间接访问并节省一个缓存行。
- - 如果spinlock_t的大小大于long的大小,我们使用page->ptl作为spinlock_t
-   的指针并动态分配它。这允许在启用DEBUG_SPINLOCK或DEBUG_LOCK_ALLOC的
-   情况下使用分页锁,但由于间接访问而多花了一个缓存行。
-
-PTE表的spinlock_t分配在pgtable_pte_page_ctor()中,PMD表的spinlock_t
-分配在pgtable_pmd_page_ctor()中。
-
-请不要直接访问page->ptl - -使用适当的辅助函数。
diff --git a/Documentation/translations/zh_CN/vm/vmalloced-kernel-stacks.rst b/Documentation/translations/zh_CN/vm/vmalloced-kernel-stacks.rst
deleted file mode 100644 (file)
index ad23f27..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-.. include:: ../disclaimer-zh_CN.rst
-
-:Original: Documentation/vm/vmalloced-kernel-stacks.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-====================
-支持虚拟映射的内核栈
-====================
-
-:作者: Shuah Khan <skhan@linuxfoundation.org>
-
-.. contents:: :local:
-
-概览
-----
-
-这是介绍 `虚拟映射内核栈功能 <https://lwn.net/Articles/694348/>` 的代码
-和原始补丁系列的信息汇总。
-
-简介
-----
-
-内核堆栈溢出通常难以调试,并使内核容易被(恶意)利用。问题可能在稍后的时间出现,使其难以
-隔离和究其根本原因。
-
-带有保护页的虚拟映射内核堆栈如果溢出,会被立即捕获,而不会放任其导致难以诊断的损
-坏。
-
-HAVE_ARCH_VMAP_STACK和VMAP_STACK配置选项能够支持带有保护页的虚拟映射堆栈。
-当堆栈溢出时,这个特性会引发可靠的异常。溢出后堆栈跟踪的可用性以及对溢出本身的
-响应取决于架构。
-
-.. note::
-        截至本文撰写时, arm64, powerpc, riscv, s390, um, 和 x86 支持VMAP_STACK。
-
-HAVE_ARCH_VMAP_STACK
---------------------
-
-能够支持虚拟映射内核栈的架构应该启用这个bool配置选项。要求是:
-
-- vmalloc空间必须大到足以容纳许多内核堆栈。这可能排除了许多32位架构。
-- vmalloc空间的堆栈需要可靠地工作。例如,如果vmap页表是按需创建的,当堆栈指向
-  具有未填充页表的虚拟地址时,这种机制需要工作,或者架构代码(switch_to()和
-  switch_mm(),很可能)需要确保堆栈的页表项在可能未填充的堆栈上运行之前已经填
-  充。
-- 如果堆栈溢出到一个保护页,就应该发生一些合理的事情。“合理”的定义是灵活的,但
-  在没有记录任何东西的情况下立即重启是不友好的。
-
-VMAP_STACK
-----------
-
-VMAP_STACK bool配置选项在启用时分配虚拟映射的任务栈。这个选项依赖于
-HAVE_ARCH_VMAP_STACK。
-
-- 如果你想使用带有保护页的虚拟映射的内核堆栈,请启用该选项。这将导致内核栈溢出
-  被立即捕获,而不是难以诊断的损坏。
-
-.. note::
-
-        使用KASAN的这个功能需要架构支持用真实的影子内存来支持虚拟映射,并且
-        必须启用KASAN_VMALLOC。
-
-.. note::
-
-        启用VMAP_STACK时,无法在堆栈分配的数据上运行DMA。
-
-内核配置选项和依赖性不断变化。请参考最新的代码库:
-
-`Kconfig <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/Kconfig>`
-
-分配方法
---------
-
-当一个新的内核线程被创建时,线程堆栈是由页级分配器分配的虚拟连续的内存页组成。这
-些页面被映射到有PAGE_KERNEL保护的连续的内核虚拟空间。
-
-alloc_thread_stack_node()调用__vmalloc_node_range()来分配带有PAGE_KERNEL
-保护的栈。
-
-- 分配的堆栈被缓存起来,以后会被新的线程重用,所以在分配/释放堆栈给任务时,要手动
-  进行memcg核算。因此,__vmalloc_node_range被调用时没有__GFP_ACCOUNT。
-- vm_struct被缓存起来,以便能够找到在中断上下文中启动的空闲线程。 free_thread_stack()
-  可以在中断上下文中调用。
-- 在arm64上,所有VMAP的堆栈都需要有相同的对齐方式,以确保VMAP的堆栈溢出检测正常
-  工作。架构特定的vmap堆栈分配器照顾到了这个细节。
-- 这并不涉及中断堆栈--参考原始补丁
-
-线程栈分配是由clone()、fork()、vfork()、kernel_thread()通过kernel_clone()
-启动的。留点提示在这,以便搜索代码库,了解线程栈何时以及如何分配。
-
-大量的代码是在:
-`kernel/fork.c <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/fork.c>`.
-
-task_struct中的stack_vm_area指针可以跟踪虚拟分配的堆栈,一个非空的stack_vm_area
-指针可以表明虚拟映射的内核堆栈已经启用。
-
-::
-
-        struct vm_struct *stack_vm_area;
-
-堆栈溢出处理
-------------
-
-前守护页和后守护页有助于检测堆栈溢出。当堆栈溢出到守护页时,处理程序必须小心不要再
-次溢出堆栈。当处理程序被调用时,很可能只留下很少的堆栈空间。
-
-在x86上,这是通过处理表明内核堆栈溢出的双异常堆栈的缺页异常来实现的。
-
-用守护页测试VMAP分配
---------------------
-
-我们如何确保VMAP_STACK在分配时确实有前守护页和后守护页的保护?下面的 lkdtm 测试
-可以帮助检测任何回归。
-
-::
-
-        void lkdtm_STACK_GUARD_PAGE_LEADING()
-        void lkdtm_STACK_GUARD_PAGE_TRAILING()
-
-结论
-----
-
-- vmalloced堆栈的percpu缓存似乎比高阶堆栈分配要快一些,至少在缓存命中时是这样。
-- THREAD_INFO_IN_TASK完全摆脱了arch-specific thread_info,并简单地将
-  thread_info(仅包含标志)和'int cpu'嵌入task_struct中。
-- 一旦任务死亡,线程栈就可以被释放(无需等待RCU),然后,如果使用vmapped栈,就
-  可以将整个栈缓存起来,以便在同一cpu上重复使用。
diff --git a/Documentation/translations/zh_CN/vm/z3fold.rst b/Documentation/translations/zh_CN/vm/z3fold.rst
deleted file mode 100644 (file)
index 57204aa..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-:Original: Documentation/vm/z3fold.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-
-======
-z3fold
-======
-
-z3fold是一个专门用于存储压缩页的分配器。它被设计为每个物理页最多可以存储三个压缩页。
-它是zbud的衍生物,允许更高的压缩率,保持其前辈的简单性和确定性。
-
-z3fold和zbud的主要区别是:
-
-* 与zbud不同的是,z3fold允许最大的PAGE_SIZE分配。
-* z3fold在其页面中最多可以容纳3个压缩页面
-* z3fold本身没有输出任何API,因此打算通过zpool的API来使用
-
-为了保持确定性和简单性,z3fold,就像zbud一样,总是在每页存储一个整数的压缩页,但是
-它最多可以存储3页,不像zbud最多可以存储2页。因此压缩率达到2.7倍左右,而zbud的压缩
-率是1.7倍左右。
-
-不像zbud(但也像zsmalloc),z3fold_alloc()那样不返回一个可重复引用的指针。相反,它
-返回一个无符号长句柄,它编码了被分配对象的实际位置。
-
-保持有效的压缩率接近于zsmalloc,z3fold不依赖于MMU的启用,并提供更可预测的回收行
-为,这使得它更适合于小型和反应迅速的系统。
diff --git a/Documentation/translations/zh_CN/vm/zsmalloc.rst b/Documentation/translations/zh_CN/vm/zsmalloc.rst
deleted file mode 100644 (file)
index 45a9b7a..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-:Original: Documentation/vm/zsmalloc.rst
-
-:翻译:
-
- 司延腾 Yanteng Si <siyanteng@loongson.cn>
-
-:校译:
-
-========
-zsmalloc
-========
-
-这个分配器是为与zram一起使用而设计的。因此,该分配器应该在低内存条件下工作良好。特别是,
-它从未尝试过higher order页面的分配,这在内存压力下很可能会失败。另一方面,如果我们只
-是使用单(0-order)页,它将遭受非常高的碎片化 - 任何大小为PAGE_SIZE/2或更大的对象将
-占据整个页面。这是其前身(xvmalloc)的主要问题之一。
-
-为了克服这些问题,zsmalloc分配了一堆0-order页面,并使用各种"struct page"字段将它
-们链接起来。这些链接的页面作为一个单一的higher order页面,即一个对象可以跨越0-order
-页面的边界。代码将这些链接的页面作为一个实体,称为zspage。
-
-为了简单起见,zsmalloc只能分配大小不超过PAGE_SIZE的对象,因为这满足了所有当前用户的
-要求(在最坏的情况下,页面是不可压缩的,因此以"原样"即未压缩的形式存储)。对于大于这
-个大小的分配请求,会返回失败(见zs_malloc)。
-
-此外,zs_malloc()并不返回一个可重复引用的指针。相反,它返回一个不透明的句柄(无符号
-长),它编码了被分配对象的实际位置。这种间接性的原因是zsmalloc并不保持zspages的永久
-映射,因为这在32位系统上会导致问题,因为内核空间映射的VA区域非常小。因此,在使用分配
-的内存之前,对象必须使用zs_map_object()进行映射以获得一个可用的指针,随后使用
-zs_unmap_object()解除映射。
-
-stat
-====
-
-通过CONFIG_ZSMALLOC_STAT,我们可以通过 ``/sys/kernel/debug/zsmalloc/<user name>``
-看到zsmalloc内部信息。下面是一个统计输出的例子。::
-
- # cat /sys/kernel/debug/zsmalloc/zram0/classes
-
- class  size almost_full almost_empty obj_allocated   obj_used pages_used pages_per_zspage
-    ...
-    ...
-     9   176           0            1           186        129          8                4
-    10   192           1            0          2880       2872        135                3
-    11   208           0            1           819        795         42                2
-    12   224           0            1           219        159         12                4
-    ...
-    ...
-
-
-class
-       索引
-size
-       zspage存储对象大小
-almost_empty
-       ZS_ALMOST_EMPTY zspage的数量(见下文)。
-almost_full
-       ZS_ALMOST_FULL zspage的数量(见下图)
-obj_allocated
-       已分配对象的数量
-obj_used
-       分配给用户的对象的数量
-pages_used
-       为该类分配的页数
-pages_per_zspage
-       组成一个zspage的0-order页面的数量
-
-当n <= N / f时,我们将一个zspage分配给ZS_ALMOST_EMPTYfullness组,其中
-
-* n = 已分配对象的数量
-* N = zspage可以存储的对象总数
-* f = fullness_threshold_frac(即,目前是4个)
-
-同样地,我们将zspage分配给:
-
-* ZS_ALMOST_FULL  when n > N / f
-* ZS_EMPTY        when n == 0
-* ZS_FULL         when n == N
index e1ce9d8..e97d7d5 100644 (file)
@@ -128,7 +128,7 @@ TODOList:
 * security/index
 * sound/index
 * crypto/index
-* vm/index
+* mm/index
 * bpf/index
 * usb/index
 * PCI/index
diff --git a/Documentation/vm/.gitignore b/Documentation/vm/.gitignore
deleted file mode 100644 (file)
index bc74f56..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-page-types
-slabinfo
diff --git a/Documentation/vm/active_mm.rst b/Documentation/vm/active_mm.rst
deleted file mode 100644 (file)
index 6f8269c..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-.. _active_mm:
-
-=========
-Active MM
-=========
-
-::
-
- List:       linux-kernel
- Subject:    Re: active_mm
- From:       Linus Torvalds <torvalds () transmeta ! com>
- Date:       1999-07-30 21:36:24
-
- Cc'd to linux-kernel, because I don't write explanations all that often,
- and when I do I feel better about more people reading them.
-
- On Fri, 30 Jul 1999, David Mosberger wrote:
- >
- > Is there a brief description someplace on how "mm" vs. "active_mm" in
- > the task_struct are supposed to be used?  (My apologies if this was
- > discussed on the mailing lists---I just returned from vacation and
- > wasn't able to follow linux-kernel for a while).
-
- Basically, the new setup is:
-
-  - we have "real address spaces" and "anonymous address spaces". The
-    difference is that an anonymous address space doesn't care about the
-    user-level page tables at all, so when we do a context switch into an
-    anonymous address space we just leave the previous address space
-    active.
-
-    The obvious use for a "anonymous address space" is any thread that
-    doesn't need any user mappings - all kernel threads basically fall into
-    this category, but even "real" threads can temporarily say that for
-    some amount of time they are not going to be interested in user space,
-    and that the scheduler might as well try to avoid wasting time on
-    switching the VM state around. Currently only the old-style bdflush
-    sync does that.
-
-  - "tsk->mm" points to the "real address space". For an anonymous process,
-    tsk->mm will be NULL, for the logical reason that an anonymous process
-    really doesn't _have_ a real address space at all.
-
-  - however, we obviously need to keep track of which address space we
-    "stole" for such an anonymous user. For that, we have "tsk->active_mm",
-    which shows what the currently active address space is.
-
-    The rule is that for a process with a real address space (ie tsk->mm is
-    non-NULL) the active_mm obviously always has to be the same as the real
-    one.
-
-    For a anonymous process, tsk->mm == NULL, and tsk->active_mm is the
-    "borrowed" mm while the anonymous process is running. When the
-    anonymous process gets scheduled away, the borrowed address space is
-    returned and cleared.
-
- To support all that, the "struct mm_struct" now has two counters: a
- "mm_users" counter that is how many "real address space users" there are,
- and a "mm_count" counter that is the number of "lazy" users (ie anonymous
- users) plus one if there are any real users.
-
- Usually there is at least one real user, but it could be that the real
- user exited on another CPU while a lazy user was still active, so you do
- actually get cases where you have a address space that is _only_ used by
- lazy users. That is often a short-lived state, because once that thread
- gets scheduled away in favour of a real thread, the "zombie" mm gets
- released because "mm_count" becomes zero.
-
- Also, a new rule is that _nobody_ ever has "init_mm" as a real MM any
- more. "init_mm" should be considered just a "lazy context when no other
- context is available", and in fact it is mainly used just at bootup when
- no real VM has yet been created. So code that used to check
-
-       if (current->mm == &init_mm)
-
- should generally just do
-
-       if (!current->mm)
-
- instead (which makes more sense anyway - the test is basically one of "do
- we have a user context", and is generally done by the page fault handler
- and things like that).
-
- Anyway, I put a pre-patch-2.3.13-1 on ftp.kernel.org just a moment ago,
- because it slightly changes the interfaces to accommodate the alpha (who
- would have thought it, but the alpha actually ends up having one of the
- ugliest context switch codes - unlike the other architectures where the MM
- and register state is separate, the alpha PALcode joins the two, and you
- need to switch both together).
-
- (From http://marc.info/?l=linux-kernel&m=93337278602211&w=2)
diff --git a/Documentation/vm/arch_pgtable_helpers.rst b/Documentation/vm/arch_pgtable_helpers.rst
deleted file mode 100644 (file)
index cbaee9e..0000000
+++ /dev/null
@@ -1,260 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-.. _arch_page_table_helpers:
-
-===============================
-Architecture Page Table Helpers
-===============================
-
-Generic MM expects architectures (with MMU) to provide helpers to create, access
-and modify page table entries at various level for different memory functions.
-These page table helpers need to conform to a common semantics across platforms.
-Following tables describe the expected semantics which can also be tested during
-boot via CONFIG_DEBUG_VM_PGTABLE option. All future changes in here or the debug
-test need to be in sync.
-
-
-PTE Page Table Helpers
-======================
-
-+---------------------------+--------------------------------------------------+
-| pte_same                  | Tests whether both PTE entries are the same      |
-+---------------------------+--------------------------------------------------+
-| pte_bad                   | Tests a non-table mapped PTE                     |
-+---------------------------+--------------------------------------------------+
-| pte_present               | Tests a valid mapped PTE                         |
-+---------------------------+--------------------------------------------------+
-| pte_young                 | Tests a young PTE                                |
-+---------------------------+--------------------------------------------------+
-| pte_dirty                 | Tests a dirty PTE                                |
-+---------------------------+--------------------------------------------------+
-| pte_write                 | Tests a writable PTE                             |
-+---------------------------+--------------------------------------------------+
-| pte_special               | Tests a special PTE                              |
-+---------------------------+--------------------------------------------------+
-| pte_protnone              | Tests a PROT_NONE PTE                            |
-+---------------------------+--------------------------------------------------+
-| pte_devmap                | Tests a ZONE_DEVICE mapped PTE                   |
-+---------------------------+--------------------------------------------------+
-| pte_soft_dirty            | Tests a soft dirty PTE                           |
-+---------------------------+--------------------------------------------------+
-| pte_swp_soft_dirty        | Tests a soft dirty swapped PTE                   |
-+---------------------------+--------------------------------------------------+
-| pte_mkyoung               | Creates a young PTE                              |
-+---------------------------+--------------------------------------------------+
-| pte_mkold                 | Creates an old PTE                               |
-+---------------------------+--------------------------------------------------+
-| pte_mkdirty               | Creates a dirty PTE                              |
-+---------------------------+--------------------------------------------------+
-| pte_mkclean               | Creates a clean PTE                              |
-+---------------------------+--------------------------------------------------+
-| pte_mkwrite               | Creates a writable PTE                           |
-+---------------------------+--------------------------------------------------+
-| pte_wrprotect             | Creates a write protected PTE                    |
-+---------------------------+--------------------------------------------------+
-| pte_mkspecial             | Creates a special PTE                            |
-+---------------------------+--------------------------------------------------+
-| pte_mkdevmap              | Creates a ZONE_DEVICE mapped PTE                 |
-+---------------------------+--------------------------------------------------+
-| pte_mksoft_dirty          | Creates a soft dirty PTE                         |
-+---------------------------+--------------------------------------------------+
-| pte_clear_soft_dirty      | Clears a soft dirty PTE                          |
-+---------------------------+--------------------------------------------------+
-| pte_swp_mksoft_dirty      | Creates a soft dirty swapped PTE                 |
-+---------------------------+--------------------------------------------------+
-| pte_swp_clear_soft_dirty  | Clears a soft dirty swapped PTE                  |
-+---------------------------+--------------------------------------------------+
-| pte_mknotpresent          | Invalidates a mapped PTE                         |
-+---------------------------+--------------------------------------------------+
-| ptep_clear                | Clears a PTE                                     |
-+---------------------------+--------------------------------------------------+
-| ptep_get_and_clear        | Clears and returns PTE                           |
-+---------------------------+--------------------------------------------------+
-| ptep_get_and_clear_full   | Clears and returns PTE (batched PTE unmap)       |
-+---------------------------+--------------------------------------------------+
-| ptep_test_and_clear_young | Clears young from a PTE                          |
-+---------------------------+--------------------------------------------------+
-| ptep_set_wrprotect        | Converts into a write protected PTE              |
-+---------------------------+--------------------------------------------------+
-| ptep_set_access_flags     | Converts into a more permissive PTE              |
-+---------------------------+--------------------------------------------------+
-
-
-PMD Page Table Helpers
-======================
-
-+---------------------------+--------------------------------------------------+
-| pmd_same                  | Tests whether both PMD entries are the same      |
-+---------------------------+--------------------------------------------------+
-| pmd_bad                   | Tests a non-table mapped PMD                     |
-+---------------------------+--------------------------------------------------+
-| pmd_leaf                  | Tests a leaf mapped PMD                          |
-+---------------------------+--------------------------------------------------+
-| pmd_huge                  | Tests a HugeTLB mapped PMD                       |
-+---------------------------+--------------------------------------------------+
-| pmd_trans_huge            | Tests a Transparent Huge Page (THP) at PMD       |
-+---------------------------+--------------------------------------------------+
-| pmd_present               | Tests a valid mapped PMD                         |
-+---------------------------+--------------------------------------------------+
-| pmd_young                 | Tests a young PMD                                |
-+---------------------------+--------------------------------------------------+
-| pmd_dirty                 | Tests a dirty PMD                                |
-+---------------------------+--------------------------------------------------+
-| pmd_write                 | Tests a writable PMD                             |
-+---------------------------+--------------------------------------------------+
-| pmd_special               | Tests a special PMD                              |
-+---------------------------+--------------------------------------------------+
-| pmd_protnone              | Tests a PROT_NONE PMD                            |
-+---------------------------+--------------------------------------------------+
-| pmd_devmap                | Tests a ZONE_DEVICE mapped PMD                   |
-+---------------------------+--------------------------------------------------+
-| pmd_soft_dirty            | Tests a soft dirty PMD                           |
-+---------------------------+--------------------------------------------------+
-| pmd_swp_soft_dirty        | Tests a soft dirty swapped PMD                   |
-+---------------------------+--------------------------------------------------+
-| pmd_mkyoung               | Creates a young PMD                              |
-+---------------------------+--------------------------------------------------+
-| pmd_mkold                 | Creates an old PMD                               |
-+---------------------------+--------------------------------------------------+
-| pmd_mkdirty               | Creates a dirty PMD                              |
-+---------------------------+--------------------------------------------------+
-| pmd_mkclean               | Creates a clean PMD                              |
-+---------------------------+--------------------------------------------------+
-| pmd_mkwrite               | Creates a writable PMD                           |
-+---------------------------+--------------------------------------------------+
-| pmd_wrprotect             | Creates a write protected PMD                    |
-+---------------------------+--------------------------------------------------+
-| pmd_mkspecial             | Creates a special PMD                            |
-+---------------------------+--------------------------------------------------+
-| pmd_mkdevmap              | Creates a ZONE_DEVICE mapped PMD                 |
-+---------------------------+--------------------------------------------------+
-| pmd_mksoft_dirty          | Creates a soft dirty PMD                         |
-+---------------------------+--------------------------------------------------+
-| pmd_clear_soft_dirty      | Clears a soft dirty PMD                          |
-+---------------------------+--------------------------------------------------+
-| pmd_swp_mksoft_dirty      | Creates a soft dirty swapped PMD                 |
-+---------------------------+--------------------------------------------------+
-| pmd_swp_clear_soft_dirty  | Clears a soft dirty swapped PMD                  |
-+---------------------------+--------------------------------------------------+
-| pmd_mkinvalid             | Invalidates a mapped PMD [1]                     |
-+---------------------------+--------------------------------------------------+
-| pmd_set_huge              | Creates a PMD huge mapping                       |
-+---------------------------+--------------------------------------------------+
-| pmd_clear_huge            | Clears a PMD huge mapping                        |
-+---------------------------+--------------------------------------------------+
-| pmdp_get_and_clear        | Clears a PMD                                     |
-+---------------------------+--------------------------------------------------+
-| pmdp_get_and_clear_full   | Clears a PMD                                     |
-+---------------------------+--------------------------------------------------+
-| pmdp_test_and_clear_young | Clears young from a PMD                          |
-+---------------------------+--------------------------------------------------+
-| pmdp_set_wrprotect        | Converts into a write protected PMD              |
-+---------------------------+--------------------------------------------------+
-| pmdp_set_access_flags     | Converts into a more permissive PMD              |
-+---------------------------+--------------------------------------------------+
-
-
-PUD Page Table Helpers
-======================
-
-+---------------------------+--------------------------------------------------+
-| pud_same                  | Tests whether both PUD entries are the same      |
-+---------------------------+--------------------------------------------------+
-| pud_bad                   | Tests a non-table mapped PUD                     |
-+---------------------------+--------------------------------------------------+
-| pud_leaf                  | Tests a leaf mapped PUD                          |
-+---------------------------+--------------------------------------------------+
-| pud_huge                  | Tests a HugeTLB mapped PUD                       |
-+---------------------------+--------------------------------------------------+
-| pud_trans_huge            | Tests a Transparent Huge Page (THP) at PUD       |
-+---------------------------+--------------------------------------------------+
-| pud_present               | Tests a valid mapped PUD                         |
-+---------------------------+--------------------------------------------------+
-| pud_young                 | Tests a young PUD                                |
-+---------------------------+--------------------------------------------------+
-| pud_dirty                 | Tests a dirty PUD                                |
-+---------------------------+--------------------------------------------------+
-| pud_write                 | Tests a writable PUD                             |
-+---------------------------+--------------------------------------------------+
-| pud_devmap                | Tests a ZONE_DEVICE mapped PUD                   |
-+---------------------------+--------------------------------------------------+
-| pud_mkyoung               | Creates a young PUD                              |
-+---------------------------+--------------------------------------------------+
-| pud_mkold                 | Creates an old PUD                               |
-+---------------------------+--------------------------------------------------+
-| pud_mkdirty               | Creates a dirty PUD                              |
-+---------------------------+--------------------------------------------------+
-| pud_mkclean               | Creates a clean PUD                              |
-+---------------------------+--------------------------------------------------+
-| pud_mkwrite               | Creates a writable PUD                           |
-+---------------------------+--------------------------------------------------+
-| pud_wrprotect             | Creates a write protected PUD                    |
-+---------------------------+--------------------------------------------------+
-| pud_mkdevmap              | Creates a ZONE_DEVICE mapped PUD                 |
-+---------------------------+--------------------------------------------------+
-| pud_mkinvalid             | Invalidates a mapped PUD [1]                     |
-+---------------------------+--------------------------------------------------+
-| pud_set_huge              | Creates a PUD huge mapping                       |
-+---------------------------+--------------------------------------------------+
-| pud_clear_huge            | Clears a PUD huge mapping                        |
-+---------------------------+--------------------------------------------------+
-| pudp_get_and_clear        | Clears a PUD                                     |
-+---------------------------+--------------------------------------------------+
-| pudp_get_and_clear_full   | Clears a PUD                                     |
-+---------------------------+--------------------------------------------------+
-| pudp_test_and_clear_young | Clears young from a PUD                          |
-+---------------------------+--------------------------------------------------+
-| pudp_set_wrprotect        | Converts into a write protected PUD              |
-+---------------------------+--------------------------------------------------+
-| pudp_set_access_flags     | Converts into a more permissive PUD              |
-+---------------------------+--------------------------------------------------+
-
-
-HugeTLB Page Table Helpers
-==========================
-
-+---------------------------+--------------------------------------------------+
-| pte_huge                  | Tests a HugeTLB                                  |
-+---------------------------+--------------------------------------------------+
-| pte_mkhuge                | Creates a HugeTLB                                |
-+---------------------------+--------------------------------------------------+
-| huge_pte_dirty            | Tests a dirty HugeTLB                            |
-+---------------------------+--------------------------------------------------+
-| huge_pte_write            | Tests a writable HugeTLB                         |
-+---------------------------+--------------------------------------------------+
-| huge_pte_mkdirty          | Creates a dirty HugeTLB                          |
-+---------------------------+--------------------------------------------------+
-| huge_pte_mkwrite          | Creates a writable HugeTLB                       |
-+---------------------------+--------------------------------------------------+
-| huge_pte_wrprotect        | Creates a write protected HugeTLB                |
-+---------------------------+--------------------------------------------------+
-| huge_ptep_get_and_clear   | Clears a HugeTLB                                 |
-+---------------------------+--------------------------------------------------+
-| huge_ptep_set_wrprotect   | Converts into a write protected HugeTLB          |
-+---------------------------+--------------------------------------------------+
-| huge_ptep_set_access_flags  | Converts into a more permissive HugeTLB        |
-+---------------------------+--------------------------------------------------+
-
-
-SWAP Page Table Helpers
-========================
-
-+---------------------------+--------------------------------------------------+
-| __pte_to_swp_entry        | Creates a swapped entry (arch) from a mapped PTE |
-+---------------------------+--------------------------------------------------+
-| __swp_to_pte_entry        | Creates a mapped PTE from a swapped entry (arch) |
-+---------------------------+--------------------------------------------------+
-| __pmd_to_swp_entry        | Creates a swapped entry (arch) from a mapped PMD |
-+---------------------------+--------------------------------------------------+
-| __swp_to_pmd_entry        | Creates a mapped PMD from a swapped entry (arch) |
-+---------------------------+--------------------------------------------------+
-| is_migration_entry        | Tests a migration (read or write) swapped entry  |
-+-------------------------------+----------------------------------------------+
-| is_writable_migration_entry   | Tests a write migration swapped entry        |
-+-------------------------------+----------------------------------------------+
-| make_readable_migration_entry | Creates a read migration swapped entry       |
-+-------------------------------+----------------------------------------------+
-| make_writable_migration_entry | Creates a write migration swapped entry      |
-+-------------------------------+----------------------------------------------+
-
-[1] https://lore.kernel.org/linux-mm/20181017020930.GN30832@redhat.com/
diff --git a/Documentation/vm/balance.rst b/Documentation/vm/balance.rst
deleted file mode 100644 (file)
index 6a1fadf..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-.. _balance:
-
-================
-Memory Balancing
-================
-
-Started Jan 2000 by Kanoj Sarcar <kanoj@sgi.com>
-
-Memory balancing is needed for !__GFP_ATOMIC and !__GFP_KSWAPD_RECLAIM as
-well as for non __GFP_IO allocations.
-
-The first reason why a caller may avoid reclaim is that the caller can not
-sleep due to holding a spinlock or is in interrupt context. The second may
-be that the caller is willing to fail the allocation without incurring the
-overhead of page reclaim. This may happen for opportunistic high-order
-allocation requests that have order-0 fallback options. In such cases,
-the caller may also wish to avoid waking kswapd.
-
-__GFP_IO allocation requests are made to prevent file system deadlocks.
-
-In the absence of non sleepable allocation requests, it seems detrimental
-to be doing balancing. Page reclamation can be kicked off lazily, that
-is, only when needed (aka zone free memory is 0), instead of making it
-a proactive process.
-
-That being said, the kernel should try to fulfill requests for direct
-mapped pages from the direct mapped pool, instead of falling back on
-the dma pool, so as to keep the dma pool filled for dma requests (atomic
-or not). A similar argument applies to highmem and direct mapped pages.
-OTOH, if there is a lot of free dma pages, it is preferable to satisfy
-regular memory requests by allocating one from the dma pool, instead
-of incurring the overhead of regular zone balancing.
-
-In 2.2, memory balancing/page reclamation would kick off only when the
-_total_ number of free pages fell below 1/64 th of total memory. With the
-right ratio of dma and regular memory, it is quite possible that balancing
-would not be done even when the dma zone was completely empty. 2.2 has
-been running production machines of varying memory sizes, and seems to be
-doing fine even with the presence of this problem. In 2.3, due to
-HIGHMEM, this problem is aggravated.
-
-In 2.3, zone balancing can be done in one of two ways: depending on the
-zone size (and possibly of the size of lower class zones), we can decide
-at init time how many free pages we should aim for while balancing any
-zone. The good part is, while balancing, we do not need to look at sizes
-of lower class zones, the bad part is, we might do too frequent balancing
-due to ignoring possibly lower usage in the lower class zones. Also,
-with a slight change in the allocation routine, it is possible to reduce
-the memclass() macro to be a simple equality.
-
-Another possible solution is that we balance only when the free memory
-of a zone _and_ all its lower class zones falls below 1/64th of the
-total memory in the zone and its lower class zones. This fixes the 2.2
-balancing problem, and stays as close to 2.2 behavior as possible. Also,
-the balancing algorithm works the same way on the various architectures,
-which have different numbers and types of zones. If we wanted to get
-fancy, we could assign different weights to free pages in different
-zones in the future.
-
-Note that if the size of the regular zone is huge compared to dma zone,
-it becomes less significant to consider the free dma pages while
-deciding whether to balance the regular zone. The first solution
-becomes more attractive then.
-
-The appended patch implements the second solution. It also "fixes" two
-problems: first, kswapd is woken up as in 2.2 on low memory conditions
-for non-sleepable allocations. Second, the HIGHMEM zone is also balanced,
-so as to give a fighting chance for replace_with_highmem() to get a
-HIGHMEM page, as well as to ensure that HIGHMEM allocations do not
-fall back into regular zone. This also makes sure that HIGHMEM pages
-are not leaked (for example, in situations where a HIGHMEM page is in
-the swapcache but is not being used by anyone)
-
-kswapd also needs to know about the zones it should balance. kswapd is
-primarily needed in a situation where balancing can not be done,
-probably because all allocation requests are coming from intr context
-and all process contexts are sleeping. For 2.3, kswapd does not really
-need to balance the highmem zone, since intr context does not request
-highmem pages. kswapd looks at the zone_wake_kswapd field in the zone
-structure to decide whether a zone needs balancing.
-
-Page stealing from process memory and shm is done if stealing the page would
-alleviate memory pressure on any zone in the page's node that has fallen below
-its watermark.
-
-watemark[WMARK_MIN/WMARK_LOW/WMARK_HIGH]/low_on_memory/zone_wake_kswapd: These
-are per-zone fields, used to determine when a zone needs to be balanced. When
-the number of pages falls below watermark[WMARK_MIN], the hysteric field
-low_on_memory gets set. This stays set till the number of free pages becomes
-watermark[WMARK_HIGH]. When low_on_memory is set, page allocation requests will
-try to free some pages in the zone (providing GFP_WAIT is set in the request).
-Orthogonal to this, is the decision to poke kswapd to free some zone pages.
-That decision is not hysteresis based, and is done when the number of free
-pages is below watermark[WMARK_LOW]; in which case zone_wake_kswapd is also set.
-
-
-(Good) Ideas that I have heard:
-
-1. Dynamic experience should influence balancing: number of failed requests
-   for a zone can be tracked and fed into the balancing scheme (jalvo@mbay.net)
-2. Implement a replace_with_highmem()-like replace_with_regular() to preserve
-   dma pages. (lkd@tantalophile.demon.co.uk)
diff --git a/Documentation/vm/bootmem.rst b/Documentation/vm/bootmem.rst
deleted file mode 100644 (file)
index eb2b31e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-===========
-Boot Memory
-===========
diff --git a/Documentation/vm/damon/api.rst b/Documentation/vm/damon/api.rst
deleted file mode 100644 (file)
index 08f34df..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-=============
-API Reference
-=============
-
-Kernel space programs can use every feature of DAMON using below APIs.  All you
-need to do is including ``damon.h``, which is located in ``include/linux/`` of
-the source tree.
-
-Structures
-==========
-
-.. kernel-doc:: include/linux/damon.h
-
-
-Functions
-=========
-
-.. kernel-doc:: mm/damon/core.c
diff --git a/Documentation/vm/damon/design.rst b/Documentation/vm/damon/design.rst
deleted file mode 100644 (file)
index 0cff6fa..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-======
-Design
-======
-
-Configurable Layers
-===================
-
-DAMON provides data access monitoring functionality while making the accuracy
-and the overhead controllable.  The fundamental access monitorings require
-primitives that dependent on and optimized for the target address space.  On
-the other hand, the accuracy and overhead tradeoff mechanism, which is the core
-of DAMON, is in the pure logic space.  DAMON separates the two parts in
-different layers and defines its interface to allow various low level
-primitives implementations configurable with the core logic.  We call the low
-level primitives implementations monitoring operations.
-
-Due to this separated design and the configurable interface, users can extend
-DAMON for any address space by configuring the core logics with appropriate
-monitoring operations.  If appropriate one is not provided, users can implement
-the operations on their own.
-
-For example, physical memory, virtual memory, swap space, those for specific
-processes, NUMA nodes, files, and backing memory devices would be supportable.
-Also, if some architectures or devices support special optimized access check
-primitives, those will be easily configurable.
-
-
-Reference Implementations of Address Space Specific Monitoring Operations
-=========================================================================
-
-The monitoring operations are defined in two parts:
-
-1. Identification of the monitoring target address range for the address space.
-2. Access check of specific address range in the target space.
-
-DAMON currently provides the implementations of the operations for the physical
-and virtual address spaces. Below two subsections describe how those work.
-
-
-VMA-based Target Address Range Construction
--------------------------------------------
-
-This is only for the virtual address space monitoring operations
-implementation.  That for the physical address space simply asks users to
-manually set the monitoring target address ranges.
-
-Only small parts in the super-huge virtual address space of the processes are
-mapped to the physical memory and accessed.  Thus, tracking the unmapped
-address regions is just wasteful.  However, because DAMON can deal with some
-level of noise using the adaptive regions adjustment mechanism, tracking every
-mapping is not strictly required but could even incur a high overhead in some
-cases.  That said, too huge unmapped areas inside the monitoring target should
-be removed to not take the time for the adaptive mechanism.
-
-For the reason, this implementation converts the complex mappings to three
-distinct regions that cover every mapped area of the address space.  The two
-gaps between the three regions are the two biggest unmapped areas in the given
-address space.  The two biggest unmapped areas would be the gap between the
-heap and the uppermost mmap()-ed region, and the gap between the lowermost
-mmap()-ed region and the stack in most of the cases.  Because these gaps are
-exceptionally huge in usual address spaces, excluding these will be sufficient
-to make a reasonable trade-off.  Below shows this in detail::
-
-    <heap>
-    <BIG UNMAPPED REGION 1>
-    <uppermost mmap()-ed region>
-    (small mmap()-ed regions and munmap()-ed regions)
-    <lowermost mmap()-ed region>
-    <BIG UNMAPPED REGION 2>
-    <stack>
-
-
-PTE Accessed-bit Based Access Check
------------------------------------
-
-Both of the implementations for physical and virtual address spaces use PTE
-Accessed-bit for basic access checks.  Only one difference is the way of
-finding the relevant PTE Accessed bit(s) from the address.  While the
-implementation for the virtual address walks the page table for the target task
-of the address, the implementation for the physical address walks every page
-table having a mapping to the address.  In this way, the implementations find
-and clear the bit(s) for next sampling target address and checks whether the
-bit(s) set again after one sampling period.  This could disturb other kernel
-subsystems using the Accessed bits, namely Idle page tracking and the reclaim
-logic.  DAMON does nothing to avoid disturbing Idle page tracking, so handling
-the interference is the responsibility of sysadmins.  However, it solves the
-conflict with the reclaim logic using ``PG_idle`` and ``PG_young`` page flags,
-as Idle page tracking does.
-
-
-Address Space Independent Core Mechanisms
-=========================================
-
-Below four sections describe each of the DAMON core mechanisms and the five
-monitoring attributes, ``sampling interval``, ``aggregation interval``,
-``update interval``, ``minimum number of regions``, and ``maximum number of
-regions``.
-
-
-Access Frequency Monitoring
----------------------------
-
-The output of DAMON says what pages are how frequently accessed for a given
-duration.  The resolution of the access frequency is controlled by setting
-``sampling interval`` and ``aggregation interval``.  In detail, DAMON checks
-access to each page per ``sampling interval`` and aggregates the results.  In
-other words, counts the number of the accesses to each page.  After each
-``aggregation interval`` passes, DAMON calls callback functions that previously
-registered by users so that users can read the aggregated results and then
-clears the results.  This can be described in below simple pseudo-code::
-
-    while monitoring_on:
-        for page in monitoring_target:
-            if accessed(page):
-                nr_accesses[page] += 1
-        if time() % aggregation_interval == 0:
-            for callback in user_registered_callbacks:
-                callback(monitoring_target, nr_accesses)
-            for page in monitoring_target:
-                nr_accesses[page] = 0
-        sleep(sampling interval)
-
-The monitoring overhead of this mechanism will arbitrarily increase as the
-size of the target workload grows.
-
-
-Region Based Sampling
----------------------
-
-To avoid the unbounded increase of the overhead, DAMON groups adjacent pages
-that assumed to have the same access frequencies into a region.  As long as the
-assumption (pages in a region have the same access frequencies) is kept, only
-one page in the region is required to be checked.  Thus, for each ``sampling
-interval``, DAMON randomly picks one page in each region, waits for one
-``sampling interval``, checks whether the page is accessed meanwhile, and
-increases the access frequency of the region if so.  Therefore, the monitoring
-overhead is controllable by setting the number of regions.  DAMON allows users
-to set the minimum and the maximum number of regions for the trade-off.
-
-This scheme, however, cannot preserve the quality of the output if the
-assumption is not guaranteed.
-
-
-Adaptive Regions Adjustment
----------------------------
-
-Even somehow the initial monitoring target regions are well constructed to
-fulfill the assumption (pages in same region have similar access frequencies),
-the data access pattern can be dynamically changed.  This will result in low
-monitoring quality.  To keep the assumption as much as possible, DAMON
-adaptively merges and splits each region based on their access frequency.
-
-For each ``aggregation interval``, it compares the access frequencies of
-adjacent regions and merges those if the frequency difference is small.  Then,
-after it reports and clears the aggregated access frequency of each region, it
-splits each region into two or three regions if the total number of regions
-will not exceed the user-specified maximum number of regions after the split.
-
-In this way, DAMON provides its best-effort quality and minimal overhead while
-keeping the bounds users set for their trade-off.
-
-
-Dynamic Target Space Updates Handling
--------------------------------------
-
-The monitoring target address range could dynamically changed.  For example,
-virtual memory could be dynamically mapped and unmapped.  Physical memory could
-be hot-plugged.
-
-As the changes could be quite frequent in some cases, DAMON allows the
-monitoring operations to check dynamic changes including memory mapping changes
-and applies it to monitoring operations-related data structures such as the
-abstracted monitoring target memory area only for each of a user-specified time
-interval (``update interval``).
diff --git a/Documentation/vm/damon/faq.rst b/Documentation/vm/damon/faq.rst
deleted file mode 100644 (file)
index dde7e24..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-==========================
-Frequently Asked Questions
-==========================
-
-Why a new subsystem, instead of extending perf or other user space tools?
-=========================================================================
-
-First, because it needs to be lightweight as much as possible so that it can be
-used online, any unnecessary overhead such as kernel - user space context
-switching cost should be avoided.  Second, DAMON aims to be used by other
-programs including the kernel.  Therefore, having a dependency on specific
-tools like perf is not desirable.  These are the two biggest reasons why DAMON
-is implemented in the kernel space.
-
-
-Can 'idle pages tracking' or 'perf mem' substitute DAMON?
-=========================================================
-
-Idle page tracking is a low level primitive for access check of the physical
-address space.  'perf mem' is similar, though it can use sampling to minimize
-the overhead.  On the other hand, DAMON is a higher-level framework for the
-monitoring of various address spaces.  It is focused on memory management
-optimization and provides sophisticated accuracy/overhead handling mechanisms.
-Therefore, 'idle pages tracking' and 'perf mem' could provide a subset of
-DAMON's output, but cannot substitute DAMON.
-
-
-Does DAMON support virtual memory only?
-=======================================
-
-No.  The core of the DAMON is address space independent.  The address space
-specific monitoring operations including monitoring target regions
-constructions and actual access checks can be implemented and configured on the
-DAMON core by the users.  In this way, DAMON users can monitor any address
-space with any access check technique.
-
-Nonetheless, DAMON provides vma/rmap tracking and PTE Accessed bit check based
-implementations of the address space dependent functions for the virtual memory
-and the physical memory by default, for a reference and convenient use.
-
-
-Can I simply monitor page granularity?
-======================================
-
-Yes.  You can do so by setting the ``min_nr_regions`` attribute higher than the
-working set size divided by the page size.  Because the monitoring target
-regions size is forced to be ``>=page size``, the region split will make no
-effect.
diff --git a/Documentation/vm/damon/index.rst b/Documentation/vm/damon/index.rst
deleted file mode 100644 (file)
index 48c0bbf..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-==========================
-DAMON: Data Access MONitor
-==========================
-
-DAMON is a data access monitoring framework subsystem for the Linux kernel.
-The core mechanisms of DAMON (refer to :doc:`design` for the detail) make it
-
- - *accurate* (the monitoring output is useful enough for DRAM level memory
-   management; It might not appropriate for CPU Cache levels, though),
- - *light-weight* (the monitoring overhead is low enough to be applied online),
-   and
- - *scalable* (the upper-bound of the overhead is in constant range regardless
-   of the size of target workloads).
-
-Using this framework, therefore, the kernel's memory management mechanisms can
-make advanced decisions.  Experimental memory management optimization works
-that incurring high data accesses monitoring overhead could implemented again.
-In user space, meanwhile, users who have some special workloads can write
-personalized applications for better understanding and optimizations of their
-workloads and systems.
-
-.. toctree::
-   :maxdepth: 2
-
-   faq
-   design
-   api
diff --git a/Documentation/vm/free_page_reporting.rst b/Documentation/vm/free_page_reporting.rst
deleted file mode 100644 (file)
index 8c05e62..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-.. _free_page_reporting:
-
-=====================
-Free Page Reporting
-=====================
-
-Free page reporting is an API by which a device can register to receive
-lists of pages that are currently unused by the system. This is useful in
-the case of virtualization where a guest is then able to use this data to
-notify the hypervisor that it is no longer using certain pages in memory.
-
-For the driver, typically a balloon driver, to use of this functionality
-it will allocate and initialize a page_reporting_dev_info structure. The
-field within the structure it will populate is the "report" function
-pointer used to process the scatterlist. It must also guarantee that it can
-handle at least PAGE_REPORTING_CAPACITY worth of scatterlist entries per
-call to the function. A call to page_reporting_register will register the
-page reporting interface with the reporting framework assuming no other
-page reporting devices are already registered.
-
-Once registered the page reporting API will begin reporting batches of
-pages to the driver. The API will start reporting pages 2 seconds after
-the interface is registered and will continue to do so 2 seconds after any
-page of a sufficiently high order is freed.
-
-Pages reported will be stored in the scatterlist passed to the reporting
-function with the final entry having the end bit set in entry nent - 1.
-While pages are being processed by the report function they will not be
-accessible to the allocator. Once the report function has been completed
-the pages will be returned to the free area from which they were obtained.
-
-Prior to removing a driver that is making use of free page reporting it
-is necessary to call page_reporting_unregister to have the
-page_reporting_dev_info structure that is currently in use by free page
-reporting removed. Doing this will prevent further reports from being
-issued via the interface. If another driver or the same driver is
-registered it is possible for it to resume where the previous driver had
-left off in terms of reporting free pages.
-
-Alexander Duyck, Dec 04, 2019
diff --git a/Documentation/vm/frontswap.rst b/Documentation/vm/frontswap.rst
deleted file mode 100644 (file)
index feecc5e..0000000
+++ /dev/null
@@ -1,266 +0,0 @@
-.. _frontswap:
-
-=========
-Frontswap
-=========
-
-Frontswap provides a "transcendent memory" interface for swap pages.
-In some environments, dramatic performance savings may be obtained because
-swapped pages are saved in RAM (or a RAM-like device) instead of a swap disk.
-
-.. _Transcendent memory in a nutshell: https://lwn.net/Articles/454795/
-
-Frontswap is so named because it can be thought of as the opposite of
-a "backing" store for a swap device.  The storage is assumed to be
-a synchronous concurrency-safe page-oriented "pseudo-RAM device" conforming
-to the requirements of transcendent memory (such as Xen's "tmem", or
-in-kernel compressed memory, aka "zcache", or future RAM-like devices);
-this pseudo-RAM device is not directly accessible or addressable by the
-kernel and is of unknown and possibly time-varying size.  The driver
-links itself to frontswap by calling frontswap_register_ops to set the
-frontswap_ops funcs appropriately and the functions it provides must
-conform to certain policies as follows:
-
-An "init" prepares the device to receive frontswap pages associated
-with the specified swap device number (aka "type").  A "store" will
-copy the page to transcendent memory and associate it with the type and
-offset associated with the page. A "load" will copy the page, if found,
-from transcendent memory into kernel memory, but will NOT remove the page
-from transcendent memory.  An "invalidate_page" will remove the page
-from transcendent memory and an "invalidate_area" will remove ALL pages
-associated with the swap type (e.g., like swapoff) and notify the "device"
-to refuse further stores with that swap type.
-
-Once a page is successfully stored, a matching load on the page will normally
-succeed.  So when the kernel finds itself in a situation where it needs
-to swap out a page, it first attempts to use frontswap.  If the store returns
-success, the data has been successfully saved to transcendent memory and
-a disk write and, if the data is later read back, a disk read are avoided.
-If a store returns failure, transcendent memory has rejected the data, and the
-page can be written to swap as usual.
-
-Note that if a page is stored and the page already exists in transcendent memory
-(a "duplicate" store), either the store succeeds and the data is overwritten,
-or the store fails AND the page is invalidated.  This ensures stale data may
-never be obtained from frontswap.
-
-If properly configured, monitoring of frontswap is done via debugfs in
-the `/sys/kernel/debug/frontswap` directory.  The effectiveness of
-frontswap can be measured (across all swap devices) with:
-
-``failed_stores``
-       how many store attempts have failed
-
-``loads``
-       how many loads were attempted (all should succeed)
-
-``succ_stores``
-       how many store attempts have succeeded
-
-``invalidates``
-       how many invalidates were attempted
-
-A backend implementation may provide additional metrics.
-
-FAQ
-===
-
-* Where's the value?
-
-When a workload starts swapping, performance falls through the floor.
-Frontswap significantly increases performance in many such workloads by
-providing a clean, dynamic interface to read and write swap pages to
-"transcendent memory" that is otherwise not directly addressable to the kernel.
-This interface is ideal when data is transformed to a different form
-and size (such as with compression) or secretly moved (as might be
-useful for write-balancing for some RAM-like devices).  Swap pages (and
-evicted page-cache pages) are a great use for this kind of slower-than-RAM-
-but-much-faster-than-disk "pseudo-RAM device".
-
-Frontswap with a fairly small impact on the kernel,
-provides a huge amount of flexibility for more dynamic, flexible RAM
-utilization in various system configurations:
-
-In the single kernel case, aka "zcache", pages are compressed and
-stored in local memory, thus increasing the total anonymous pages
-that can be safely kept in RAM.  Zcache essentially trades off CPU
-cycles used in compression/decompression for better memory utilization.
-Benchmarks have shown little or no impact when memory pressure is
-low while providing a significant performance improvement (25%+)
-on some workloads under high memory pressure.
-
-"RAMster" builds on zcache by adding "peer-to-peer" transcendent memory
-support for clustered systems.  Frontswap pages are locally compressed
-as in zcache, but then "remotified" to another system's RAM.  This
-allows RAM to be dynamically load-balanced back-and-forth as needed,
-i.e. when system A is overcommitted, it can swap to system B, and
-vice versa.  RAMster can also be configured as a memory server so
-many servers in a cluster can swap, dynamically as needed, to a single
-server configured with a large amount of RAM... without pre-configuring
-how much of the RAM is available for each of the clients!
-
-In the virtual case, the whole point of virtualization is to statistically
-multiplex physical resources across the varying demands of multiple
-virtual machines.  This is really hard to do with RAM and efforts to do
-it well with no kernel changes have essentially failed (except in some
-well-publicized special-case workloads).
-Specifically, the Xen Transcendent Memory backend allows otherwise
-"fallow" hypervisor-owned RAM to not only be "time-shared" between multiple
-virtual machines, but the pages can be compressed and deduplicated to
-optimize RAM utilization.  And when guest OS's are induced to surrender
-underutilized RAM (e.g. with "selfballooning"), sudden unexpected
-memory pressure may result in swapping; frontswap allows those pages
-to be swapped to and from hypervisor RAM (if overall host system memory
-conditions allow), thus mitigating the potentially awful performance impact
-of unplanned swapping.
-
-A KVM implementation is underway and has been RFC'ed to lkml.  And,
-using frontswap, investigation is also underway on the use of NVM as
-a memory extension technology.
-
-* Sure there may be performance advantages in some situations, but
-  what's the space/time overhead of frontswap?
-
-If CONFIG_FRONTSWAP is disabled, every frontswap hook compiles into
-nothingness and the only overhead is a few extra bytes per swapon'ed
-swap device.  If CONFIG_FRONTSWAP is enabled but no frontswap "backend"
-registers, there is one extra global variable compared to zero for
-every swap page read or written.  If CONFIG_FRONTSWAP is enabled
-AND a frontswap backend registers AND the backend fails every "store"
-request (i.e. provides no memory despite claiming it might),
-CPU overhead is still negligible -- and since every frontswap fail
-precedes a swap page write-to-disk, the system is highly likely
-to be I/O bound and using a small fraction of a percent of a CPU
-will be irrelevant anyway.
-
-As for space, if CONFIG_FRONTSWAP is enabled AND a frontswap backend
-registers, one bit is allocated for every swap page for every swap
-device that is swapon'd.  This is added to the EIGHT bits (which
-was sixteen until about 2.6.34) that the kernel already allocates
-for every swap page for every swap device that is swapon'd.  (Hugh
-Dickins has observed that frontswap could probably steal one of
-the existing eight bits, but let's worry about that minor optimization
-later.)  For very large swap disks (which are rare) on a standard
-4K pagesize, this is 1MB per 32GB swap.
-
-When swap pages are stored in transcendent memory instead of written
-out to disk, there is a side effect that this may create more memory
-pressure that can potentially outweigh the other advantages.  A
-backend, such as zcache, must implement policies to carefully (but
-dynamically) manage memory limits to ensure this doesn't happen.
-
-* OK, how about a quick overview of what this frontswap patch does
-  in terms that a kernel hacker can grok?
-
-Let's assume that a frontswap "backend" has registered during
-kernel initialization; this registration indicates that this
-frontswap backend has access to some "memory" that is not directly
-accessible by the kernel.  Exactly how much memory it provides is
-entirely dynamic and random.
-
-Whenever a swap-device is swapon'd frontswap_init() is called,
-passing the swap device number (aka "type") as a parameter.
-This notifies frontswap to expect attempts to "store" swap pages
-associated with that number.
-
-Whenever the swap subsystem is readying a page to write to a swap
-device (c.f swap_writepage()), frontswap_store is called.  Frontswap
-consults with the frontswap backend and if the backend says it does NOT
-have room, frontswap_store returns -1 and the kernel swaps the page
-to the swap device as normal.  Note that the response from the frontswap
-backend is unpredictable to the kernel; it may choose to never accept a
-page, it could accept every ninth page, or it might accept every
-page.  But if the backend does accept a page, the data from the page
-has already been copied and associated with the type and offset,
-and the backend guarantees the persistence of the data.  In this case,
-frontswap sets a bit in the "frontswap_map" for the swap device
-corresponding to the page offset on the swap device to which it would
-otherwise have written the data.
-
-When the swap subsystem needs to swap-in a page (swap_readpage()),
-it first calls frontswap_load() which checks the frontswap_map to
-see if the page was earlier accepted by the frontswap backend.  If
-it was, the page of data is filled from the frontswap backend and
-the swap-in is complete.  If not, the normal swap-in code is
-executed to obtain the page of data from the real swap device.
-
-So every time the frontswap backend accepts a page, a swap device read
-and (potentially) a swap device write are replaced by a "frontswap backend
-store" and (possibly) a "frontswap backend loads", which are presumably much
-faster.
-
-* Can't frontswap be configured as a "special" swap device that is
-  just higher priority than any real swap device (e.g. like zswap,
-  or maybe swap-over-nbd/NFS)?
-
-No.  First, the existing swap subsystem doesn't allow for any kind of
-swap hierarchy.  Perhaps it could be rewritten to accommodate a hierarchy,
-but this would require fairly drastic changes.  Even if it were
-rewritten, the existing swap subsystem uses the block I/O layer which
-assumes a swap device is fixed size and any page in it is linearly
-addressable.  Frontswap barely touches the existing swap subsystem,
-and works around the constraints of the block I/O subsystem to provide
-a great deal of flexibility and dynamicity.
-
-For example, the acceptance of any swap page by the frontswap backend is
-entirely unpredictable. This is critical to the definition of frontswap
-backends because it grants completely dynamic discretion to the
-backend.  In zcache, one cannot know a priori how compressible a page is.
-"Poorly" compressible pages can be rejected, and "poorly" can itself be
-defined dynamically depending on current memory constraints.
-
-Further, frontswap is entirely synchronous whereas a real swap
-device is, by definition, asynchronous and uses block I/O.  The
-block I/O layer is not only unnecessary, but may perform "optimizations"
-that are inappropriate for a RAM-oriented device including delaying
-the write of some pages for a significant amount of time.  Synchrony is
-required to ensure the dynamicity of the backend and to avoid thorny race
-conditions that would unnecessarily and greatly complicate frontswap
-and/or the block I/O subsystem.  That said, only the initial "store"
-and "load" operations need be synchronous.  A separate asynchronous thread
-is free to manipulate the pages stored by frontswap.  For example,
-the "remotification" thread in RAMster uses standard asynchronous
-kernel sockets to move compressed frontswap pages to a remote machine.
-Similarly, a KVM guest-side implementation could do in-guest compression
-and use "batched" hypercalls.
-
-In a virtualized environment, the dynamicity allows the hypervisor
-(or host OS) to do "intelligent overcommit".  For example, it can
-choose to accept pages only until host-swapping might be imminent,
-then force guests to do their own swapping.
-
-There is a downside to the transcendent memory specifications for
-frontswap:  Since any "store" might fail, there must always be a real
-slot on a real swap device to swap the page.  Thus frontswap must be
-implemented as a "shadow" to every swapon'd device with the potential
-capability of holding every page that the swap device might have held
-and the possibility that it might hold no pages at all.  This means
-that frontswap cannot contain more pages than the total of swapon'd
-swap devices.  For example, if NO swap device is configured on some
-installation, frontswap is useless.  Swapless portable devices
-can still use frontswap but a backend for such devices must configure
-some kind of "ghost" swap device and ensure that it is never used.
-
-* Why this weird definition about "duplicate stores"?  If a page
-  has been previously successfully stored, can't it always be
-  successfully overwritten?
-
-Nearly always it can, but no, sometimes it cannot.  Consider an example
-where data is compressed and the original 4K page has been compressed
-to 1K.  Now an attempt is made to overwrite the page with data that
-is non-compressible and so would take the entire 4K.  But the backend
-has no more space.  In this case, the store must be rejected.  Whenever
-frontswap rejects a store that would overwrite, it also must invalidate
-the old data and ensure that it is no longer accessible.  Since the
-swap subsystem then writes the new data to the read swap device,
-this is the correct course of action to ensure coherency.
-
-* Why does the frontswap patch create the new include file swapfile.h?
-
-The frontswap code depends on some swap-subsystem-internal data
-structures that have, over the years, moved back and forth between
-static and global.  This seemed a reasonable compromise:  Define
-them as global but declare them in a new include file that isn't
-included by the large number of source files that include swap.h.
-
-Dan Magenheimer, last updated April 9, 2012
diff --git a/Documentation/vm/highmem.rst b/Documentation/vm/highmem.rst
deleted file mode 100644 (file)
index c9887f2..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-.. _highmem:
-
-====================
-High Memory Handling
-====================
-
-By: Peter Zijlstra <a.p.zijlstra@chello.nl>
-
-.. contents:: :local:
-
-What Is High Memory?
-====================
-
-High memory (highmem) is used when the size of physical memory approaches or
-exceeds the maximum size of virtual memory.  At that point it becomes
-impossible for the kernel to keep all of the available physical memory mapped
-at all times.  This means the kernel needs to start using temporary mappings of
-the pieces of physical memory that it wants to access.
-
-The part of (physical) memory not covered by a permanent mapping is what we
-refer to as 'highmem'.  There are various architecture dependent constraints on
-where exactly that border lies.
-
-In the i386 arch, for example, we choose to map the kernel into every process's
-VM space so that we don't have to pay the full TLB invalidation costs for
-kernel entry/exit.  This means the available virtual memory space (4GiB on
-i386) has to be divided between user and kernel space.
-
-The traditional split for architectures using this approach is 3:1, 3GiB for
-userspace and the top 1GiB for kernel space::
-
-               +--------+ 0xffffffff
-               | Kernel |
-               +--------+ 0xc0000000
-               |        |
-               | User   |
-               |        |
-               +--------+ 0x00000000
-
-This means that the kernel can at most map 1GiB of physical memory at any one
-time, but because we need virtual address space for other things - including
-temporary maps to access the rest of the physical memory - the actual direct
-map will typically be less (usually around ~896MiB).
-
-Other architectures that have mm context tagged TLBs can have separate kernel
-and user maps.  Some hardware (like some ARMs), however, have limited virtual
-space when they use mm context tags.
-
-
-Temporary Virtual Mappings
-==========================
-
-The kernel contains several ways of creating temporary mappings. The following
-list shows them in order of preference of use.
-
-* kmap_local_page().  This function is used to require short term mappings.
-  It can be invoked from any context (including interrupts) but the mappings
-  can only be used in the context which acquired them.
-
-  This function should be preferred, where feasible, over all the others.
-
-  These mappings are thread-local and CPU-local, meaning that the mapping
-  can only be accessed from within this thread and the thread is bound the
-  CPU while the mapping is active. Even if the thread is preempted (since
-  preemption is never disabled by the function) the CPU can not be
-  unplugged from the system via CPU-hotplug until the mapping is disposed.
-
-  It's valid to take pagefaults in a local kmap region, unless the context
-  in which the local mapping is acquired does not allow it for other reasons.
-
-  kmap_local_page() always returns a valid virtual address and it is assumed
-  that kunmap_local() will never fail.
-
-  Nesting kmap_local_page() and kmap_atomic() mappings is allowed to a certain
-  extent (up to KMAP_TYPE_NR) but their invocations have to be strictly ordered
-  because the map implementation is stack based. See kmap_local_page() kdocs
-  (included in the "Functions" section) for details on how to manage nested
-  mappings.
-
-* kmap_atomic().  This permits a very short duration mapping of a single
-  page.  Since the mapping is restricted to the CPU that issued it, it
-  performs well, but the issuing task is therefore required to stay on that
-  CPU until it has finished, lest some other task displace its mappings.
-
-  kmap_atomic() may also be used by interrupt contexts, since it does not
-  sleep and the callers too may not sleep until after kunmap_atomic() is
-  called.
-
-  Each call of kmap_atomic() in the kernel creates a non-preemptible section
-  and disable pagefaults. This could be a source of unwanted latency. Therefore
-  users should prefer kmap_local_page() instead of kmap_atomic().
-
-  It is assumed that k[un]map_atomic() won't fail.
-
-* kmap().  This should be used to make short duration mapping of a single
-  page with no restrictions on preemption or migration. It comes with an
-  overhead as mapping space is restricted and protected by a global lock
-  for synchronization. When mapping is no longer needed, the address that
-  the page was mapped to must be released with kunmap().
-
-  Mapping changes must be propagated across all the CPUs. kmap() also
-  requires global TLB invalidation when the kmap's pool wraps and it might
-  block when the mapping space is fully utilized until a slot becomes
-  available. Therefore, kmap() is only callable from preemptible context.
-
-  All the above work is necessary if a mapping must last for a relatively
-  long time but the bulk of high-memory mappings in the kernel are
-  short-lived and only used in one place. This means that the cost of
-  kmap() is mostly wasted in such cases. kmap() was not intended for long
-  term mappings but it has morphed in that direction and its use is
-  strongly discouraged in newer code and the set of the preceding functions
-  should be preferred.
-
-  On 64-bit systems, calls to kmap_local_page(), kmap_atomic() and kmap() have
-  no real work to do because a 64-bit address space is more than sufficient to
-  address all the physical memory whose pages are permanently mapped.
-
-* vmap().  This can be used to make a long duration mapping of multiple
-  physical pages into a contiguous virtual space.  It needs global
-  synchronization to unmap.
-
-
-Cost of Temporary Mappings
-==========================
-
-The cost of creating temporary mappings can be quite high.  The arch has to
-manipulate the kernel's page tables, the data TLB and/or the MMU's registers.
-
-If CONFIG_HIGHMEM is not set, then the kernel will try and create a mapping
-simply with a bit of arithmetic that will convert the page struct address into
-a pointer to the page contents rather than juggling mappings about.  In such a
-case, the unmap operation may be a null operation.
-
-If CONFIG_MMU is not set, then there can be no temporary mappings and no
-highmem.  In such a case, the arithmetic approach will also be used.
-
-
-i386 PAE
-========
-
-The i386 arch, under some circumstances, will permit you to stick up to 64GiB
-of RAM into your 32-bit machine.  This has a number of consequences:
-
-* Linux needs a page-frame structure for each page in the system and the
-  pageframes need to live in the permanent mapping, which means:
-
-* you can have 896M/sizeof(struct page) page-frames at most; with struct
-  page being 32-bytes that would end up being something in the order of 112G
-  worth of pages; the kernel, however, needs to store more than just
-  page-frames in that memory...
-
-* PAE makes your page tables larger - which slows the system down as more
-  data has to be accessed to traverse in TLB fills and the like.  One
-  advantage is that PAE has more PTE bits and can provide advanced features
-  like NX and PAT.
-
-The general recommendation is that you don't use more than 8GiB on a 32-bit
-machine - although more might work for you and your workload, you're pretty
-much on your own - don't expect kernel developers to really care much if things
-come apart.
-
-
-Functions
-=========
-
-.. kernel-doc:: include/linux/highmem.h
-.. kernel-doc:: include/linux/highmem-internal.h
diff --git a/Documentation/vm/hmm.rst b/Documentation/vm/hmm.rst
deleted file mode 100644 (file)
index f2a59ed..0000000
+++ /dev/null
@@ -1,452 +0,0 @@
-.. _hmm:
-
-=====================================
-Heterogeneous Memory Management (HMM)
-=====================================
-
-Provide infrastructure and helpers to integrate non-conventional memory (device
-memory like GPU on board memory) into regular kernel path, with the cornerstone
-of this being specialized struct page for such memory (see sections 5 to 7 of
-this document).
-
-HMM also provides optional helpers for SVM (Share Virtual Memory), i.e.,
-allowing a device to transparently access program addresses coherently with
-the CPU meaning that any valid pointer on the CPU is also a valid pointer
-for the device. This is becoming mandatory to simplify the use of advanced
-heterogeneous computing where GPU, DSP, or FPGA are used to perform various
-computations on behalf of a process.
-
-This document is divided as follows: in the first section I expose the problems
-related to using device specific memory allocators. In the second section, I
-expose the hardware limitations that are inherent to many platforms. The third
-section gives an overview of the HMM design. The fourth section explains how
-CPU page-table mirroring works and the purpose of HMM in this context. The
-fifth section deals with how device memory is represented inside the kernel.
-Finally, the last section presents a new migration helper that allows
-leveraging the device DMA engine.
-
-.. contents:: :local:
-
-Problems of using a device specific memory allocator
-====================================================
-
-Devices with a large amount of on board memory (several gigabytes) like GPUs
-have historically managed their memory through dedicated driver specific APIs.
-This creates a disconnect between memory allocated and managed by a device
-driver and regular application memory (private anonymous, shared memory, or
-regular file backed memory). From here on I will refer to this aspect as split
-address space. I use shared address space to refer to the opposite situation:
-i.e., one in which any application memory region can be used by a device
-transparently.
-
-Split address space happens because devices can only access memory allocated
-through a device specific API. This implies that all memory objects in a program
-are not equal from the device point of view which complicates large programs
-that rely on a wide set of libraries.
-
-Concretely, this means that code that wants to leverage devices like GPUs needs
-to copy objects between generically allocated memory (malloc, mmap private, mmap
-share) and memory allocated through the device driver API (this still ends up
-with an mmap but of the device file).
-
-For flat data sets (array, grid, image, ...) this isn't too hard to achieve but
-for complex data sets (list, tree, ...) it's hard to get right. Duplicating a
-complex data set needs to re-map all the pointer relations between each of its
-elements. This is error prone and programs get harder to debug because of the
-duplicate data set and addresses.
-
-Split address space also means that libraries cannot transparently use data
-they are getting from the core program or another library and thus each library
-might have to duplicate its input data set using the device specific memory
-allocator. Large projects suffer from this and waste resources because of the
-various memory copies.
-
-Duplicating each library API to accept as input or output memory allocated by
-each device specific allocator is not a viable option. It would lead to a
-combinatorial explosion in the library entry points.
-
-Finally, with the advance of high level language constructs (in C++ but in
-other languages too) it is now possible for the compiler to leverage GPUs and
-other devices without programmer knowledge. Some compiler identified patterns
-are only do-able with a shared address space. It is also more reasonable to use
-a shared address space for all other patterns.
-
-
-I/O bus, device memory characteristics
-======================================
-
-I/O buses cripple shared address spaces due to a few limitations. Most I/O
-buses only allow basic memory access from device to main memory; even cache
-coherency is often optional. Access to device memory from a CPU is even more
-limited. More often than not, it is not cache coherent.
-
-If we only consider the PCIE bus, then a device can access main memory (often
-through an IOMMU) and be cache coherent with the CPUs. However, it only allows
-a limited set of atomic operations from the device on main memory. This is worse
-in the other direction: the CPU can only access a limited range of the device
-memory and cannot perform atomic operations on it. Thus device memory cannot
-be considered the same as regular memory from the kernel point of view.
-
-Another crippling factor is the limited bandwidth (~32GBytes/s with PCIE 4.0
-and 16 lanes). This is 33 times less than the fastest GPU memory (1 TBytes/s).
-The final limitation is latency. Access to main memory from the device has an
-order of magnitude higher latency than when the device accesses its own memory.
-
-Some platforms are developing new I/O buses or additions/modifications to PCIE
-to address some of these limitations (OpenCAPI, CCIX). They mainly allow
-two-way cache coherency between CPU and device and allow all atomic operations the
-architecture supports. Sadly, not all platforms are following this trend and
-some major architectures are left without hardware solutions to these problems.
-
-So for shared address space to make sense, not only must we allow devices to
-access any memory but we must also permit any memory to be migrated to device
-memory while the device is using it (blocking CPU access while it happens).
-
-
-Shared address space and migration
-==================================
-
-HMM intends to provide two main features. The first one is to share the address
-space by duplicating the CPU page table in the device page table so the same
-address points to the same physical memory for any valid main memory address in
-the process address space.
-
-To achieve this, HMM offers a set of helpers to populate the device page table
-while keeping track of CPU page table updates. Device page table updates are
-not as easy as CPU page table updates. To update the device page table, you must
-allocate a buffer (or use a pool of pre-allocated buffers) and write GPU
-specific commands in it to perform the update (unmap, cache invalidations, and
-flush, ...). This cannot be done through common code for all devices. Hence
-why HMM provides helpers to factor out everything that can be while leaving the
-hardware specific details to the device driver.
-
-The second mechanism HMM provides is a new kind of ZONE_DEVICE memory that
-allows allocating a struct page for each page of device memory. Those pages
-are special because the CPU cannot map them. However, they allow migrating
-main memory to device memory using existing migration mechanisms and everything
-looks like a page that is swapped out to disk from the CPU point of view. Using a
-struct page gives the easiest and cleanest integration with existing mm
-mechanisms. Here again, HMM only provides helpers, first to hotplug new ZONE_DEVICE
-memory for the device memory and second to perform migration. Policy decisions
-of what and when to migrate is left to the device driver.
-
-Note that any CPU access to a device page triggers a page fault and a migration
-back to main memory. For example, when a page backing a given CPU address A is
-migrated from a main memory page to a device page, then any CPU access to
-address A triggers a page fault and initiates a migration back to main memory.
-
-With these two features, HMM not only allows a device to mirror process address
-space and keeps both CPU and device page tables synchronized, but also
-leverages device memory by migrating the part of the data set that is actively being
-used by the device.
-
-
-Address space mirroring implementation and API
-==============================================
-
-Address space mirroring's main objective is to allow duplication of a range of
-CPU page table into a device page table; HMM helps keep both synchronized. A
-device driver that wants to mirror a process address space must start with the
-registration of a mmu_interval_notifier::
-
- int mmu_interval_notifier_insert(struct mmu_interval_notifier *interval_sub,
-                                 struct mm_struct *mm, unsigned long start,
-                                 unsigned long length,
-                                 const struct mmu_interval_notifier_ops *ops);
-
-During the ops->invalidate() callback the device driver must perform the
-update action to the range (mark range read only, or fully unmap, etc.). The
-device must complete the update before the driver callback returns.
-
-When the device driver wants to populate a range of virtual addresses, it can
-use::
-
-  int hmm_range_fault(struct hmm_range *range);
-
-It will trigger a page fault on missing or read-only entries if write access is
-requested (see below). Page faults use the generic mm page fault code path just
-like a CPU page fault.
-
-Both functions copy CPU page table entries into their pfns array argument. Each
-entry in that array corresponds to an address in the virtual range. HMM
-provides a set of flags to help the driver identify special CPU page table
-entries.
-
-Locking within the sync_cpu_device_pagetables() callback is the most important
-aspect the driver must respect in order to keep things properly synchronized.
-The usage pattern is::
-
- int driver_populate_range(...)
- {
-      struct hmm_range range;
-      ...
-
-      range.notifier = &interval_sub;
-      range.start = ...;
-      range.end = ...;
-      range.hmm_pfns = ...;
-
-      if (!mmget_not_zero(interval_sub->notifier.mm))
-          return -EFAULT;
-
- again:
-      range.notifier_seq = mmu_interval_read_begin(&interval_sub);
-      mmap_read_lock(mm);
-      ret = hmm_range_fault(&range);
-      if (ret) {
-          mmap_read_unlock(mm);
-          if (ret == -EBUSY)
-                 goto again;
-          return ret;
-      }
-      mmap_read_unlock(mm);
-
-      take_lock(driver->update);
-      if (mmu_interval_read_retry(&ni, range.notifier_seq) {
-          release_lock(driver->update);
-          goto again;
-      }
-
-      /* Use pfns array content to update device page table,
-       * under the update lock */
-
-      release_lock(driver->update);
-      return 0;
- }
-
-The driver->update lock is the same lock that the driver takes inside its
-invalidate() callback. That lock must be held before calling
-mmu_interval_read_retry() to avoid any race with a concurrent CPU page table
-update.
-
-Leverage default_flags and pfn_flags_mask
-=========================================
-
-The hmm_range struct has 2 fields, default_flags and pfn_flags_mask, that specify
-fault or snapshot policy for the whole range instead of having to set them
-for each entry in the pfns array.
-
-For instance if the device driver wants pages for a range with at least read
-permission, it sets::
-
-    range->default_flags = HMM_PFN_REQ_FAULT;
-    range->pfn_flags_mask = 0;
-
-and calls hmm_range_fault() as described above. This will fill fault all pages
-in the range with at least read permission.
-
-Now let's say the driver wants to do the same except for one page in the range for
-which it wants to have write permission. Now driver set::
-
-    range->default_flags = HMM_PFN_REQ_FAULT;
-    range->pfn_flags_mask = HMM_PFN_REQ_WRITE;
-    range->pfns[index_of_write] = HMM_PFN_REQ_WRITE;
-
-With this, HMM will fault in all pages with at least read (i.e., valid) and for the
-address == range->start + (index_of_write << PAGE_SHIFT) it will fault with
-write permission i.e., if the CPU pte does not have write permission set then HMM
-will call handle_mm_fault().
-
-After hmm_range_fault completes the flag bits are set to the current state of
-the page tables, ie HMM_PFN_VALID | HMM_PFN_WRITE will be set if the page is
-writable.
-
-
-Represent and manage device memory from core kernel point of view
-=================================================================
-
-Several different designs were tried to support device memory. The first one
-used a device specific data structure to keep information about migrated memory
-and HMM hooked itself in various places of mm code to handle any access to
-addresses that were backed by device memory. It turns out that this ended up
-replicating most of the fields of struct page and also needed many kernel code
-paths to be updated to understand this new kind of memory.
-
-Most kernel code paths never try to access the memory behind a page
-but only care about struct page contents. Because of this, HMM switched to
-directly using struct page for device memory which left most kernel code paths
-unaware of the difference. We only need to make sure that no one ever tries to
-map those pages from the CPU side.
-
-Migration to and from device memory
-===================================
-
-Because the CPU cannot access device memory directly, the device driver must
-use hardware DMA or device specific load/store instructions to migrate data.
-The migrate_vma_setup(), migrate_vma_pages(), and migrate_vma_finalize()
-functions are designed to make drivers easier to write and to centralize common
-code across drivers.
-
-Before migrating pages to device private memory, special device private
-``struct page`` need to be created. These will be used as special "swap"
-page table entries so that a CPU process will fault if it tries to access
-a page that has been migrated to device private memory.
-
-These can be allocated and freed with::
-
-    struct resource *res;
-    struct dev_pagemap pagemap;
-
-    res = request_free_mem_region(&iomem_resource, /* number of bytes */,
-                                  "name of driver resource");
-    pagemap.type = MEMORY_DEVICE_PRIVATE;
-    pagemap.range.start = res->start;
-    pagemap.range.end = res->end;
-    pagemap.nr_range = 1;
-    pagemap.ops = &device_devmem_ops;
-    memremap_pages(&pagemap, numa_node_id());
-
-    memunmap_pages(&pagemap);
-    release_mem_region(pagemap.range.start, range_len(&pagemap.range));
-
-There are also devm_request_free_mem_region(), devm_memremap_pages(),
-devm_memunmap_pages(), and devm_release_mem_region() when the resources can
-be tied to a ``struct device``.
-
-The overall migration steps are similar to migrating NUMA pages within system
-memory (see :ref:`Page migration <page_migration>`) but the steps are split
-between device driver specific code and shared common code:
-
-1. ``mmap_read_lock()``
-
-   The device driver has to pass a ``struct vm_area_struct`` to
-   migrate_vma_setup() so the mmap_read_lock() or mmap_write_lock() needs to
-   be held for the duration of the migration.
-
-2. ``migrate_vma_setup(struct migrate_vma *args)``
-
-   The device driver initializes the ``struct migrate_vma`` fields and passes
-   the pointer to migrate_vma_setup(). The ``args->flags`` field is used to
-   filter which source pages should be migrated. For example, setting
-   ``MIGRATE_VMA_SELECT_SYSTEM`` will only migrate system memory and
-   ``MIGRATE_VMA_SELECT_DEVICE_PRIVATE`` will only migrate pages residing in
-   device private memory. If the latter flag is set, the ``args->pgmap_owner``
-   field is used to identify device private pages owned by the driver. This
-   avoids trying to migrate device private pages residing in other devices.
-   Currently only anonymous private VMA ranges can be migrated to or from
-   system memory and device private memory.
-
-   One of the first steps migrate_vma_setup() does is to invalidate other
-   device's MMUs with the ``mmu_notifier_invalidate_range_start(()`` and
-   ``mmu_notifier_invalidate_range_end()`` calls around the page table
-   walks to fill in the ``args->src`` array with PFNs to be migrated.
-   The ``invalidate_range_start()`` callback is passed a
-   ``struct mmu_notifier_range`` with the ``event`` field set to
-   ``MMU_NOTIFY_MIGRATE`` and the ``owner`` field set to
-   the ``args->pgmap_owner`` field passed to migrate_vma_setup(). This is
-   allows the device driver to skip the invalidation callback and only
-   invalidate device private MMU mappings that are actually migrating.
-   This is explained more in the next section.
-
-   While walking the page tables, a ``pte_none()`` or ``is_zero_pfn()``
-   entry results in a valid "zero" PFN stored in the ``args->src`` array.
-   This lets the driver allocate device private memory and clear it instead
-   of copying a page of zeros. Valid PTE entries to system memory or
-   device private struct pages will be locked with ``lock_page()``, isolated
-   from the LRU (if system memory since device private pages are not on
-   the LRU), unmapped from the process, and a special migration PTE is
-   inserted in place of the original PTE.
-   migrate_vma_setup() also clears the ``args->dst`` array.
-
-3. The device driver allocates destination pages and copies source pages to
-   destination pages.
-
-   The driver checks each ``src`` entry to see if the ``MIGRATE_PFN_MIGRATE``
-   bit is set and skips entries that are not migrating. The device driver
-   can also choose to skip migrating a page by not filling in the ``dst``
-   array for that page.
-
-   The driver then allocates either a device private struct page or a
-   system memory page, locks the page with ``lock_page()``, and fills in the
-   ``dst`` array entry with::
-
-     dst[i] = migrate_pfn(page_to_pfn(dpage));
-
-   Now that the driver knows that this page is being migrated, it can
-   invalidate device private MMU mappings and copy device private memory
-   to system memory or another device private page. The core Linux kernel
-   handles CPU page table invalidations so the device driver only has to
-   invalidate its own MMU mappings.
-
-   The driver can use ``migrate_pfn_to_page(src[i])`` to get the
-   ``struct page`` of the source and either copy the source page to the
-   destination or clear the destination device private memory if the pointer
-   is ``NULL`` meaning the source page was not populated in system memory.
-
-4. ``migrate_vma_pages()``
-
-   This step is where the migration is actually "committed".
-
-   If the source page was a ``pte_none()`` or ``is_zero_pfn()`` page, this
-   is where the newly allocated page is inserted into the CPU's page table.
-   This can fail if a CPU thread faults on the same page. However, the page
-   table is locked and only one of the new pages will be inserted.
-   The device driver will see that the ``MIGRATE_PFN_MIGRATE`` bit is cleared
-   if it loses the race.
-
-   If the source page was locked, isolated, etc. the source ``struct page``
-   information is now copied to destination ``struct page`` finalizing the
-   migration on the CPU side.
-
-5. Device driver updates device MMU page tables for pages still migrating,
-   rolling back pages not migrating.
-
-   If the ``src`` entry still has ``MIGRATE_PFN_MIGRATE`` bit set, the device
-   driver can update the device MMU and set the write enable bit if the
-   ``MIGRATE_PFN_WRITE`` bit is set.
-
-6. ``migrate_vma_finalize()``
-
-   This step replaces the special migration page table entry with the new
-   page's page table entry and releases the reference to the source and
-   destination ``struct page``.
-
-7. ``mmap_read_unlock()``
-
-   The lock can now be released.
-
-Exclusive access memory
-=======================
-
-Some devices have features such as atomic PTE bits that can be used to implement
-atomic access to system memory. To support atomic operations to a shared virtual
-memory page such a device needs access to that page which is exclusive of any
-userspace access from the CPU. The ``make_device_exclusive_range()`` function
-can be used to make a memory range inaccessible from userspace.
-
-This replaces all mappings for pages in the given range with special swap
-entries. Any attempt to access the swap entry results in a fault which is
-resovled by replacing the entry with the original mapping. A driver gets
-notified that the mapping has been changed by MMU notifiers, after which point
-it will no longer have exclusive access to the page. Exclusive access is
-guranteed to last until the driver drops the page lock and page reference, at
-which point any CPU faults on the page may proceed as described.
-
-Memory cgroup (memcg) and rss accounting
-========================================
-
-For now, device memory is accounted as any regular page in rss counters (either
-anonymous if device page is used for anonymous, file if device page is used for
-file backed page, or shmem if device page is used for shared memory). This is a
-deliberate choice to keep existing applications, that might start using device
-memory without knowing about it, running unimpacted.
-
-A drawback is that the OOM killer might kill an application using a lot of
-device memory and not a lot of regular system memory and thus not freeing much
-system memory. We want to gather more real world experience on how applications
-and system react under memory pressure in the presence of device memory before
-deciding to account device memory differently.
-
-
-Same decision was made for memory cgroup. Device memory pages are accounted
-against same memory cgroup a regular page would be accounted to. This does
-simplify migration to and from device memory. This also means that migration
-back from device memory to regular memory cannot fail because it would
-go above memory cgroup limit. We might revisit this choice latter on once we
-get more experience in how device memory is used and its impact on memory
-resource control.
-
-
-Note that device memory can never be pinned by a device driver nor through GUP
-and thus such memory is always free upon process exit. Or when last reference
-is dropped in case of shared memory or file backed memory.
diff --git a/Documentation/vm/hugetlbfs_reserv.rst b/Documentation/vm/hugetlbfs_reserv.rst
deleted file mode 100644 (file)
index f143954..0000000
+++ /dev/null
@@ -1,596 +0,0 @@
-.. _hugetlbfs_reserve:
-
-=====================
-Hugetlbfs Reservation
-=====================
-
-Overview
-========
-
-Huge pages as described at :ref:`hugetlbpage` are typically
-preallocated for application use.  These huge pages are instantiated in a
-task's address space at page fault time if the VMA indicates huge pages are
-to be used.  If no huge page exists at page fault time, the task is sent
-a SIGBUS and often dies an unhappy death.  Shortly after huge page support
-was added, it was determined that it would be better to detect a shortage
-of huge pages at mmap() time.  The idea is that if there were not enough
-huge pages to cover the mapping, the mmap() would fail.  This was first
-done with a simple check in the code at mmap() time to determine if there
-were enough free huge pages to cover the mapping.  Like most things in the
-kernel, the code has evolved over time.  However, the basic idea was to
-'reserve' huge pages at mmap() time to ensure that huge pages would be
-available for page faults in that mapping.  The description below attempts to
-describe how huge page reserve processing is done in the v4.10 kernel.
-
-
-Audience
-========
-This description is primarily targeted at kernel developers who are modifying
-hugetlbfs code.
-
-
-The Data Structures
-===================
-
-resv_huge_pages
-       This is a global (per-hstate) count of reserved huge pages.  Reserved
-       huge pages are only available to the task which reserved them.
-       Therefore, the number of huge pages generally available is computed
-       as (``free_huge_pages - resv_huge_pages``).
-Reserve Map
-       A reserve map is described by the structure::
-
-               struct resv_map {
-                       struct kref refs;
-                       spinlock_t lock;
-                       struct list_head regions;
-                       long adds_in_progress;
-                       struct list_head region_cache;
-                       long region_cache_count;
-               };
-
-       There is one reserve map for each huge page mapping in the system.
-       The regions list within the resv_map describes the regions within
-       the mapping.  A region is described as::
-
-               struct file_region {
-                       struct list_head link;
-                       long from;
-                       long to;
-               };
-
-       The 'from' and 'to' fields of the file region structure are huge page
-       indices into the mapping.  Depending on the type of mapping, a
-       region in the reserv_map may indicate reservations exist for the
-       range, or reservations do not exist.
-Flags for MAP_PRIVATE Reservations
-       These are stored in the bottom bits of the reservation map pointer.
-
-       ``#define HPAGE_RESV_OWNER    (1UL << 0)``
-               Indicates this task is the owner of the reservations
-               associated with the mapping.
-       ``#define HPAGE_RESV_UNMAPPED (1UL << 1)``
-               Indicates task originally mapping this range (and creating
-               reserves) has unmapped a page from this task (the child)
-               due to a failed COW.
-Page Flags
-       The PagePrivate page flag is used to indicate that a huge page
-       reservation must be restored when the huge page is freed.  More
-       details will be discussed in the "Freeing huge pages" section.
-
-
-Reservation Map Location (Private or Shared)
-============================================
-
-A huge page mapping or segment is either private or shared.  If private,
-it is typically only available to a single address space (task).  If shared,
-it can be mapped into multiple address spaces (tasks).  The location and
-semantics of the reservation map is significantly different for the two types
-of mappings.  Location differences are:
-
-- For private mappings, the reservation map hangs off the VMA structure.
-  Specifically, vma->vm_private_data.  This reserve map is created at the
-  time the mapping (mmap(MAP_PRIVATE)) is created.
-- For shared mappings, the reservation map hangs off the inode.  Specifically,
-  inode->i_mapping->private_data.  Since shared mappings are always backed
-  by files in the hugetlbfs filesystem, the hugetlbfs code ensures each inode
-  contains a reservation map.  As a result, the reservation map is allocated
-  when the inode is created.
-
-
-Creating Reservations
-=====================
-Reservations are created when a huge page backed shared memory segment is
-created (shmget(SHM_HUGETLB)) or a mapping is created via mmap(MAP_HUGETLB).
-These operations result in a call to the routine hugetlb_reserve_pages()::
-
-       int hugetlb_reserve_pages(struct inode *inode,
-                                 long from, long to,
-                                 struct vm_area_struct *vma,
-                                 vm_flags_t vm_flags)
-
-The first thing hugetlb_reserve_pages() does is check if the NORESERVE
-flag was specified in either the shmget() or mmap() call.  If NORESERVE
-was specified, then this routine returns immediately as no reservations
-are desired.
-
-The arguments 'from' and 'to' are huge page indices into the mapping or
-underlying file.  For shmget(), 'from' is always 0 and 'to' corresponds to
-the length of the segment/mapping.  For mmap(), the offset argument could
-be used to specify the offset into the underlying file.  In such a case,
-the 'from' and 'to' arguments have been adjusted by this offset.
-
-One of the big differences between PRIVATE and SHARED mappings is the way
-in which reservations are represented in the reservation map.
-
-- For shared mappings, an entry in the reservation map indicates a reservation
-  exists or did exist for the corresponding page.  As reservations are
-  consumed, the reservation map is not modified.
-- For private mappings, the lack of an entry in the reservation map indicates
-  a reservation exists for the corresponding page.  As reservations are
-  consumed, entries are added to the reservation map.  Therefore, the
-  reservation map can also be used to determine which reservations have
-  been consumed.
-
-For private mappings, hugetlb_reserve_pages() creates the reservation map and
-hangs it off the VMA structure.  In addition, the HPAGE_RESV_OWNER flag is set
-to indicate this VMA owns the reservations.
-
-The reservation map is consulted to determine how many huge page reservations
-are needed for the current mapping/segment.  For private mappings, this is
-always the value (to - from).  However, for shared mappings it is possible that
-some reservations may already exist within the range (to - from).  See the
-section :ref:`Reservation Map Modifications <resv_map_modifications>`
-for details on how this is accomplished.
-
-The mapping may be associated with a subpool.  If so, the subpool is consulted
-to ensure there is sufficient space for the mapping.  It is possible that the
-subpool has set aside reservations that can be used for the mapping.  See the
-section :ref:`Subpool Reservations <sub_pool_resv>` for more details.
-
-After consulting the reservation map and subpool, the number of needed new
-reservations is known.  The routine hugetlb_acct_memory() is called to check
-for and take the requested number of reservations.  hugetlb_acct_memory()
-calls into routines that potentially allocate and adjust surplus page counts.
-However, within those routines the code is simply checking to ensure there
-are enough free huge pages to accommodate the reservation.  If there are,
-the global reservation count resv_huge_pages is adjusted something like the
-following::
-
-       if (resv_needed <= (resv_huge_pages - free_huge_pages))
-               resv_huge_pages += resv_needed;
-
-Note that the global lock hugetlb_lock is held when checking and adjusting
-these counters.
-
-If there were enough free huge pages and the global count resv_huge_pages
-was adjusted, then the reservation map associated with the mapping is
-modified to reflect the reservations.  In the case of a shared mapping, a
-file_region will exist that includes the range 'from' - 'to'.  For private
-mappings, no modifications are made to the reservation map as lack of an
-entry indicates a reservation exists.
-
-If hugetlb_reserve_pages() was successful, the global reservation count and
-reservation map associated with the mapping will be modified as required to
-ensure reservations exist for the range 'from' - 'to'.
-
-.. _consume_resv:
-
-Consuming Reservations/Allocating a Huge Page
-=============================================
-
-Reservations are consumed when huge pages associated with the reservations
-are allocated and instantiated in the corresponding mapping.  The allocation
-is performed within the routine alloc_huge_page()::
-
-       struct page *alloc_huge_page(struct vm_area_struct *vma,
-                                    unsigned long addr, int avoid_reserve)
-
-alloc_huge_page is passed a VMA pointer and a virtual address, so it can
-consult the reservation map to determine if a reservation exists.  In addition,
-alloc_huge_page takes the argument avoid_reserve which indicates reserves
-should not be used even if it appears they have been set aside for the
-specified address.  The avoid_reserve argument is most often used in the case
-of Copy on Write and Page Migration where additional copies of an existing
-page are being allocated.
-
-The helper routine vma_needs_reservation() is called to determine if a
-reservation exists for the address within the mapping(vma).  See the section
-:ref:`Reservation Map Helper Routines <resv_map_helpers>` for detailed
-information on what this routine does.
-The value returned from vma_needs_reservation() is generally
-0 or 1.  0 if a reservation exists for the address, 1 if no reservation exists.
-If a reservation does not exist, and there is a subpool associated with the
-mapping the subpool is consulted to determine if it contains reservations.
-If the subpool contains reservations, one can be used for this allocation.
-However, in every case the avoid_reserve argument overrides the use of
-a reservation for the allocation.  After determining whether a reservation
-exists and can be used for the allocation, the routine dequeue_huge_page_vma()
-is called.  This routine takes two arguments related to reservations:
-
-- avoid_reserve, this is the same value/argument passed to alloc_huge_page()
-- chg, even though this argument is of type long only the values 0 or 1 are
-  passed to dequeue_huge_page_vma.  If the value is 0, it indicates a
-  reservation exists (see the section "Memory Policy and Reservations" for
-  possible issues).  If the value is 1, it indicates a reservation does not
-  exist and the page must be taken from the global free pool if possible.
-
-The free lists associated with the memory policy of the VMA are searched for
-a free page.  If a page is found, the value free_huge_pages is decremented
-when the page is removed from the free list.  If there was a reservation
-associated with the page, the following adjustments are made::
-
-       SetPagePrivate(page);   /* Indicates allocating this page consumed
-                                * a reservation, and if an error is
-                                * encountered such that the page must be
-                                * freed, the reservation will be restored. */
-       resv_huge_pages--;      /* Decrement the global reservation count */
-
-Note, if no huge page can be found that satisfies the VMA's memory policy
-an attempt will be made to allocate one using the buddy allocator.  This
-brings up the issue of surplus huge pages and overcommit which is beyond
-the scope reservations.  Even if a surplus page is allocated, the same
-reservation based adjustments as above will be made: SetPagePrivate(page) and
-resv_huge_pages--.
-
-After obtaining a new huge page, (page)->private is set to the value of
-the subpool associated with the page if it exists.  This will be used for
-subpool accounting when the page is freed.
-
-The routine vma_commit_reservation() is then called to adjust the reserve
-map based on the consumption of the reservation.  In general, this involves
-ensuring the page is represented within a file_region structure of the region
-map.  For shared mappings where the reservation was present, an entry
-in the reserve map already existed so no change is made.  However, if there
-was no reservation in a shared mapping or this was a private mapping a new
-entry must be created.
-
-It is possible that the reserve map could have been changed between the call
-to vma_needs_reservation() at the beginning of alloc_huge_page() and the
-call to vma_commit_reservation() after the page was allocated.  This would
-be possible if hugetlb_reserve_pages was called for the same page in a shared
-mapping.  In such cases, the reservation count and subpool free page count
-will be off by one.  This rare condition can be identified by comparing the
-return value from vma_needs_reservation and vma_commit_reservation.  If such
-a race is detected, the subpool and global reserve counts are adjusted to
-compensate.  See the section
-:ref:`Reservation Map Helper Routines <resv_map_helpers>` for more
-information on these routines.
-
-
-Instantiate Huge Pages
-======================
-
-After huge page allocation, the page is typically added to the page tables
-of the allocating task.  Before this, pages in a shared mapping are added
-to the page cache and pages in private mappings are added to an anonymous
-reverse mapping.  In both cases, the PagePrivate flag is cleared.  Therefore,
-when a huge page that has been instantiated is freed no adjustment is made
-to the global reservation count (resv_huge_pages).
-
-
-Freeing Huge Pages
-==================
-
-Huge page freeing is performed by the routine free_huge_page().  This routine
-is the destructor for hugetlbfs compound pages.  As a result, it is only
-passed a pointer to the page struct.  When a huge page is freed, reservation
-accounting may need to be performed.  This would be the case if the page was
-associated with a subpool that contained reserves, or the page is being freed
-on an error path where a global reserve count must be restored.
-
-The page->private field points to any subpool associated with the page.
-If the PagePrivate flag is set, it indicates the global reserve count should
-be adjusted (see the section
-:ref:`Consuming Reservations/Allocating a Huge Page <consume_resv>`
-for information on how these are set).
-
-The routine first calls hugepage_subpool_put_pages() for the page.  If this
-routine returns a value of 0 (which does not equal the value passed 1) it
-indicates reserves are associated with the subpool, and this newly free page
-must be used to keep the number of subpool reserves above the minimum size.
-Therefore, the global resv_huge_pages counter is incremented in this case.
-
-If the PagePrivate flag was set in the page, the global resv_huge_pages counter
-will always be incremented.
-
-.. _sub_pool_resv:
-
-Subpool Reservations
-====================
-
-There is a struct hstate associated with each huge page size.  The hstate
-tracks all huge pages of the specified size.  A subpool represents a subset
-of pages within a hstate that is associated with a mounted hugetlbfs
-filesystem.
-
-When a hugetlbfs filesystem is mounted a min_size option can be specified
-which indicates the minimum number of huge pages required by the filesystem.
-If this option is specified, the number of huge pages corresponding to
-min_size are reserved for use by the filesystem.  This number is tracked in
-the min_hpages field of a struct hugepage_subpool.  At mount time,
-hugetlb_acct_memory(min_hpages) is called to reserve the specified number of
-huge pages.  If they can not be reserved, the mount fails.
-
-The routines hugepage_subpool_get/put_pages() are called when pages are
-obtained from or released back to a subpool.  They perform all subpool
-accounting, and track any reservations associated with the subpool.
-hugepage_subpool_get/put_pages are passed the number of huge pages by which
-to adjust the subpool 'used page' count (down for get, up for put).  Normally,
-they return the same value that was passed or an error if not enough pages
-exist in the subpool.
-
-However, if reserves are associated with the subpool a return value less
-than the passed value may be returned.  This return value indicates the
-number of additional global pool adjustments which must be made.  For example,
-suppose a subpool contains 3 reserved huge pages and someone asks for 5.
-The 3 reserved pages associated with the subpool can be used to satisfy part
-of the request.  But, 2 pages must be obtained from the global pools.  To
-relay this information to the caller, the value 2 is returned.  The caller
-is then responsible for attempting to obtain the additional two pages from
-the global pools.
-
-
-COW and Reservations
-====================
-
-Since shared mappings all point to and use the same underlying pages, the
-biggest reservation concern for COW is private mappings.  In this case,
-two tasks can be pointing at the same previously allocated page.  One task
-attempts to write to the page, so a new page must be allocated so that each
-task points to its own page.
-
-When the page was originally allocated, the reservation for that page was
-consumed.  When an attempt to allocate a new page is made as a result of
-COW, it is possible that no free huge pages are free and the allocation
-will fail.
-
-When the private mapping was originally created, the owner of the mapping
-was noted by setting the HPAGE_RESV_OWNER bit in the pointer to the reservation
-map of the owner.  Since the owner created the mapping, the owner owns all
-the reservations associated with the mapping.  Therefore, when a write fault
-occurs and there is no page available, different action is taken for the owner
-and non-owner of the reservation.
-
-In the case where the faulting task is not the owner, the fault will fail and
-the task will typically receive a SIGBUS.
-
-If the owner is the faulting task, we want it to succeed since it owned the
-original reservation.  To accomplish this, the page is unmapped from the
-non-owning task.  In this way, the only reference is from the owning task.
-In addition, the HPAGE_RESV_UNMAPPED bit is set in the reservation map pointer
-of the non-owning task.  The non-owning task may receive a SIGBUS if it later
-faults on a non-present page.  But, the original owner of the
-mapping/reservation will behave as expected.
-
-
-.. _resv_map_modifications:
-
-Reservation Map Modifications
-=============================
-
-The following low level routines are used to make modifications to a
-reservation map.  Typically, these routines are not called directly.  Rather,
-a reservation map helper routine is called which calls one of these low level
-routines.  These low level routines are fairly well documented in the source
-code (mm/hugetlb.c).  These routines are::
-
-       long region_chg(struct resv_map *resv, long f, long t);
-       long region_add(struct resv_map *resv, long f, long t);
-       void region_abort(struct resv_map *resv, long f, long t);
-       long region_count(struct resv_map *resv, long f, long t);
-
-Operations on the reservation map typically involve two operations:
-
-1) region_chg() is called to examine the reserve map and determine how
-   many pages in the specified range [f, t) are NOT currently represented.
-
-   The calling code performs global checks and allocations to determine if
-   there are enough huge pages for the operation to succeed.
-
-2)
-  a) If the operation can succeed, region_add() is called to actually modify
-     the reservation map for the same range [f, t) previously passed to
-     region_chg().
-  b) If the operation can not succeed, region_abort is called for the same
-     range [f, t) to abort the operation.
-
-Note that this is a two step process where region_add() and region_abort()
-are guaranteed to succeed after a prior call to region_chg() for the same
-range.  region_chg() is responsible for pre-allocating any data structures
-necessary to ensure the subsequent operations (specifically region_add()))
-will succeed.
-
-As mentioned above, region_chg() determines the number of pages in the range
-which are NOT currently represented in the map.  This number is returned to
-the caller.  region_add() returns the number of pages in the range added to
-the map.  In most cases, the return value of region_add() is the same as the
-return value of region_chg().  However, in the case of shared mappings it is
-possible for changes to the reservation map to be made between the calls to
-region_chg() and region_add().  In this case, the return value of region_add()
-will not match the return value of region_chg().  It is likely that in such
-cases global counts and subpool accounting will be incorrect and in need of
-adjustment.  It is the responsibility of the caller to check for this condition
-and make the appropriate adjustments.
-
-The routine region_del() is called to remove regions from a reservation map.
-It is typically called in the following situations:
-
-- When a file in the hugetlbfs filesystem is being removed, the inode will
-  be released and the reservation map freed.  Before freeing the reservation
-  map, all the individual file_region structures must be freed.  In this case
-  region_del is passed the range [0, LONG_MAX).
-- When a hugetlbfs file is being truncated.  In this case, all allocated pages
-  after the new file size must be freed.  In addition, any file_region entries
-  in the reservation map past the new end of file must be deleted.  In this
-  case, region_del is passed the range [new_end_of_file, LONG_MAX).
-- When a hole is being punched in a hugetlbfs file.  In this case, huge pages
-  are removed from the middle of the file one at a time.  As the pages are
-  removed, region_del() is called to remove the corresponding entry from the
-  reservation map.  In this case, region_del is passed the range
-  [page_idx, page_idx + 1).
-
-In every case, region_del() will return the number of pages removed from the
-reservation map.  In VERY rare cases, region_del() can fail.  This can only
-happen in the hole punch case where it has to split an existing file_region
-entry and can not allocate a new structure.  In this error case, region_del()
-will return -ENOMEM.  The problem here is that the reservation map will
-indicate that there is a reservation for the page.  However, the subpool and
-global reservation counts will not reflect the reservation.  To handle this
-situation, the routine hugetlb_fix_reserve_counts() is called to adjust the
-counters so that they correspond with the reservation map entry that could
-not be deleted.
-
-region_count() is called when unmapping a private huge page mapping.  In
-private mappings, the lack of a entry in the reservation map indicates that
-a reservation exists.  Therefore, by counting the number of entries in the
-reservation map we know how many reservations were consumed and how many are
-outstanding (outstanding = (end - start) - region_count(resv, start, end)).
-Since the mapping is going away, the subpool and global reservation counts
-are decremented by the number of outstanding reservations.
-
-.. _resv_map_helpers:
-
-Reservation Map Helper Routines
-===============================
-
-Several helper routines exist to query and modify the reservation maps.
-These routines are only interested with reservations for a specific huge
-page, so they just pass in an address instead of a range.  In addition,
-they pass in the associated VMA.  From the VMA, the type of mapping (private
-or shared) and the location of the reservation map (inode or VMA) can be
-determined.  These routines simply call the underlying routines described
-in the section "Reservation Map Modifications".  However, they do take into
-account the 'opposite' meaning of reservation map entries for private and
-shared mappings and hide this detail from the caller::
-
-       long vma_needs_reservation(struct hstate *h,
-                                  struct vm_area_struct *vma,
-                                  unsigned long addr)
-
-This routine calls region_chg() for the specified page.  If no reservation
-exists, 1 is returned.  If a reservation exists, 0 is returned::
-
-       long vma_commit_reservation(struct hstate *h,
-                                   struct vm_area_struct *vma,
-                                   unsigned long addr)
-
-This calls region_add() for the specified page.  As in the case of region_chg
-and region_add, this routine is to be called after a previous call to
-vma_needs_reservation.  It will add a reservation entry for the page.  It
-returns 1 if the reservation was added and 0 if not.  The return value should
-be compared with the return value of the previous call to
-vma_needs_reservation.  An unexpected difference indicates the reservation
-map was modified between calls::
-
-       void vma_end_reservation(struct hstate *h,
-                                struct vm_area_struct *vma,
-                                unsigned long addr)
-
-This calls region_abort() for the specified page.  As in the case of region_chg
-and region_abort, this routine is to be called after a previous call to
-vma_needs_reservation.  It will abort/end the in progress reservation add
-operation::
-
-       long vma_add_reservation(struct hstate *h,
-                                struct vm_area_struct *vma,
-                                unsigned long addr)
-
-This is a special wrapper routine to help facilitate reservation cleanup
-on error paths.  It is only called from the routine restore_reserve_on_error().
-This routine is used in conjunction with vma_needs_reservation in an attempt
-to add a reservation to the reservation map.  It takes into account the
-different reservation map semantics for private and shared mappings.  Hence,
-region_add is called for shared mappings (as an entry present in the map
-indicates a reservation), and region_del is called for private mappings (as
-the absence of an entry in the map indicates a reservation).  See the section
-"Reservation cleanup in error paths" for more information on what needs to
-be done on error paths.
-
-
-Reservation Cleanup in Error Paths
-==================================
-
-As mentioned in the section
-:ref:`Reservation Map Helper Routines <resv_map_helpers>`, reservation
-map modifications are performed in two steps.  First vma_needs_reservation
-is called before a page is allocated.  If the allocation is successful,
-then vma_commit_reservation is called.  If not, vma_end_reservation is called.
-Global and subpool reservation counts are adjusted based on success or failure
-of the operation and all is well.
-
-Additionally, after a huge page is instantiated the PagePrivate flag is
-cleared so that accounting when the page is ultimately freed is correct.
-
-However, there are several instances where errors are encountered after a huge
-page is allocated but before it is instantiated.  In this case, the page
-allocation has consumed the reservation and made the appropriate subpool,
-reservation map and global count adjustments.  If the page is freed at this
-time (before instantiation and clearing of PagePrivate), then free_huge_page
-will increment the global reservation count.  However, the reservation map
-indicates the reservation was consumed.  This resulting inconsistent state
-will cause the 'leak' of a reserved huge page.  The global reserve count will
-be  higher than it should and prevent allocation of a pre-allocated page.
-
-The routine restore_reserve_on_error() attempts to handle this situation.  It
-is fairly well documented.  The intention of this routine is to restore
-the reservation map to the way it was before the page allocation.   In this
-way, the state of the reservation map will correspond to the global reservation
-count after the page is freed.
-
-The routine restore_reserve_on_error itself may encounter errors while
-attempting to restore the reservation map entry.  In this case, it will
-simply clear the PagePrivate flag of the page.  In this way, the global
-reserve count will not be incremented when the page is freed.  However, the
-reservation map will continue to look as though the reservation was consumed.
-A page can still be allocated for the address, but it will not use a reserved
-page as originally intended.
-
-There is some code (most notably userfaultfd) which can not call
-restore_reserve_on_error.  In this case, it simply modifies the PagePrivate
-so that a reservation will not be leaked when the huge page is freed.
-
-
-Reservations and Memory Policy
-==============================
-Per-node huge page lists existed in struct hstate when git was first used
-to manage Linux code.  The concept of reservations was added some time later.
-When reservations were added, no attempt was made to take memory policy
-into account.  While cpusets are not exactly the same as memory policy, this
-comment in hugetlb_acct_memory sums up the interaction between reservations
-and cpusets/memory policy::
-
-       /*
-        * When cpuset is configured, it breaks the strict hugetlb page
-        * reservation as the accounting is done on a global variable. Such
-        * reservation is completely rubbish in the presence of cpuset because
-        * the reservation is not checked against page availability for the
-        * current cpuset. Application can still potentially OOM'ed by kernel
-        * with lack of free htlb page in cpuset that the task is in.
-        * Attempt to enforce strict accounting with cpuset is almost
-        * impossible (or too ugly) because cpuset is too fluid that
-        * task or memory node can be dynamically moved between cpusets.
-        *
-        * The change of semantics for shared hugetlb mapping with cpuset is
-        * undesirable. However, in order to preserve some of the semantics,
-        * we fall back to check against current free page availability as
-        * a best attempt and hopefully to minimize the impact of changing
-        * semantics that cpuset has.
-        */
-
-Huge page reservations were added to prevent unexpected page allocation
-failures (OOM) at page fault time.  However, if an application makes use
-of cpusets or memory policy there is no guarantee that huge pages will be
-available on the required nodes.  This is true even if there are a sufficient
-number of global reservations.
-
-Hugetlbfs regression testing
-============================
-
-The most complete set of hugetlb tests are in the libhugetlbfs repository.
-If you modify any hugetlb related code, use the libhugetlbfs test suite
-to check for regressions.  In addition, if you add any new hugetlb
-functionality, please add appropriate tests to libhugetlbfs.
-
---
-Mike Kravetz, 7 April 2017
diff --git a/Documentation/vm/hwpoison.rst b/Documentation/vm/hwpoison.rst
deleted file mode 100644 (file)
index b9d5253..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-.. hwpoison:
-
-========
-hwpoison
-========
-
-What is hwpoison?
-=================
-
-Upcoming Intel CPUs have support for recovering from some memory errors
-(``MCA recovery``). This requires the OS to declare a page "poisoned",
-kill the processes associated with it and avoid using it in the future.
-
-This patchkit implements the necessary infrastructure in the VM.
-
-To quote the overview comment::
-
-       High level machine check handler. Handles pages reported by the
-       hardware as being corrupted usually due to a 2bit ECC memory or cache
-       failure.
-
-       This focusses on pages detected as corrupted in the background.
-       When the current CPU tries to consume corruption the currently
-       running process can just be killed directly instead. This implies
-       that if the error cannot be handled for some reason it's safe to
-       just ignore it because no corruption has been consumed yet. Instead
-       when that happens another machine check will happen.
-
-       Handles page cache pages in various states. The tricky part
-       here is that we can access any page asynchronous to other VM
-       users, because memory failures could happen anytime and anywhere,
-       possibly violating some of their assumptions. This is why this code
-       has to be extremely careful. Generally it tries to use normal locking
-       rules, as in get the standard locks, even if that means the
-       error handling takes potentially a long time.
-
-       Some of the operations here are somewhat inefficient and have non
-       linear algorithmic complexity, because the data structures have not
-       been optimized for this case. This is in particular the case
-       for the mapping from a vma to a process. Since this case is expected
-       to be rare we hope we can get away with this.
-
-The code consists of a the high level handler in mm/memory-failure.c,
-a new page poison bit and various checks in the VM to handle poisoned
-pages.
-
-The main target right now is KVM guests, but it works for all kinds
-of applications. KVM support requires a recent qemu-kvm release.
-
-For the KVM use there was need for a new signal type so that
-KVM can inject the machine check into the guest with the proper
-address. This in theory allows other applications to handle
-memory failures too. The expection is that near all applications
-won't do that, but some very specialized ones might.
-
-Failure recovery modes
-======================
-
-There are two (actually three) modes memory failure recovery can be in:
-
-vm.memory_failure_recovery sysctl set to zero:
-       All memory failures cause a panic. Do not attempt recovery.
-
-early kill
-       (can be controlled globally and per process)
-       Send SIGBUS to the application as soon as the error is detected
-       This allows applications who can process memory errors in a gentle
-       way (e.g. drop affected object)
-       This is the mode used by KVM qemu.
-
-late kill
-       Send SIGBUS when the application runs into the corrupted page.
-       This is best for memory error unaware applications and default
-       Note some pages are always handled as late kill.
-
-User control
-============
-
-vm.memory_failure_recovery
-       See sysctl.txt
-
-vm.memory_failure_early_kill
-       Enable early kill mode globally
-
-PR_MCE_KILL
-       Set early/late kill mode/revert to system default
-
-       arg1: PR_MCE_KILL_CLEAR:
-               Revert to system default
-       arg1: PR_MCE_KILL_SET:
-               arg2 defines thread specific mode
-
-               PR_MCE_KILL_EARLY:
-                       Early kill
-               PR_MCE_KILL_LATE:
-                       Late kill
-               PR_MCE_KILL_DEFAULT
-                       Use system global default
-
-       Note that if you want to have a dedicated thread which handles
-       the SIGBUS(BUS_MCEERR_AO) on behalf of the process, you should
-       call prctl(PR_MCE_KILL_EARLY) on the designated thread. Otherwise,
-       the SIGBUS is sent to the main thread.
-
-PR_MCE_KILL_GET
-       return current mode
-
-Testing
-=======
-
-* madvise(MADV_HWPOISON, ....) (as root) - Poison a page in the
-  process for testing
-
-* hwpoison-inject module through debugfs ``/sys/kernel/debug/hwpoison/``
-
-  corrupt-pfn
-       Inject hwpoison fault at PFN echoed into this file. This does
-       some early filtering to avoid corrupted unintended pages in test suites.
-
-  unpoison-pfn
-       Software-unpoison page at PFN echoed into this file. This way
-       a page can be reused again.  This only works for Linux
-       injected failures, not for real memory failures. Once any hardware
-       memory failure happens, this feature is disabled.
-
-  Note these injection interfaces are not stable and might change between
-  kernel versions
-
-  corrupt-filter-dev-major, corrupt-filter-dev-minor
-       Only handle memory failures to pages associated with the file
-       system defined by block device major/minor.  -1U is the
-       wildcard value.  This should be only used for testing with
-       artificial injection.
-
-  corrupt-filter-memcg
-       Limit injection to pages owned by memgroup. Specified by inode
-       number of the memcg.
-
-       Example::
-
-               mkdir /sys/fs/cgroup/mem/hwpoison
-
-               usemem -m 100 -s 1000 &
-               echo `jobs -p` > /sys/fs/cgroup/mem/hwpoison/tasks
-
-               memcg_ino=$(ls -id /sys/fs/cgroup/mem/hwpoison | cut -f1 -d' ')
-               echo $memcg_ino > /debug/hwpoison/corrupt-filter-memcg
-
-               page-types -p `pidof init`   --hwpoison  # shall do nothing
-               page-types -p `pidof usemem` --hwpoison  # poison its pages
-
-  corrupt-filter-flags-mask, corrupt-filter-flags-value
-       When specified, only poison pages if ((page_flags & mask) ==
-       value).  This allows stress testing of many kinds of
-       pages. The page_flags are the same as in /proc/kpageflags. The
-       flag bits are defined in include/linux/kernel-page-flags.h and
-       documented in Documentation/admin-guide/mm/pagemap.rst
-
-* Architecture specific MCE injector
-
-  x86 has mce-inject, mce-test
-
-  Some portable hwpoison test programs in mce-test, see below.
-
-References
-==========
-
-http://halobates.de/mce-lc09-2.pdf
-       Overview presentation from LinuxCon 09
-
-git://git.kernel.org/pub/scm/utils/cpu/mce/mce-test.git
-       Test suite (hwpoison specific portable tests in tsrc)
-
-git://git.kernel.org/pub/scm/utils/cpu/mce/mce-inject.git
-       x86 specific injector
-
-
-Limitations
-===========
-- Not all page types are supported and never will. Most kernel internal
-  objects cannot be recovered, only LRU pages for now.
-
----
-Andi Kleen, Oct 2009
diff --git a/Documentation/vm/index.rst b/Documentation/vm/index.rst
deleted file mode 100644 (file)
index 575ccd4..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-=====================================
-Linux Memory Management Documentation
-=====================================
-
-Memory Management Guide
-=======================
-
-This is a guide to understanding the memory management subsystem
-of Linux.  If you are looking for advice on simply allocating memory,
-see the :ref:`memory_allocation`.  For controlling and tuning guides,
-see the :doc:`admin guide <../admin-guide/mm/index>`.
-
-.. toctree::
-   :maxdepth: 1
-
-   physical_memory
-   page_tables
-   process_addrs
-   bootmem
-   page_allocation
-   vmalloc
-   slab
-   highmem
-   page_reclaim
-   swap
-   page_cache
-   shmfs
-   oom
-
-Legacy Documentation
-====================
-
-This is a collection of older documents about the Linux memory management
-(MM) subsystem internals with different level of details ranging from
-notes and mailing list responses for elaborating descriptions of data
-structures and algorithms.  It should all be integrated nicely into the
-above structured documentation, or deleted if it has served its purpose.
-
-.. toctree::
-   :maxdepth: 1
-
-   active_mm
-   arch_pgtable_helpers
-   balance
-   damon/index
-   free_page_reporting
-   frontswap
-   hmm
-   hwpoison
-   hugetlbfs_reserv
-   ksm
-   memory-model
-   mmu_notifier
-   numa
-   overcommit-accounting
-   page_migration
-   page_frags
-   page_owner
-   page_table_check
-   remap_file_pages
-   slub
-   split_page_table_lock
-   transhuge
-   unevictable-lru
-   vmalloced-kernel-stacks
-   vmemmap_dedup
-   z3fold
-   zsmalloc
diff --git a/Documentation/vm/ksm.rst b/Documentation/vm/ksm.rst
deleted file mode 100644 (file)
index 9e37add..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-.. _ksm:
-
-=======================
-Kernel Samepage Merging
-=======================
-
-KSM is a memory-saving de-duplication feature, enabled by CONFIG_KSM=y,
-added to the Linux kernel in 2.6.32.  See ``mm/ksm.c`` for its implementation,
-and http://lwn.net/Articles/306704/ and https://lwn.net/Articles/330589/
-
-The userspace interface of KSM is described in :ref:`Documentation/admin-guide/mm/ksm.rst <admin_guide_ksm>`
-
-Design
-======
-
-Overview
---------
-
-.. kernel-doc:: mm/ksm.c
-   :DOC: Overview
-
-Reverse mapping
----------------
-KSM maintains reverse mapping information for KSM pages in the stable
-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 rmap_item and the ``page->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".
-
-Every "chain" and all "dups" linked into a "chain" enforce the
-invariant that they represent the same write protected memory content,
-even if each "dup" will be pointed by a different KSM page copy of
-that content.
-
-This way the stable tree lookup computational complexity is unaffected
-if compared to an unlimited list of reverse mappings. It is still
-enforced that there cannot be KSM page content duplicates in the
-stable tree itself.
-
-The deduplication limit enforced by ``max_page_sharing`` is required
-to avoid the virtual memory rmap lists to grow too large. The rmap
-walk has O(N) complexity where N is the number of rmap_items
-(i.e. virtual mappings) that are sharing the page, which is in turn
-capped by ``max_page_sharing``. So this effectively spreads the linear
-O(N) computational complexity from rmap walk context over different
-KSM pages. The ksmd walk over the stable_node "chains" is also O(N),
-but N is the number of stable_node "dups", not the number of
-rmap_items, so it has not a significant impact on ksmd performance. In
-practice the best stable_node "dup" candidate will be kept and found
-at the head of the "dups" list.
-
-High values of ``max_page_sharing`` result in faster memory merging
-(because there will be fewer stable_node dups queued into the
-stable_node chain->hlist to check for pruning) and higher
-deduplication factor at the expense of slower worst case for rmap
-walks for any KSM page which can happen during swapping, compaction,
-NUMA balancing and page migration.
-
-The ``stable_node_dups/stable_node_chains`` ratio is also affected by the
-``max_page_sharing`` tunable, and an high ratio may indicate fragmentation
-in the stable_node dups, which could be solved by introducing
-fragmentation algorithms in ksmd which would refile rmap_items from
-one stable_node dup to another stable_node dup, in order to free up
-stable_node "dups" with few rmap_items in them, but that may increase
-the ksmd CPU usage and possibly slowdown the readonly computations on
-the KSM pages of the applications.
-
-The whole list of stable_node "dups" linked in the stable_node
-"chains" is scanned periodically in order to prune stale stable_nodes.
-The frequency of such scans is defined by
-``stable_node_chains_prune_millisecs`` sysfs tunable.
-
-Reference
----------
-.. kernel-doc:: mm/ksm.c
-   :functions: mm_slot ksm_scan stable_node rmap_item
-
---
-Izik Eidus,
-Hugh Dickins, 17 Nov 2009
diff --git a/Documentation/vm/memory-model.rst b/Documentation/vm/memory-model.rst
deleted file mode 100644 (file)
index 30e8fbe..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-.. _physical_memory_model:
-
-=====================
-Physical Memory Model
-=====================
-
-Physical memory in a system may be addressed in different ways. The
-simplest case is when the physical memory starts at address 0 and
-spans a contiguous range up to the maximal address. It could be,
-however, that this range contains small holes that are not accessible
-for the CPU. Then there could be several contiguous ranges at
-completely distinct addresses. And, don't forget about NUMA, where
-different memory banks are attached to different CPUs.
-
-Linux abstracts this diversity using one of the two memory models:
-FLATMEM and SPARSEMEM. Each architecture defines what
-memory models it supports, what the default memory model is and
-whether it is possible to manually override that default.
-
-All the memory models track the status of physical page frames using
-struct page arranged in one or more arrays.
-
-Regardless of the selected memory model, there exists one-to-one
-mapping between the physical page frame number (PFN) and the
-corresponding `struct page`.
-
-Each memory model defines :c:func:`pfn_to_page` and :c:func:`page_to_pfn`
-helpers that allow the conversion from PFN to `struct page` and vice
-versa.
-
-FLATMEM
-=======
-
-The simplest memory model is FLATMEM. This model is suitable for
-non-NUMA systems with contiguous, or mostly contiguous, physical
-memory.
-
-In the FLATMEM memory model, there is a global `mem_map` array that
-maps the entire physical memory. For most architectures, the holes
-have entries in the `mem_map` array. The `struct page` objects
-corresponding to the holes are never fully initialized.
-
-To allocate the `mem_map` array, architecture specific setup code should
-call :c:func:`free_area_init` function. Yet, the mappings array is not
-usable until the call to :c:func:`memblock_free_all` that hands all the
-memory to the page allocator.
-
-An architecture may free parts of the `mem_map` array that do not cover the
-actual physical pages. In such case, the architecture specific
-:c:func:`pfn_valid` implementation should take the holes in the
-`mem_map` into account.
-
-With FLATMEM, the conversion between a PFN and the `struct page` is
-straightforward: `PFN - ARCH_PFN_OFFSET` is an index to the
-`mem_map` array.
-
-The `ARCH_PFN_OFFSET` defines the first page frame number for
-systems with physical memory starting at address different from 0.
-
-SPARSEMEM
-=========
-
-SPARSEMEM is the most versatile memory model available in Linux and it
-is the only memory model that supports several advanced features such
-as hot-plug and hot-remove of the physical memory, alternative memory
-maps for non-volatile memory devices and deferred initialization of
-the memory map for larger systems.
-
-The SPARSEMEM model presents the physical memory as a collection of
-sections. A section is represented with struct mem_section
-that contains `section_mem_map` that is, logically, a pointer to an
-array of struct pages. However, it is stored with some other magic
-that aids the sections management. The section size and maximal number
-of section is specified using `SECTION_SIZE_BITS` and
-`MAX_PHYSMEM_BITS` constants defined by each architecture that
-supports SPARSEMEM. While `MAX_PHYSMEM_BITS` is an actual width of a
-physical address that an architecture supports, the
-`SECTION_SIZE_BITS` is an arbitrary value.
-
-The maximal number of sections is denoted `NR_MEM_SECTIONS` and
-defined as
-
-.. math::
-
-   NR\_MEM\_SECTIONS = 2 ^ {(MAX\_PHYSMEM\_BITS - SECTION\_SIZE\_BITS)}
-
-The `mem_section` objects are arranged in a two-dimensional array
-called `mem_sections`. The size and placement of this array depend
-on `CONFIG_SPARSEMEM_EXTREME` and the maximal possible number of
-sections:
-
-* When `CONFIG_SPARSEMEM_EXTREME` is disabled, the `mem_sections`
-  array is static and has `NR_MEM_SECTIONS` rows. Each row holds a
-  single `mem_section` object.
-* When `CONFIG_SPARSEMEM_EXTREME` is enabled, the `mem_sections`
-  array is dynamically allocated. Each row contains PAGE_SIZE worth of
-  `mem_section` objects and the number of rows is calculated to fit
-  all the memory sections.
-
-The architecture setup code should call sparse_init() to
-initialize the memory sections and the memory maps.
-
-With SPARSEMEM there are two possible ways to convert a PFN to the
-corresponding `struct page` - a "classic sparse" and "sparse
-vmemmap". The selection is made at build time and it is determined by
-the value of `CONFIG_SPARSEMEM_VMEMMAP`.
-
-The classic sparse encodes the section number of a page in page->flags
-and uses high bits of a PFN to access the section that maps that page
-frame. Inside a section, the PFN is the index to the array of pages.
-
-The sparse vmemmap uses a virtually mapped memory map to optimize
-pfn_to_page and page_to_pfn operations. There is a global `struct
-page *vmemmap` pointer that points to a virtually contiguous array of
-`struct page` objects. A PFN is an index to that array and the
-offset of the `struct page` from `vmemmap` is the PFN of that
-page.
-
-To use vmemmap, an architecture has to reserve a range of virtual
-addresses that will map the physical pages containing the memory
-map and make sure that `vmemmap` points to that range. In addition,
-the architecture should implement :c:func:`vmemmap_populate` method
-that will allocate the physical memory and create page tables for the
-virtual memory map. If an architecture does not have any special
-requirements for the vmemmap mappings, it can use default
-:c:func:`vmemmap_populate_basepages` provided by the generic memory
-management.
-
-The virtually mapped memory map allows storing `struct page` objects
-for persistent memory devices in pre-allocated storage on those
-devices. This storage is represented with struct vmem_altmap
-that is eventually passed to vmemmap_populate() through a long chain
-of function calls. The vmemmap_populate() implementation may use the
-`vmem_altmap` along with :c:func:`vmemmap_alloc_block_buf` helper to
-allocate memory map on the persistent memory device.
-
-ZONE_DEVICE
-===========
-The `ZONE_DEVICE` facility builds upon `SPARSEMEM_VMEMMAP` to offer
-`struct page` `mem_map` services for device driver identified physical
-address ranges. The "device" aspect of `ZONE_DEVICE` relates to the fact
-that the page objects for these address ranges are never marked online,
-and that a reference must be taken against the device, not just the page
-to keep the memory pinned for active use. `ZONE_DEVICE`, via
-:c:func:`devm_memremap_pages`, performs just enough memory hotplug to
-turn on :c:func:`pfn_to_page`, :c:func:`page_to_pfn`, and
-:c:func:`get_user_pages` service for the given range of pfns. Since the
-page reference count never drops below 1 the page is never tracked as
-free memory and the page's `struct list_head lru` space is repurposed
-for back referencing to the host device / driver that mapped the memory.
-
-While `SPARSEMEM` presents memory as a collection of sections,
-optionally collected into memory blocks, `ZONE_DEVICE` users have a need
-for smaller granularity of populating the `mem_map`. Given that
-`ZONE_DEVICE` memory is never marked online it is subsequently never
-subject to its memory ranges being exposed through the sysfs memory
-hotplug api on memory block boundaries. The implementation relies on
-this lack of user-api constraint to allow sub-section sized memory
-ranges to be specified to :c:func:`arch_add_memory`, the top-half of
-memory hotplug. Sub-section support allows for 2MB as the cross-arch
-common alignment granularity for :c:func:`devm_memremap_pages`.
-
-The users of `ZONE_DEVICE` are:
-
-* pmem: Map platform persistent memory to be used as a direct-I/O target
-  via DAX mappings.
-
-* hmm: Extend `ZONE_DEVICE` with `->page_fault()` and `->page_free()`
-  event callbacks to allow a device-driver to coordinate memory management
-  events related to device-memory, typically GPU memory. See
-  Documentation/vm/hmm.rst.
-
-* p2pdma: Create `struct page` objects to allow peer devices in a
-  PCI/-E topology to coordinate direct-DMA operations between themselves,
-  i.e. bypass host memory.
diff --git a/Documentation/vm/mmu_notifier.rst b/Documentation/vm/mmu_notifier.rst
deleted file mode 100644 (file)
index df5d777..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-.. _mmu_notifier:
-
-When do you need to notify inside page table lock ?
-===================================================
-
-When clearing a pte/pmd we are given a choice to notify the event through
-(notify version of \*_clear_flush call mmu_notifier_invalidate_range) under
-the page table lock. But that notification is not necessary in all cases.
-
-For secondary TLB (non CPU TLB) like IOMMU TLB or device TLB (when device use
-thing like ATS/PASID to get the IOMMU to walk the CPU page table to access a
-process virtual address space). There is only 2 cases when you need to notify
-those secondary TLB while holding page table lock when clearing a pte/pmd:
-
-  A) page backing address is free before mmu_notifier_invalidate_range_end()
-  B) a page table entry is updated to point to a new page (COW, write fault
-     on zero page, __replace_page(), ...)
-
-Case A is obvious you do not want to take the risk for the device to write to
-a page that might now be used by some completely different task.
-
-Case B is more subtle. For correctness it requires the following sequence to
-happen:
-
-  - take page table lock
-  - clear page table entry and notify ([pmd/pte]p_huge_clear_flush_notify())
-  - set page table entry to point to new page
-
-If clearing the page table entry is not followed by a notify before setting
-the new pte/pmd value then you can break memory model like C11 or C++11 for
-the device.
-
-Consider the following scenario (device use a feature similar to ATS/PASID):
-
-Two address addrA and addrB such that \|addrA - addrB\| >= PAGE_SIZE we assume
-they are write protected for COW (other case of B apply too).
-
-::
-
- [Time N] --------------------------------------------------------------------
- CPU-thread-0  {try to write to addrA}
- CPU-thread-1  {try to write to addrB}
- CPU-thread-2  {}
- CPU-thread-3  {}
- DEV-thread-0  {read addrA and populate device TLB}
- DEV-thread-2  {read addrB and populate device TLB}
- [Time N+1] ------------------------------------------------------------------
- CPU-thread-0  {COW_step0: {mmu_notifier_invalidate_range_start(addrA)}}
- CPU-thread-1  {COW_step0: {mmu_notifier_invalidate_range_start(addrB)}}
- CPU-thread-2  {}
- CPU-thread-3  {}
- DEV-thread-0  {}
- DEV-thread-2  {}
- [Time N+2] ------------------------------------------------------------------
- CPU-thread-0  {COW_step1: {update page table to point to new page for addrA}}
- CPU-thread-1  {COW_step1: {update page table to point to new page for addrB}}
- CPU-thread-2  {}
- CPU-thread-3  {}
- DEV-thread-0  {}
- DEV-thread-2  {}
- [Time N+3] ------------------------------------------------------------------
- CPU-thread-0  {preempted}
- CPU-thread-1  {preempted}
- CPU-thread-2  {write to addrA which is a write to new page}
- CPU-thread-3  {}
- DEV-thread-0  {}
- DEV-thread-2  {}
- [Time N+3] ------------------------------------------------------------------
- CPU-thread-0  {preempted}
- CPU-thread-1  {preempted}
- CPU-thread-2  {}
- CPU-thread-3  {write to addrB which is a write to new page}
- DEV-thread-0  {}
- DEV-thread-2  {}
- [Time N+4] ------------------------------------------------------------------
- CPU-thread-0  {preempted}
- CPU-thread-1  {COW_step3: {mmu_notifier_invalidate_range_end(addrB)}}
- CPU-thread-2  {}
- CPU-thread-3  {}
- DEV-thread-0  {}
- DEV-thread-2  {}
- [Time N+5] ------------------------------------------------------------------
- CPU-thread-0  {preempted}
- CPU-thread-1  {}
- CPU-thread-2  {}
- CPU-thread-3  {}
- DEV-thread-0  {read addrA from old page}
- DEV-thread-2  {read addrB from new page}
-
-So here because at time N+2 the clear page table entry was not pair with a
-notification to invalidate the secondary TLB, the device see the new value for
-addrB before seeing the new value for addrA. This break total memory ordering
-for the device.
-
-When changing a pte to write protect or to point to a new write protected page
-with same content (KSM) it is fine to delay the mmu_notifier_invalidate_range
-call to mmu_notifier_invalidate_range_end() outside the page table lock. This
-is true even if the thread doing the page table update is preempted right after
-releasing page table lock but before call mmu_notifier_invalidate_range_end().
diff --git a/Documentation/vm/numa.rst b/Documentation/vm/numa.rst
deleted file mode 100644 (file)
index 99fdeca..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-.. _numa:
-
-Started Nov 1999 by Kanoj Sarcar <kanoj@sgi.com>
-
-=============
-What is NUMA?
-=============
-
-This question can be answered from a couple of perspectives:  the
-hardware view and the Linux software view.
-
-From the hardware perspective, a NUMA system is a computer platform that
-comprises multiple components or assemblies each of which may contain 0
-or more CPUs, local memory, and/or IO buses.  For brevity and to
-disambiguate the hardware view of these physical components/assemblies
-from the software abstraction thereof, we'll call the components/assemblies
-'cells' in this document.
-
-Each of the 'cells' may be viewed as an SMP [symmetric multi-processor] subset
-of the system--although some components necessary for a stand-alone SMP system
-may not be populated on any given cell.   The cells of the NUMA system are
-connected together with some sort of system interconnect--e.g., a crossbar or
-point-to-point link are common types of NUMA system interconnects.  Both of
-these types of interconnects can be aggregated to create NUMA platforms with
-cells at multiple distances from other cells.
-
-For Linux, the NUMA platforms of interest are primarily what is known as Cache
-Coherent NUMA or ccNUMA systems.   With ccNUMA systems, all memory is visible
-to and accessible from any CPU attached to any cell and cache coherency
-is handled in hardware by the processor caches and/or the system interconnect.
-
-Memory access time and effective memory bandwidth varies depending on how far
-away the cell containing the CPU or IO bus making the memory access is from the
-cell containing the target memory.  For example, access to memory by CPUs
-attached to the same cell will experience faster access times and higher
-bandwidths than accesses to memory on other, remote cells.  NUMA platforms
-can have cells at multiple remote distances from any given cell.
-
-Platform vendors don't build NUMA systems just to make software developers'
-lives interesting.  Rather, this architecture is a means to provide scalable
-memory bandwidth.  However, to achieve scalable memory bandwidth, system and
-application software must arrange for a large majority of the memory references
-[cache misses] to be to "local" memory--memory on the same cell, if any--or
-to the closest cell with memory.
-
-This leads to the Linux software view of a NUMA system:
-
-Linux divides the system's hardware resources into multiple software
-abstractions called "nodes".  Linux maps the nodes onto the physical cells
-of the hardware platform, abstracting away some of the details for some
-architectures.  As with physical cells, software nodes may contain 0 or more
-CPUs, memory and/or IO buses.  And, again, memory accesses to memory on
-"closer" nodes--nodes that map to closer cells--will generally experience
-faster access times and higher effective bandwidth than accesses to more
-remote cells.
-
-For some architectures, such as x86, Linux will "hide" any node representing a
-physical cell that has no memory attached, and reassign any CPUs attached to
-that cell to a node representing a cell that does have memory.  Thus, on
-these architectures, one cannot assume that all CPUs that Linux associates with
-a given node will see the same local memory access times and bandwidth.
-
-In addition, for some architectures, again x86 is an example, Linux supports
-the emulation of additional nodes.  For NUMA emulation, linux will carve up
-the existing nodes--or the system memory for non-NUMA platforms--into multiple
-nodes.  Each emulated node will manage a fraction of the underlying cells'
-physical memory.  NUMA emluation is useful for testing NUMA kernel and
-application features on non-NUMA platforms, and as a sort of memory resource
-management mechanism when used together with cpusets.
-[see Documentation/admin-guide/cgroup-v1/cpusets.rst]
-
-For each node with memory, Linux constructs an independent memory management
-subsystem, complete with its own free page lists, in-use page lists, usage
-statistics and locks to mediate access.  In addition, Linux constructs for
-each memory zone [one or more of DMA, DMA32, NORMAL, HIGH_MEMORY, MOVABLE],
-an ordered "zonelist".  A zonelist specifies the zones/nodes to visit when a
-selected zone/node cannot satisfy the allocation request.  This situation,
-when a zone has no available memory to satisfy a request, is called
-"overflow" or "fallback".
-
-Because some nodes contain multiple zones containing different types of
-memory, Linux must decide whether to order the zonelists such that allocations
-fall back to the same zone type on a different node, or to a different zone
-type on the same node.  This is an important consideration because some zones,
-such as DMA or DMA32, represent relatively scarce resources.  Linux chooses
-a default Node ordered zonelist. This means it tries to fallback to other zones
-from the same node before using remote nodes which are ordered by NUMA distance.
-
-By default, Linux will attempt to satisfy memory allocation requests from the
-node to which the CPU that executes the request is assigned.  Specifically,
-Linux will attempt to allocate from the first node in the appropriate zonelist
-for the node where the request originates.  This is called "local allocation."
-If the "local" node cannot satisfy the request, the kernel will examine other
-nodes' zones in the selected zonelist looking for the first zone in the list
-that can satisfy the request.
-
-Local allocation will tend to keep subsequent access to the allocated memory
-"local" to the underlying physical resources and off the system interconnect--
-as long as the task on whose behalf the kernel allocated some memory does not
-later migrate away from that memory.  The Linux scheduler is aware of the
-NUMA topology of the platform--embodied in the "scheduling domains" data
-structures [see Documentation/scheduler/sched-domains.rst]--and the scheduler
-attempts to minimize task migration to distant scheduling domains.  However,
-the scheduler does not take a task's NUMA footprint into account directly.
-Thus, under sufficient imbalance, tasks can migrate between nodes, remote
-from their initial node and kernel data structures.
-
-System administrators and application designers can restrict a task's migration
-to improve NUMA locality using various CPU affinity command line interfaces,
-such as taskset(1) and numactl(1), and program interfaces such as
-sched_setaffinity(2).  Further, one can modify the kernel's default local
-allocation behavior using Linux NUMA memory policy. [see
-:ref:`Documentation/admin-guide/mm/numa_memory_policy.rst <numa_memory_policy>`].
-
-System administrators can restrict the CPUs and nodes' memories that a non-
-privileged user can specify in the scheduling or NUMA commands and functions
-using control groups and CPUsets.  [see Documentation/admin-guide/cgroup-v1/cpusets.rst]
-
-On architectures that do not hide memoryless nodes, Linux will include only
-zones [nodes] with memory in the zonelists.  This means that for a memoryless
-node the "local memory node"--the node of the first zone in CPU's node's
-zonelist--will not be the node itself.  Rather, it will be the node that the
-kernel selected as the nearest node with memory when it built the zonelists.
-So, default, local allocations will succeed with the kernel supplying the
-closest available memory.  This is a consequence of the same mechanism that
-allows such allocations to fallback to other nearby nodes when a node that
-does contain memory overflows.
-
-Some kernel allocations do not want or cannot tolerate this allocation fallback
-behavior.  Rather they want to be sure they get memory from the specified node
-or get notified that the node has no free memory.  This is usually the case when
-a subsystem allocates per CPU memory resources, for example.
-
-A typical model for making such an allocation is to obtain the node id of the
-node to which the "current CPU" is attached using one of the kernel's
-numa_node_id() or CPU_to_node() functions and then request memory from only
-the node id returned.  When such an allocation fails, the requesting subsystem
-may revert to its own fallback path.  The slab kernel memory allocator is an
-example of this.  Or, the subsystem may choose to disable or not to enable
-itself on allocation failure.  The kernel profiling subsystem is an example of
-this.
-
-If the architecture supports--does not hide--memoryless nodes, then CPUs
-attached to memoryless nodes would always incur the fallback path overhead
-or some subsystems would fail to initialize if they attempted to allocated
-memory exclusively from a node without memory.  To support such
-architectures transparently, kernel subsystems can use the numa_mem_id()
-or cpu_to_mem() function to locate the "local memory node" for the calling or
-specified CPU.  Again, this is the same node from which default, local page
-allocations will be attempted.
diff --git a/Documentation/vm/oom.rst b/Documentation/vm/oom.rst
deleted file mode 100644 (file)
index 18e9e40..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-======================
-Out Of Memory Handling
-======================
diff --git a/Documentation/vm/overcommit-accounting.rst b/Documentation/vm/overcommit-accounting.rst
deleted file mode 100644 (file)
index a4895d6..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-=====================
-Overcommit Accounting
-=====================
-
-The Linux kernel supports the following overcommit handling modes
-
-0
-       Heuristic overcommit handling. Obvious overcommits of address
-       space are refused. Used for a typical system. It ensures a
-       seriously wild allocation fails while allowing overcommit to
-       reduce swap usage.  root is allowed to allocate slightly more
-       memory in this mode. This is the default.
-
-1
-       Always overcommit. Appropriate for some scientific
-       applications. Classic example is code using sparse arrays and
-       just relying on the virtual memory consisting almost entirely
-       of zero pages.
-
-2
-       Don't overcommit. The total address space commit for the
-       system is not permitted to exceed swap + a configurable amount
-       (default is 50%) of physical RAM.  Depending on the amount you
-       use, in most situations this means a process will not be
-       killed while accessing pages but will receive errors on memory
-       allocation as appropriate.
-
-       Useful for applications that want to guarantee their memory
-       allocations will be available in the future without having to
-       initialize every page.
-
-The overcommit policy is set via the sysctl ``vm.overcommit_memory``.
-
-The overcommit amount can be set via ``vm.overcommit_ratio`` (percentage)
-or ``vm.overcommit_kbytes`` (absolute value). These only have an effect
-when ``vm.overcommit_memory`` is set to 2.
-
-The current overcommit limit and amount committed are viewable in
-``/proc/meminfo`` as CommitLimit and Committed_AS respectively.
-
-Gotchas
-=======
-
-The C language stack growth does an implicit mremap. If you want absolute
-guarantees and run close to the edge you MUST mmap your stack for the
-largest size you think you will need. For typical stack usage this does
-not matter much but it's a corner case if you really really care
-
-In mode 2 the MAP_NORESERVE flag is ignored.
-
-
-How It Works
-============
-
-The overcommit is based on the following rules
-
-For a file backed map
-       | SHARED or READ-only   -       0 cost (the file is the map not swap)
-       | PRIVATE WRITABLE      -       size of mapping per instance
-
-For an anonymous or ``/dev/zero`` map
-       | SHARED                        -       size of mapping
-       | PRIVATE READ-only     -       0 cost (but of little use)
-       | PRIVATE WRITABLE      -       size of mapping per instance
-
-Additional accounting
-       | Pages made writable copies by mmap
-       | shmfs memory drawn from the same pool
-
-Status
-======
-
-*      We account mmap memory mappings
-*      We account mprotect changes in commit
-*      We account mremap changes in size
-*      We account brk
-*      We account munmap
-*      We report the commit status in /proc
-*      Account and check on fork
-*      Review stack handling/building on exec
-*      SHMfs accounting
-*      Implement actual limit enforcement
-
-To Do
-=====
-*      Account ptrace pages (this is hard)
diff --git a/Documentation/vm/page_allocation.rst b/Documentation/vm/page_allocation.rst
deleted file mode 100644 (file)
index d9b4495..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-===============
-Page Allocation
-===============
diff --git a/Documentation/vm/page_cache.rst b/Documentation/vm/page_cache.rst
deleted file mode 100644 (file)
index 75eba7c..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-==========
-Page Cache
-==========
diff --git a/Documentation/vm/page_frags.rst b/Documentation/vm/page_frags.rst
deleted file mode 100644 (file)
index 7d6f938..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-.. _page_frags:
-
-==============
-Page fragments
-==============
-
-A page fragment is an arbitrary-length arbitrary-offset area of memory
-which resides within a 0 or higher order compound page.  Multiple
-fragments within that page are individually refcounted, in the page's
-reference counter.
-
-The page_frag functions, page_frag_alloc and page_frag_free, provide a
-simple allocation framework for page fragments.  This is used by the
-network stack and network device drivers to provide a backing region of
-memory for use as either an sk_buff->head, or to be used in the "frags"
-portion of skb_shared_info.
-
-In order to make use of the page fragment APIs a backing page fragment
-cache is needed.  This provides a central point for the fragment allocation
-and tracks allows multiple calls to make use of a cached page.  The
-advantage to doing this is that multiple calls to get_page can be avoided
-which can be expensive at allocation time.  However due to the nature of
-this caching it is required that any calls to the cache be protected by
-either a per-cpu limitation, or a per-cpu limitation and forcing interrupts
-to be disabled when executing the fragment allocation.
-
-The network stack uses two separate caches per CPU to handle fragment
-allocation.  The netdev_alloc_cache is used by callers making use of the
-netdev_alloc_frag and __netdev_alloc_skb calls.  The napi_alloc_cache is
-used by callers of the __napi_alloc_frag and __napi_alloc_skb calls.  The
-main difference between these two calls is the context in which they may be
-called.  The "netdev" prefixed functions are usable in any context as these
-functions will disable interrupts, while the "napi" prefixed functions are
-only usable within the softirq context.
-
-Many network device drivers use a similar methodology for allocating page
-fragments, but the page fragments are cached at the ring or descriptor
-level.  In order to enable these cases it is necessary to provide a generic
-way of tearing down a page cache.  For this reason __page_frag_cache_drain
-was implemented.  It allows for freeing multiple references from a single
-page via a single call.  The advantage to doing this is that it allows for
-cleaning up the multiple references that were added to a page in order to
-avoid calling get_page per allocation.
-
-Alexander Duyck, Nov 29, 2016.
diff --git a/Documentation/vm/page_migration.rst b/Documentation/vm/page_migration.rst
deleted file mode 100644 (file)
index 11493ba..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-.. _page_migration:
-
-==============
-Page migration
-==============
-
-Page migration allows moving the physical location of pages between
-nodes in a NUMA system while the process is running. This means that the
-virtual addresses that the process sees do not change. However, the
-system rearranges the physical location of those pages.
-
-Also see :ref:`Heterogeneous Memory Management (HMM) <hmm>`
-for migrating pages to or from device private memory.
-
-The main intent of page migration is to reduce the latency of memory accesses
-by moving pages near to the processor where the process accessing that memory
-is running.
-
-Page migration allows a process to manually relocate the node on which its
-pages are located through the MF_MOVE and MF_MOVE_ALL options while setting
-a new memory policy via mbind(). The pages of a process can also be relocated
-from another process using the sys_migrate_pages() function call. The
-migrate_pages() function call takes two sets of nodes and moves pages of a
-process that are located on the from nodes to the destination nodes.
-Page migration functions are provided by the numactl package by Andi Kleen
-(a version later than 0.9.3 is required. Get it from
-https://github.com/numactl/numactl.git). numactl provides libnuma
-which provides an interface similar to other NUMA functionality for page
-migration.  cat ``/proc/<pid>/numa_maps`` allows an easy review of where the
-pages of a process are located. See also the numa_maps documentation in the
-proc(5) man page.
-
-Manual migration is useful if for example the scheduler has relocated
-a process to a processor on a distant node. A batch scheduler or an
-administrator may detect the situation and move the pages of the process
-nearer to the new processor. The kernel itself only provides
-manual page migration support. Automatic page migration may be implemented
-through user space processes that move pages. A special function call
-"move_pages" allows the moving of individual pages within a process.
-For example, A NUMA profiler may obtain a log showing frequent off-node
-accesses and may use the result to move pages to more advantageous
-locations.
-
-Larger installations usually partition the system using cpusets into
-sections of nodes. Paul Jackson has equipped cpusets with the ability to
-move pages when a task is moved to another cpuset (See
-:ref:`CPUSETS <cpusets>`).
-Cpusets allow the automation of process locality. If a task is moved to
-a new cpuset then also all its pages are moved with it so that the
-performance of the process does not sink dramatically. Also the pages
-of processes in a cpuset are moved if the allowed memory nodes of a
-cpuset are changed.
-
-Page migration allows the preservation of the relative location of pages
-within a group of nodes for all migration techniques which will preserve a
-particular memory allocation pattern generated even after migrating a
-process. This is necessary in order to preserve the memory latencies.
-Processes will run with similar performance after migration.
-
-Page migration occurs in several steps. First a high level
-description for those trying to use migrate_pages() from the kernel
-(for userspace usage see the Andi Kleen's numactl package mentioned above)
-and then a low level description of how the low level details work.
-
-In kernel use of migrate_pages()
-================================
-
-1. Remove pages from the LRU.
-
-   Lists of pages to be migrated are generated by scanning over
-   pages and moving them into lists. This is done by
-   calling isolate_lru_page().
-   Calling isolate_lru_page() increases the references to the page
-   so that it cannot vanish while the page migration occurs.
-   It also prevents the swapper or other scans from encountering
-   the page.
-
-2. We need to have a function of type new_page_t that can be
-   passed to migrate_pages(). This function should figure out
-   how to allocate the correct new page given the old page.
-
-3. The migrate_pages() function is called which attempts
-   to do the migration. It will call the function to allocate
-   the new page for each page that is considered for
-   moving.
-
-How migrate_pages() works
-=========================
-
-migrate_pages() does several passes over its list of pages. A page is moved
-if all references to a page are removable at the time. The page has
-already been removed from the LRU via isolate_lru_page() and the refcount
-is increased so that the page cannot be freed while page migration occurs.
-
-Steps:
-
-1. Lock the page to be migrated.
-
-2. Ensure that writeback is complete.
-
-3. Lock the new page that we want to move to. It is locked so that accesses to
-   this (not yet up-to-date) page immediately block while the move is in progress.
-
-4. All the page table references to the page are converted to migration
-   entries. This decreases the mapcount of a page. If the resulting
-   mapcount is not zero then we do not migrate the page. All user space
-   processes that attempt to access the page will now wait on the page lock
-   or wait for the migration page table entry to be removed.
-
-5. The i_pages lock is taken. This will cause all processes trying
-   to access the page via the mapping to block on the spinlock.
-
-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.
-
-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.
-
-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
-    the new page is referenced by the address space.
-
-11. The i_pages lock is dropped. With that lookups in the mapping
-    become possible again. Processes will move from spinning on the lock
-    to sleeping on the locked new page.
-
-12. The page contents are copied to the new page.
-
-13. The remaining page flags are copied to the new page.
-
-14. The old page flags are cleared to indicate that the page does
-    not provide any information anymore.
-
-15. Queued up writeback on the new page is triggered.
-
-16. If migration entries were inserted into the page table, then replace them
-    with real ptes. Doing so will enable access for user space processes not
-    already waiting for the page lock.
-
-17. The page locks are dropped from the old and new page.
-    Processes waiting on the page lock will redo their page faults
-    and will reach the new page.
-
-18. The new page is moved to the LRU and can be scanned by the swapper,
-    etc. again.
-
-Non-LRU page migration
-======================
-
-Although migration originally aimed for reducing the latency of memory
-accesses for NUMA, compaction also uses migration to create high-order
-pages.  For compaction purposes, it is also useful to be able to move
-non-LRU pages, such as zsmalloc and virtio-balloon pages.
-
-If a driver wants to make its pages movable, it should define a struct
-movable_operations.  It then needs to call __SetPageMovable() on each
-page that it may be able to move.  This uses the ``page->mapping`` field,
-so this field is not available for the driver to use for other purposes.
-
-Monitoring Migration
-=====================
-
-The following events (counters) can be used to monitor page migration.
-
-1. PGMIGRATE_SUCCESS: Normal page migration success. Each count means that a
-   page was migrated. If the page was a non-THP and non-hugetlb page, then
-   this counter is increased by one. If the page was a THP or hugetlb, then
-   this counter is increased by the number of THP or hugetlb subpages.
-   For example, migration of a single 2MB THP that has 4KB-size base pages
-   (subpages) will cause this counter to increase by 512.
-
-2. PGMIGRATE_FAIL: Normal page migration failure. Same counting rules as for
-   PGMIGRATE_SUCCESS, above: this will be increased by the number of subpages,
-   if it was a THP or hugetlb.
-
-3. THP_MIGRATION_SUCCESS: A THP was migrated without being split.
-
-4. THP_MIGRATION_FAIL: A THP could not be migrated nor it could be split.
-
-5. THP_MIGRATION_SPLIT: A THP was migrated, but not as such: first, the THP had
-   to be split. After splitting, a migration retry was used for it's sub-pages.
-
-THP_MIGRATION_* events also update the appropriate PGMIGRATE_SUCCESS or
-PGMIGRATE_FAIL events. For example, a THP migration failure will cause both
-THP_MIGRATION_FAIL and PGMIGRATE_FAIL to increase.
-
-Christoph Lameter, May 8, 2006.
-Minchan Kim, Mar 28, 2016.
-
-.. kernel-doc:: include/linux/migrate.h
diff --git a/Documentation/vm/page_owner.rst b/Documentation/vm/page_owner.rst
deleted file mode 100644 (file)
index f5c954a..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-.. _page_owner:
-
-==================================================
-page owner: Tracking about who allocated each page
-==================================================
-
-Introduction
-============
-
-page owner is for the tracking about who allocated each page.
-It can be used to debug memory leak or to find a memory hogger.
-When allocation happens, information about allocation such as call stack
-and order of pages is stored into certain storage for each page.
-When we need to know about status of all pages, we can get and analyze
-this information.
-
-Although we already have tracepoint for tracing page allocation/free,
-using it for analyzing who allocate each page is rather complex. We need
-to enlarge the trace buffer for preventing overlapping until userspace
-program launched. And, launched program continually dump out the trace
-buffer for later analysis and it would change system behaviour with more
-possibility rather than just keeping it in memory, so bad for debugging.
-
-page owner can also be used for various purposes. For example, accurate
-fragmentation statistics can be obtained through gfp flag information of
-each page. It is already implemented and activated if page owner is
-enabled. Other usages are more than welcome.
-
-page owner is disabled by default. So, if you'd like to use it, you need
-to add "page_owner=on" to your boot cmdline. If the kernel is built
-with page owner and page owner is disabled in runtime due to not enabling
-boot option, runtime overhead is marginal. If disabled in runtime, it
-doesn't require memory to store owner information, so there is no runtime
-memory overhead. And, page owner inserts just two unlikely branches into
-the page allocator hotpath and if not enabled, then allocation is done
-like as the kernel without page owner. These two unlikely branches should
-not affect to allocation performance, especially if the static keys jump
-label patching functionality is available. Following is the kernel's code
-size change due to this facility.
-
-- Without page owner::
-
-   text    data     bss     dec     hex filename
-   48392   2333     644   51369    c8a9 mm/page_alloc.o
-
-- With page owner::
-
-   text    data     bss     dec     hex filename
-   48800   2445     644   51889    cab1 mm/page_alloc.o
-   6662     108      29    6799    1a8f mm/page_owner.o
-   1025       8       8    1041     411 mm/page_ext.o
-
-Although, roughly, 8 KB code is added in total, page_alloc.o increase by
-520 bytes and less than half of it is in hotpath. Building the kernel with
-page owner and turning it on if needed would be great option to debug
-kernel memory problem.
-
-There is one notice that is caused by implementation detail. page owner
-stores information into the memory from struct page extension. This memory
-is initialized some time later than that page allocator starts in sparse
-memory system, so, until initialization, many pages can be allocated and
-they would have no owner information. To fix it up, these early allocated
-pages are investigated and marked as allocated in initialization phase.
-Although it doesn't mean that they have the right owner information,
-at least, we can tell whether the page is allocated or not,
-more accurately. On 2GB memory x86-64 VM box, 13343 early allocated pages
-are catched and marked, although they are mostly allocated from struct
-page extension feature. Anyway, after that, no page is left in
-un-tracking state.
-
-Usage
-=====
-
-1) Build user-space helper::
-
-       cd tools/vm
-       make page_owner_sort
-
-2) Enable page owner: add "page_owner=on" to boot cmdline.
-
-3) Do the job that you want to debug.
-
-4) Analyze information from page owner::
-
-       cat /sys/kernel/debug/page_owner > page_owner_full.txt
-       ./page_owner_sort page_owner_full.txt sorted_page_owner.txt
-
-   The general output of ``page_owner_full.txt`` is as follows::
-
-       Page allocated via order XXX, ...
-       PFN XXX ...
-       // Detailed stack
-
-       Page allocated via order XXX, ...
-       PFN XXX ...
-       // Detailed stack
-
-   The ``page_owner_sort`` tool ignores ``PFN`` rows, puts the remaining rows
-   in buf, uses regexp to extract the page order value, counts the times
-   and pages of buf, and finally sorts them according to the parameter(s).
-
-   See the result about who allocated each page
-   in the ``sorted_page_owner.txt``. General output::
-
-       XXX times, XXX pages:
-       Page allocated via order XXX, ...
-       // Detailed stack
-
-   By default, ``page_owner_sort`` is sorted according to the times of buf.
-   If you want to sort by the page nums of buf, use the ``-m`` parameter.
-   The detailed parameters are:
-
-   fundamental function::
-
-       Sort:
-               -a              Sort by memory allocation time.
-               -m              Sort by total memory.
-               -p              Sort by pid.
-               -P              Sort by tgid.
-               -n              Sort by task command name.
-               -r              Sort by memory release time.
-               -s              Sort by stack trace.
-               -t              Sort by times (default).
-               --sort <order>  Specify sorting order.  Sorting syntax is [+|-]key[,[+|-]key[,...]].
-                               Choose a key from the **STANDARD FORMAT SPECIFIERS** section. The "+" is
-                               optional since default direction is increasing numerical or lexicographic
-                               order. Mixed use of abbreviated and complete-form of keys is allowed.
-
-               Examples:
-                               ./page_owner_sort <input> <output> --sort=n,+pid,-tgid
-                               ./page_owner_sort <input> <output> --sort=at
-
-   additional function::
-
-       Cull:
-               --cull <rules>
-                               Specify culling rules.Culling syntax is key[,key[,...]].Choose a
-                               multi-letter key from the **STANDARD FORMAT SPECIFIERS** section.
-
-               <rules> is a single argument in the form of a comma-separated list,
-               which offers a way to specify individual culling rules.  The recognized
-               keywords are described in the **STANDARD FORMAT SPECIFIERS** section below.
-               <rules> can be specified by the sequence of keys k1,k2, ..., as described in
-               the STANDARD SORT KEYS section below. Mixed use of abbreviated and
-               complete-form of keys is allowed.
-
-               Examples:
-                               ./page_owner_sort <input> <output> --cull=stacktrace
-                               ./page_owner_sort <input> <output> --cull=st,pid,name
-                               ./page_owner_sort <input> <output> --cull=n,f
-
-       Filter:
-               -f              Filter out the information of blocks whose memory has been released.
-
-       Select:
-               --pid <pidlist>         Select by pid. This selects the blocks whose process ID
-                                       numbers appear in <pidlist>.
-               --tgid <tgidlist>       Select by tgid. This selects the blocks whose thread
-                                       group ID numbers appear in <tgidlist>.
-               --name <cmdlist>        Select by task command name. This selects the blocks whose
-                                       task command name appear in <cmdlist>.
-
-               <pidlist>, <tgidlist>, <cmdlist> are single arguments in the form of a comma-separated list,
-               which offers a way to specify individual selecting rules.
-
-
-               Examples:
-                               ./page_owner_sort <input> <output> --pid=1
-                               ./page_owner_sort <input> <output> --tgid=1,2,3
-                               ./page_owner_sort <input> <output> --name name1,name2
-
-STANDARD FORMAT SPECIFIERS
-==========================
-::
-
-  For --sort option:
-
-       KEY             LONG            DESCRIPTION
-       p               pid             process ID
-       tg              tgid            thread group ID
-       n               name            task command name
-       st              stacktrace      stack trace of the page allocation
-       T               txt             full text of block
-       ft              free_ts         timestamp of the page when it was released
-       at              alloc_ts        timestamp of the page when it was allocated
-       ator            allocator       memory allocator for pages
-
-  For --curl option:
-
-       KEY             LONG            DESCRIPTION
-       p               pid             process ID
-       tg              tgid            thread group ID
-       n               name            task command name
-       f               free            whether the page has been released or not
-       st              stacktrace      stack trace of the page allocation
-       ator            allocator       memory allocator for pages
diff --git a/Documentation/vm/page_reclaim.rst b/Documentation/vm/page_reclaim.rst
deleted file mode 100644 (file)
index 50a30b7..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-============
-Page Reclaim
-============
diff --git a/Documentation/vm/page_table_check.rst b/Documentation/vm/page_table_check.rst
deleted file mode 100644 (file)
index 1a09472..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-.. _page_table_check:
-
-================
-Page Table Check
-================
-
-Introduction
-============
-
-Page table check allows to harden the kernel by ensuring that some types of
-the memory corruptions are prevented.
-
-Page table check performs extra verifications at the time when new pages become
-accessible from the userspace by getting their page table entries (PTEs PMDs
-etc.) added into the table.
-
-In case of detected corruption, the kernel is crashed. There is a small
-performance and memory overhead associated with the page table check. Therefore,
-it is disabled by default, but can be optionally enabled on systems where the
-extra hardening outweighs the performance costs. Also, because page table check
-is synchronous, it can help with debugging double map memory corruption issues,
-by crashing kernel at the time wrong mapping occurs instead of later which is
-often the case with memory corruptions bugs.
-
-Double mapping detection logic
-==============================
-
-+-------------------+-------------------+-------------------+------------------+
-| Current Mapping   | New mapping       | Permissions       | Rule             |
-+===================+===================+===================+==================+
-| Anonymous         | Anonymous         | Read              | Allow            |
-+-------------------+-------------------+-------------------+------------------+
-| Anonymous         | Anonymous         | Read / Write      | Prohibit         |
-+-------------------+-------------------+-------------------+------------------+
-| Anonymous         | Named             | Any               | Prohibit         |
-+-------------------+-------------------+-------------------+------------------+
-| Named             | Anonymous         | Any               | Prohibit         |
-+-------------------+-------------------+-------------------+------------------+
-| Named             | Named             | Any               | Allow            |
-+-------------------+-------------------+-------------------+------------------+
-
-Enabling Page Table Check
-=========================
-
-Build kernel with:
-
-- PAGE_TABLE_CHECK=y
-  Note, it can only be enabled on platforms where ARCH_SUPPORTS_PAGE_TABLE_CHECK
-  is available.
-
-- Boot with 'page_table_check=on' kernel parameter.
-
-Optionally, build kernel with PAGE_TABLE_CHECK_ENFORCED in order to have page
-table support without extra kernel parameter.
diff --git a/Documentation/vm/page_tables.rst b/Documentation/vm/page_tables.rst
deleted file mode 100644 (file)
index 9693957..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-===========
-Page Tables
-===========
diff --git a/Documentation/vm/physical_memory.rst b/Documentation/vm/physical_memory.rst
deleted file mode 100644 (file)
index 2ab7b8c..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-===============
-Physical Memory
-===============
diff --git a/Documentation/vm/process_addrs.rst b/Documentation/vm/process_addrs.rst
deleted file mode 100644 (file)
index e8618fb..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-=================
-Process Addresses
-=================
diff --git a/Documentation/vm/remap_file_pages.rst b/Documentation/vm/remap_file_pages.rst
deleted file mode 100644 (file)
index 7bef671..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-.. _remap_file_pages:
-
-==============================
-remap_file_pages() system call
-==============================
-
-The remap_file_pages() system call is used to create a nonlinear mapping,
-that is, a mapping in which the pages of the file are mapped into a
-nonsequential order in memory. The advantage of using remap_file_pages()
-over using repeated calls to mmap(2) is that the former approach does not
-require the kernel to create additional VMA (Virtual Memory Area) data
-structures.
-
-Supporting of nonlinear mapping requires significant amount of non-trivial
-code in kernel virtual memory subsystem including hot paths. Also to get
-nonlinear mapping work kernel need a way to distinguish normal page table
-entries from entries with file offset (pte_file). Kernel reserves flag in
-PTE for this purpose. PTE flags are scarce resource especially on some CPU
-architectures. It would be nice to free up the flag for other usage.
-
-Fortunately, there are not many users of remap_file_pages() in the wild.
-It's only known that one enterprise RDBMS implementation uses the syscall
-on 32-bit systems to map files bigger than can linearly fit into 32-bit
-virtual address space. This use-case is not critical anymore since 64-bit
-systems are widely available.
-
-The syscall is deprecated and replaced it with an emulation now. The
-emulation creates new VMAs instead of nonlinear mappings. It's going to
-work slower for rare users of remap_file_pages() but ABI is preserved.
-
-One side effect of emulation (apart from performance) is that user can hit
-vm.max_map_count limit more easily due to additional VMAs. See comment for
-DEFAULT_MAX_MAP_COUNT for more details on the limit.
diff --git a/Documentation/vm/shmfs.rst b/Documentation/vm/shmfs.rst
deleted file mode 100644 (file)
index 8b01ebb..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-========================
-Shared Memory Filesystem
-========================
diff --git a/Documentation/vm/slab.rst b/Documentation/vm/slab.rst
deleted file mode 100644 (file)
index 87d5a5b..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-===============
-Slab Allocation
-===============
diff --git a/Documentation/vm/slub.rst b/Documentation/vm/slub.rst
deleted file mode 100644 (file)
index 43063ad..0000000
+++ /dev/null
@@ -1,452 +0,0 @@
-.. _slub:
-
-==========================
-Short users guide for SLUB
-==========================
-
-The basic philosophy of SLUB is very different from SLAB. SLAB
-requires rebuilding the kernel to activate debug options for all
-slab caches. SLUB always includes full debugging but it is off by default.
-SLUB can enable debugging only for selected slabs in order to avoid
-an impact on overall system performance which may make a bug more
-difficult to find.
-
-In order to switch debugging on one can add an option ``slub_debug``
-to the kernel command line. That will enable full debugging for
-all slabs.
-
-Typically one would then use the ``slabinfo`` command to get statistical
-data and perform operation on the slabs. By default ``slabinfo`` only lists
-slabs that have data in them. See "slabinfo -h" for more options when
-running the command. ``slabinfo`` can be compiled with
-::
-
-       gcc -o slabinfo tools/vm/slabinfo.c
-
-Some of the modes of operation of ``slabinfo`` require that slub debugging
-be enabled on the command line. F.e. no tracking information will be
-available without debugging on and validation can only partially
-be performed if debugging was not switched on.
-
-Some more sophisticated uses of slub_debug:
--------------------------------------------
-
-Parameters may be given to ``slub_debug``. If none is specified then full
-debugging is enabled. Format:
-
-slub_debug=<Debug-Options>
-       Enable options for all slabs
-
-slub_debug=<Debug-Options>,<slab name1>,<slab name2>,...
-       Enable options only for select slabs (no spaces
-       after a comma)
-
-Multiple blocks of options for all slabs or selected slabs can be given, with
-blocks of options delimited by ';'. The last of "all slabs" blocks is applied
-to all slabs except those that match one of the "select slabs" block. Options
-of the first "select slabs" blocks that matches the slab's name are applied.
-
-Possible debug options are::
-
-       F               Sanity checks on (enables SLAB_DEBUG_CONSISTENCY_CHECKS
-                       Sorry SLAB legacy issues)
-       Z               Red zoning
-       P               Poisoning (object and padding)
-       U               User tracking (free and alloc)
-       T               Trace (please only use on single slabs)
-       A               Enable failslab filter mark for the cache
-       O               Switch debugging off for caches that would have
-                       caused higher minimum slab orders
-       -               Switch all debugging off (useful if the kernel is
-                       configured with CONFIG_SLUB_DEBUG_ON)
-
-F.e. in order to boot just with sanity checks and red zoning one would specify::
-
-       slub_debug=FZ
-
-Trying to find an issue in the dentry cache? Try::
-
-       slub_debug=,dentry
-
-to only enable debugging on the dentry cache.  You may use an asterisk at the
-end of the slab name, in order to cover all slabs with the same prefix.  For
-example, here's how you can poison the dentry cache as well as all kmalloc
-slabs::
-
-       slub_debug=P,kmalloc-*,dentry
-
-Red zoning and tracking may realign the slab.  We can just apply sanity checks
-to the dentry cache with::
-
-       slub_debug=F,dentry
-
-Debugging options may require the minimum possible slab order to increase as
-a result of storing the metadata (for example, caches with PAGE_SIZE object
-sizes).  This has a higher liklihood of resulting in slab allocation errors
-in low memory situations or if there's high fragmentation of memory.  To
-switch off debugging for such caches by default, use::
-
-       slub_debug=O
-
-You can apply different options to different list of slab names, using blocks
-of options. This will enable red zoning for dentry and user tracking for
-kmalloc. All other slabs will not get any debugging enabled::
-
-       slub_debug=Z,dentry;U,kmalloc-*
-
-You can also enable options (e.g. sanity checks and poisoning) for all caches
-except some that are deemed too performance critical and don't need to be
-debugged by specifying global debug options followed by a list of slab names
-with "-" as options::
-
-       slub_debug=FZ;-,zs_handle,zspage
-
-The state of each debug option for a slab can be found in the respective files
-under::
-
-       /sys/kernel/slab/<slab name>/
-
-If the file contains 1, the option is enabled, 0 means disabled. The debug
-options from the ``slub_debug`` parameter translate to the following files::
-
-       F       sanity_checks
-       Z       red_zone
-       P       poison
-       U       store_user
-       T       trace
-       A       failslab
-
-Careful with tracing: It may spew out lots of information and never stop if
-used on the wrong slab.
-
-Slab merging
-============
-
-If no debug options are specified then SLUB may merge similar slabs together
-in order to reduce overhead and increase cache hotness of objects.
-``slabinfo -a`` displays which slabs were merged together.
-
-Slab validation
-===============
-
-SLUB can validate all object if the kernel was booted with slub_debug. In
-order to do so you must have the ``slabinfo`` tool. Then you can do
-::
-
-       slabinfo -v
-
-which will test all objects. Output will be generated to the syslog.
-
-This also works in a more limited way if boot was without slab debug.
-In that case ``slabinfo -v`` simply tests all reachable objects. Usually
-these are in the cpu slabs and the partial slabs. Full slabs are not
-tracked by SLUB in a non debug situation.
-
-Getting more performance
-========================
-
-To some degree SLUB's performance is limited by the need to take the
-list_lock once in a while to deal with partial slabs. That overhead is
-governed by the order of the allocation for each slab. The allocations
-can be influenced by kernel parameters:
-
-.. slub_min_objects=x          (default 4)
-.. slub_min_order=x            (default 0)
-.. slub_max_order=x            (default 3 (PAGE_ALLOC_COSTLY_ORDER))
-
-``slub_min_objects``
-       allows to specify how many objects must at least fit into one
-       slab in order for the allocation order to be acceptable.  In
-       general slub will be able to perform this number of
-       allocations on a slab without consulting centralized resources
-       (list_lock) where contention may occur.
-
-``slub_min_order``
-       specifies a minimum order of slabs. A similar effect like
-       ``slub_min_objects``.
-
-``slub_max_order``
-       specified the order at which ``slub_min_objects`` should no
-       longer be checked. This is useful to avoid SLUB trying to
-       generate super large order pages to fit ``slub_min_objects``
-       of a slab cache with large object sizes into one high order
-       page. Setting command line parameter
-       ``debug_guardpage_minorder=N`` (N > 0), forces setting
-       ``slub_max_order`` to 0, what cause minimum possible order of
-       slabs allocation.
-
-SLUB Debug output
-=================
-
-Here is a sample of slub debug output::
-
- ====================================================================
- BUG kmalloc-8: Right Redzone overwritten
- --------------------------------------------------------------------
-
- INFO: 0xc90f6d28-0xc90f6d2b. First byte 0x00 instead of 0xcc
- INFO: Slab 0xc528c530 flags=0x400000c3 inuse=61 fp=0xc90f6d58
- INFO: Object 0xc90f6d20 @offset=3360 fp=0xc90f6d58
- INFO: Allocated in get_modalias+0x61/0xf5 age=53 cpu=1 pid=554
-
- Bytes b4 (0xc90f6d10): 00 00 00 00 00 00 00 00 5a 5a 5a 5a 5a 5a 5a 5a ........ZZZZZZZZ
- Object   (0xc90f6d20): 31 30 31 39 2e 30 30 35                         1019.005
- Redzone  (0xc90f6d28): 00 cc cc cc                                     .
- Padding  (0xc90f6d50): 5a 5a 5a 5a 5a 5a 5a 5a                         ZZZZZZZZ
-
-   [<c010523d>] dump_trace+0x63/0x1eb
-   [<c01053df>] show_trace_log_lvl+0x1a/0x2f
-   [<c010601d>] show_trace+0x12/0x14
-   [<c0106035>] dump_stack+0x16/0x18
-   [<c017e0fa>] object_err+0x143/0x14b
-   [<c017e2cc>] check_object+0x66/0x234
-   [<c017eb43>] __slab_free+0x239/0x384
-   [<c017f446>] kfree+0xa6/0xc6
-   [<c02e2335>] get_modalias+0xb9/0xf5
-   [<c02e23b7>] dmi_dev_uevent+0x27/0x3c
-   [<c027866a>] dev_uevent+0x1ad/0x1da
-   [<c0205024>] kobject_uevent_env+0x20a/0x45b
-   [<c020527f>] kobject_uevent+0xa/0xf
-   [<c02779f1>] store_uevent+0x4f/0x58
-   [<c027758e>] dev_attr_store+0x29/0x2f
-   [<c01bec4f>] sysfs_write_file+0x16e/0x19c
-   [<c0183ba7>] vfs_write+0xd1/0x15a
-   [<c01841d7>] sys_write+0x3d/0x72
-   [<c0104112>] sysenter_past_esp+0x5f/0x99
-   [<b7f7b410>] 0xb7f7b410
-   =======================
-
- FIX kmalloc-8: Restoring Redzone 0xc90f6d28-0xc90f6d2b=0xcc
-
-If SLUB encounters a corrupted object (full detection requires the kernel
-to be booted with slub_debug) then the following output will be dumped
-into the syslog:
-
-1. Description of the problem encountered
-
-   This will be a message in the system log starting with::
-
-     ===============================================
-     BUG <slab cache affected>: <What went wrong>
-     -----------------------------------------------
-
-     INFO: <corruption start>-<corruption_end> <more info>
-     INFO: Slab <address> <slab information>
-     INFO: Object <address> <object information>
-     INFO: Allocated in <kernel function> age=<jiffies since alloc> cpu=<allocated by
-       cpu> pid=<pid of the process>
-     INFO: Freed in <kernel function> age=<jiffies since free> cpu=<freed by cpu>
-       pid=<pid of the process>
-
-   (Object allocation / free information is only available if SLAB_STORE_USER is
-   set for the slab. slub_debug sets that option)
-
-2. The object contents if an object was involved.
-
-   Various types of lines can follow the BUG SLUB line:
-
-   Bytes b4 <address> : <bytes>
-       Shows a few bytes before the object where the problem was detected.
-       Can be useful if the corruption does not stop with the start of the
-       object.
-
-   Object <address> : <bytes>
-       The bytes of the object. If the object is inactive then the bytes
-       typically contain poison values. Any non-poison value shows a
-       corruption by a write after free.
-
-   Redzone <address> : <bytes>
-       The Redzone following the object. The Redzone is used to detect
-       writes after the object. All bytes should always have the same
-       value. If there is any deviation then it is due to a write after
-       the object boundary.
-
-       (Redzone information is only available if SLAB_RED_ZONE is set.
-       slub_debug sets that option)
-
-   Padding <address> : <bytes>
-       Unused data to fill up the space in order to get the next object
-       properly aligned. In the debug case we make sure that there are
-       at least 4 bytes of padding. This allows the detection of writes
-       before the object.
-
-3. A stackdump
-
-   The stackdump describes the location where the error was detected. The cause
-   of the corruption is may be more likely found by looking at the function that
-   allocated or freed the object.
-
-4. Report on how the problem was dealt with in order to ensure the continued
-   operation of the system.
-
-   These are messages in the system log beginning with::
-
-       FIX <slab cache affected>: <corrective action taken>
-
-   In the above sample SLUB found that the Redzone of an active object has
-   been overwritten. Here a string of 8 characters was written into a slab that
-   has the length of 8 characters. However, a 8 character string needs a
-   terminating 0. That zero has overwritten the first byte of the Redzone field.
-   After reporting the details of the issue encountered the FIX SLUB message
-   tells us that SLUB has restored the Redzone to its proper value and then
-   system operations continue.
-
-Emergency operations
-====================
-
-Minimal debugging (sanity checks alone) can be enabled by booting with::
-
-       slub_debug=F
-
-This will be generally be enough to enable the resiliency features of slub
-which will keep the system running even if a bad kernel component will
-keep corrupting objects. This may be important for production systems.
-Performance will be impacted by the sanity checks and there will be a
-continual stream of error messages to the syslog but no additional memory
-will be used (unlike full debugging).
-
-No guarantees. The kernel component still needs to be fixed. Performance
-may be optimized further by locating the slab that experiences corruption
-and enabling debugging only for that cache
-
-I.e.::
-
-       slub_debug=F,dentry
-
-If the corruption occurs by writing after the end of the object then it
-may be advisable to enable a Redzone to avoid corrupting the beginning
-of other objects::
-
-       slub_debug=FZ,dentry
-
-Extended slabinfo mode and plotting
-===================================
-
-The ``slabinfo`` tool has a special 'extended' ('-X') mode that includes:
- - Slabcache Totals
- - Slabs sorted by size (up to -N <num> slabs, default 1)
- - Slabs sorted by loss (up to -N <num> slabs, default 1)
-
-Additionally, in this mode ``slabinfo`` does not dynamically scale
-sizes (G/M/K) and reports everything in bytes (this functionality is
-also available to other slabinfo modes via '-B' option) which makes
-reporting more precise and accurate. Moreover, in some sense the `-X'
-mode also simplifies the analysis of slabs' behaviour, because its
-output can be plotted using the ``slabinfo-gnuplot.sh`` script. So it
-pushes the analysis from looking through the numbers (tons of numbers)
-to something easier -- visual analysis.
-
-To generate plots:
-
-a) collect slabinfo extended records, for example::
-
-       while [ 1 ]; do slabinfo -X >> FOO_STATS; sleep 1; done
-
-b) pass stats file(-s) to ``slabinfo-gnuplot.sh`` script::
-
-       slabinfo-gnuplot.sh FOO_STATS [FOO_STATS2 .. FOO_STATSN]
-
-   The ``slabinfo-gnuplot.sh`` script will pre-processes the collected records
-   and generates 3 png files (and 3 pre-processing cache files) per STATS
-   file:
-   - Slabcache Totals: FOO_STATS-totals.png
-   - Slabs sorted by size: FOO_STATS-slabs-by-size.png
-   - Slabs sorted by loss: FOO_STATS-slabs-by-loss.png
-
-Another use case, when ``slabinfo-gnuplot.sh`` can be useful, is when you
-need to compare slabs' behaviour "prior to" and "after" some code
-modification.  To help you out there, ``slabinfo-gnuplot.sh`` script
-can 'merge' the `Slabcache Totals` sections from different
-measurements. To visually compare N plots:
-
-a) Collect as many STATS1, STATS2, .. STATSN files as you need::
-
-       while [ 1 ]; do slabinfo -X >> STATS<X>; sleep 1; done
-
-b) Pre-process those STATS files::
-
-       slabinfo-gnuplot.sh STATS1 STATS2 .. STATSN
-
-c) Execute ``slabinfo-gnuplot.sh`` in '-t' mode, passing all of the
-   generated pre-processed \*-totals::
-
-       slabinfo-gnuplot.sh -t STATS1-totals STATS2-totals .. STATSN-totals
-
-   This will produce a single plot (png file).
-
-   Plots, expectedly, can be large so some fluctuations or small spikes
-   can go unnoticed. To deal with that, ``slabinfo-gnuplot.sh`` has two
-   options to 'zoom-in'/'zoom-out':
-
-   a) ``-s %d,%d`` -- overwrites the default image width and height
-   b) ``-r %d,%d`` -- specifies a range of samples to use (for example,
-      in ``slabinfo -X >> FOO_STATS; sleep 1;`` case, using a ``-r
-      40,60`` range will plot only samples collected between 40th and
-      60th seconds).
-
-
-DebugFS files for SLUB
-======================
-
-For more information about current state of SLUB caches with the user tracking
-debug option enabled, debugfs files are available, typically under
-/sys/kernel/debug/slab/<cache>/ (created only for caches with enabled user
-tracking). There are 2 types of these files with the following debug
-information:
-
-1. alloc_traces::
-
-    Prints information about unique allocation traces of the currently
-    allocated objects. The output is sorted by frequency of each trace.
-
-    Information in the output:
-    Number of objects, allocating function, minimal/average/maximal jiffies since alloc,
-    pid range of the allocating processes, cpu mask of allocating cpus, and stack trace.
-
-    Example:::
-
-    1085 populate_error_injection_list+0x97/0x110 age=166678/166680/166682 pid=1 cpus=1::
-       __slab_alloc+0x6d/0x90
-       kmem_cache_alloc_trace+0x2eb/0x300
-       populate_error_injection_list+0x97/0x110
-       init_error_injection+0x1b/0x71
-       do_one_initcall+0x5f/0x2d0
-       kernel_init_freeable+0x26f/0x2d7
-       kernel_init+0xe/0x118
-       ret_from_fork+0x22/0x30
-
-
-2. free_traces::
-
-    Prints information about unique freeing traces of the currently allocated
-    objects. The freeing traces thus come from the previous life-cycle of the
-    objects and are reported as not available for objects allocated for the first
-    time. The output is sorted by frequency of each trace.
-
-    Information in the output:
-    Number of objects, freeing function, minimal/average/maximal jiffies since free,
-    pid range of the freeing processes, cpu mask of freeing cpus, and stack trace.
-
-    Example:::
-
-    1980 <not-available> age=4294912290 pid=0 cpus=0
-    51 acpi_ut_update_ref_count+0x6a6/0x782 age=236886/237027/237772 pid=1 cpus=1
-       kfree+0x2db/0x420
-       acpi_ut_update_ref_count+0x6a6/0x782
-       acpi_ut_update_object_reference+0x1ad/0x234
-       acpi_ut_remove_reference+0x7d/0x84
-       acpi_rs_get_prt_method_data+0x97/0xd6
-       acpi_get_irq_routing_table+0x82/0xc4
-       acpi_pci_irq_find_prt_entry+0x8e/0x2e0
-       acpi_pci_irq_lookup+0x3a/0x1e0
-       acpi_pci_irq_enable+0x77/0x240
-       pcibios_enable_device+0x39/0x40
-       do_pci_enable_device.part.0+0x5d/0xe0
-       pci_enable_device_flags+0xfc/0x120
-       pci_enable_device+0x13/0x20
-       virtio_pci_probe+0x9e/0x170
-       local_pci_probe+0x48/0x80
-       pci_device_probe+0x105/0x1c0
-
-Christoph Lameter, May 30, 2007
-Sergey Senozhatsky, October 23, 2015
diff --git a/Documentation/vm/split_page_table_lock.rst b/Documentation/vm/split_page_table_lock.rst
deleted file mode 100644 (file)
index c089196..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-.. _split_page_table_lock:
-
-=====================
-Split page table lock
-=====================
-
-Originally, mm->page_table_lock spinlock protected all page tables of the
-mm_struct. But this approach leads to poor page fault scalability of
-multi-threaded applications due high contention on the lock. To improve
-scalability, split page table lock was introduced.
-
-With split page table lock we have separate per-table lock to serialize
-access to the table. At the moment we use split lock for PTE and PMD
-tables. Access to higher level tables protected by mm->page_table_lock.
-
-There are helpers to lock/unlock a table and other accessor functions:
-
- - pte_offset_map_lock()
-       maps pte and takes PTE table lock, returns pointer to the taken
-       lock;
- - pte_unmap_unlock()
-       unlocks and unmaps PTE table;
- - pte_alloc_map_lock()
-       allocates PTE table if needed and take the lock, returns pointer
-       to taken lock or NULL if allocation failed;
- - pte_lockptr()
-       returns pointer to PTE table lock;
- - pmd_lock()
-       takes PMD table lock, returns pointer to taken lock;
- - pmd_lockptr()
-       returns pointer to PMD table lock;
-
-Split page table lock for PTE tables is enabled compile-time if
-CONFIG_SPLIT_PTLOCK_CPUS (usually 4) is less or equal to NR_CPUS.
-If split lock is disabled, all tables are guarded by mm->page_table_lock.
-
-Split page table lock for PMD tables is enabled, if it's enabled for PTE
-tables and the architecture supports it (see below).
-
-Hugetlb and split page table lock
-=================================
-
-Hugetlb can support several page sizes. We use split lock only for PMD
-level, but not for PUD.
-
-Hugetlb-specific helpers:
-
- - huge_pte_lock()
-       takes pmd split lock for PMD_SIZE page, mm->page_table_lock
-       otherwise;
- - huge_pte_lockptr()
-       returns pointer to table lock;
-
-Support of split page table lock by an architecture
-===================================================
-
-There's no need in special enabling of PTE split page table lock: everything
-required is done by pgtable_pte_page_ctor() and pgtable_pte_page_dtor(), which
-must be called on PTE table allocation / freeing.
-
-Make sure the architecture doesn't use slab allocator for page table
-allocation: slab uses page->slab_cache for its pages.
-This field shares storage with page->ptl.
-
-PMD split lock only makes sense if you have more than two page table
-levels.
-
-PMD split lock enabling requires pgtable_pmd_page_ctor() call on PMD table
-allocation and pgtable_pmd_page_dtor() on freeing.
-
-Allocation usually happens in pmd_alloc_one(), freeing in pmd_free() and
-pmd_free_tlb(), but make sure you cover all PMD table allocation / freeing
-paths: i.e X86_PAE preallocate few PMDs on pgd_alloc().
-
-With everything in place you can set CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK.
-
-NOTE: pgtable_pte_page_ctor() and pgtable_pmd_page_ctor() can fail -- it must
-be handled properly.
-
-page->ptl
-=========
-
-page->ptl is used to access split page table lock, where 'page' is struct
-page of page containing the table. It shares storage with page->private
-(and few other fields in union).
-
-To avoid increasing size of struct page and have best performance, we use a
-trick:
-
- - if spinlock_t fits into long, we use page->ptr as spinlock, so we
-   can avoid indirect access and save a cache line.
- - if size of spinlock_t is bigger then size of long, we use page->ptl as
-   pointer to spinlock_t and allocate it dynamically. This allows to use
-   split lock with enabled DEBUG_SPINLOCK or DEBUG_LOCK_ALLOC, but costs
-   one more cache line for indirect access;
-
-The spinlock_t allocated in pgtable_pte_page_ctor() for PTE table and in
-pgtable_pmd_page_ctor() for PMD table.
-
-Please, never access page->ptl directly -- use appropriate helper.
diff --git a/Documentation/vm/swap.rst b/Documentation/vm/swap.rst
deleted file mode 100644 (file)
index 78819bd..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-====
-Swap
-====
diff --git a/Documentation/vm/transhuge.rst b/Documentation/vm/transhuge.rst
deleted file mode 100644 (file)
index 216db1d..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-.. _transhuge:
-
-============================
-Transparent Hugepage Support
-============================
-
-This document describes design principles for Transparent Hugepage (THP)
-support and its interaction with other parts of the memory management
-system.
-
-Design principles
-=================
-
-- "graceful fallback": mm components which don't have transparent hugepage
-  knowledge fall back to breaking huge pmd mapping into table of ptes and,
-  if necessary, split a transparent hugepage. Therefore these components
-  can continue working on the regular pages or regular pte mappings.
-
-- if a hugepage allocation fails because of memory fragmentation,
-  regular pages should be gracefully allocated instead and mixed in
-  the same vma without any failure or significant delay and without
-  userland noticing
-
-- if some task quits and more hugepages become available (either
-  immediately in the buddy or through the VM), guest physical memory
-  backed by regular pages should be relocated on hugepages
-  automatically (with khugepaged)
-
-- it doesn't require memory reservation and in turn it uses hugepages
-  whenever possible (the only possible reservation here is kernelcore=
-  to avoid unmovable pages to fragment all the memory but such a tweak
-  is not specific to transparent hugepage support and it's a generic
-  feature that applies to all dynamic high order allocations in the
-  kernel)
-
-get_user_pages and follow_page
-==============================
-
-get_user_pages and follow_page if run on a hugepage, will return the
-head or tail pages as usual (exactly as they would do on
-hugetlbfs). Most GUP users will only care about the actual physical
-address of the page and its temporary pinning to release after the I/O
-is complete, so they won't ever notice the fact the page is huge. But
-if any driver is going to mangle over the page structure of the tail
-page (like for checking page->mapping or other bits that are relevant
-for the head page and not the tail page), it should be updated to jump
-to check head page instead. Taking a reference on any head/tail page would
-prevent the page from being split by anyone.
-
-.. note::
-   these aren't new constraints to the GUP API, and they match the
-   same constraints that apply to hugetlbfs too, so any driver capable
-   of handling GUP on hugetlbfs will also work fine on transparent
-   hugepage backed mappings.
-
-Graceful fallback
-=================
-
-Code walking pagetables but unaware about huge pmds can simply call
-split_huge_pmd(vma, pmd, addr) where the pmd is the one returned by
-pmd_offset. It's trivial to make the code transparent hugepage aware
-by just grepping for "pmd_offset" and adding split_huge_pmd where
-missing after pmd_offset returns the pmd. Thanks to the graceful
-fallback design, with a one liner change, you can avoid to write
-hundreds if not thousands of lines of complex code to make your code
-hugepage aware.
-
-If you're not walking pagetables but you run into a physical hugepage
-that you can't handle natively in your code, you can split it by
-calling split_huge_page(page). This is what the Linux VM does before
-it tries to swapout the hugepage for example. split_huge_page() can fail
-if the page is pinned and you must handle this correctly.
-
-Example to make mremap.c transparent hugepage aware with a one liner
-change::
-
-       diff --git a/mm/mremap.c b/mm/mremap.c
-       --- a/mm/mremap.c
-       +++ b/mm/mremap.c
-       @@ -41,6 +41,7 @@ static pmd_t *get_old_pmd(struct mm_stru
-                       return NULL;
-
-               pmd = pmd_offset(pud, addr);
-       +       split_huge_pmd(vma, pmd, addr);
-               if (pmd_none_or_clear_bad(pmd))
-                       return NULL;
-
-Locking in hugepage aware code
-==============================
-
-We want as much code as possible hugepage aware, as calling
-split_huge_page() or split_huge_pmd() has a cost.
-
-To make pagetable walks huge pmd aware, all you need to do is to call
-pmd_trans_huge() on the pmd returned by pmd_offset. You must hold the
-mmap_lock in read (or write) mode to be sure a huge pmd cannot be
-created from under you by khugepaged (khugepaged collapse_huge_page
-takes the mmap_lock in write mode in addition to the anon_vma lock). If
-pmd_trans_huge returns false, you just fallback in the old code
-paths. If instead pmd_trans_huge returns true, you have to take the
-page table lock (pmd_lock()) and re-run pmd_trans_huge. Taking the
-page table lock will prevent the huge pmd being converted into a
-regular pmd from under you (split_huge_pmd can run in parallel to the
-pagetable walk). If the second pmd_trans_huge returns false, you
-should just drop the page table lock and fallback to the old code as
-before. Otherwise, you can proceed to process the huge pmd and the
-hugepage natively. Once finished, you can drop the page table lock.
-
-Refcounts and transparent huge pages
-====================================
-
-Refcounting on THP is mostly consistent with refcounting on other compound
-pages:
-
-  - get_page()/put_page() and GUP operate on head page's ->_refcount.
-
-  - ->_refcount in tail pages is always zero: get_page_unless_zero() never
-    succeeds on tail pages.
-
-  - map/unmap of the pages with PTE entry increment/decrement ->_mapcount
-    on relevant sub-page of the compound page.
-
-  - map/unmap of the whole compound page is accounted for in compound_mapcount
-    (stored in first tail page). For file huge pages, we also increment
-    ->_mapcount of all sub-pages in order to have race-free detection of
-    last unmap of subpages.
-
-PageDoubleMap() indicates that the page is *possibly* mapped with PTEs.
-
-For anonymous pages, PageDoubleMap() also indicates ->_mapcount in all
-subpages is offset up by one. This additional reference is required to
-get race-free detection of unmap of subpages when we have them mapped with
-both PMDs and PTEs.
-
-This optimization is required to lower the overhead of per-subpage mapcount
-tracking. The alternative is to alter ->_mapcount in all subpages on each
-map/unmap of the whole compound page.
-
-For anonymous pages, we set PG_double_map when a PMD of the page is split
-for the first time, but still have a PMD mapping. The additional references
-go away with the last compound_mapcount.
-
-File pages get PG_double_map set on the first map of the page with PTE and
-goes away when the page gets evicted from the page cache.
-
-split_huge_page internally has to distribute the refcounts in the head
-page to the tail pages before clearing all PG_head/tail bits from the page
-structures. It can be done easily for refcounts taken by page table
-entries, but we don't have enough information on how to distribute any
-additional pins (i.e. from get_user_pages). split_huge_page() fails any
-requests to split pinned huge pages: it expects page count to be equal to
-the sum of mapcount of all sub-pages plus one (split_huge_page caller must
-have a reference to the head page).
-
-split_huge_page uses migration entries to stabilize page->_refcount and
-page->_mapcount of anonymous pages. File pages just get unmapped.
-
-We are safe against physical memory scanners too: the only legitimate way
-a scanner can get a reference to a page is get_page_unless_zero().
-
-All tail pages have zero ->_refcount until atomic_add(). This prevents the
-scanner from getting a reference to the tail page up to that point. After the
-atomic_add() we don't care about the ->_refcount value. We already know how
-many references should be uncharged from the head page.
-
-For head page get_page_unless_zero() will succeed and we don't mind. It's
-clear where references should go after split: it will stay on the head page.
-
-Note that split_huge_pmd() doesn't have any limitations on refcounting:
-pmd can be split at any point and never fails.
-
-Partial unmap and deferred_split_huge_page()
-============================================
-
-Unmapping part of THP (with munmap() or other way) is not going to free
-memory immediately. Instead, we detect that a subpage of THP is not in use
-in page_remove_rmap() and queue the THP for splitting if memory pressure
-comes. Splitting will free up unused subpages.
-
-Splitting the page right away is not an option due to locking context in
-the place where we can detect partial unmap. It also might be
-counterproductive since in many cases partial unmap happens during exit(2) if
-a THP crosses a VMA boundary.
-
-The function deferred_split_huge_page() is used to queue a page for splitting.
-The splitting itself will happen when we get memory pressure via shrinker
-interface.
diff --git a/Documentation/vm/unevictable-lru.rst b/Documentation/vm/unevictable-lru.rst
deleted file mode 100644 (file)
index b280367..0000000
+++ /dev/null
@@ -1,554 +0,0 @@
-.. _unevictable_lru:
-
-==============================
-Unevictable LRU Infrastructure
-==============================
-
-.. contents:: :local:
-
-
-Introduction
-============
-
-This document describes the Linux memory manager's "Unevictable LRU"
-infrastructure and the use of this to manage several types of "unevictable"
-pages.
-
-The document attempts to provide the overall rationale behind this mechanism
-and the rationale for some of the design decisions that drove the
-implementation.  The latter design rationale is discussed in the context of an
-implementation description.  Admittedly, one can obtain the implementation
-details - the "what does it do?" - by reading the code.  One hopes that the
-descriptions below add value by provide the answer to "why does it do that?".
-
-
-
-The Unevictable LRU
-===================
-
-The Unevictable LRU facility adds an additional LRU list to track unevictable
-pages and to hide these pages from vmscan.  This mechanism is based on a patch
-by Larry Woodman of Red Hat to address several scalability problems with page
-reclaim in Linux.  The problems have been observed at customer sites on large
-memory x86_64 systems.
-
-To illustrate this with an example, a non-NUMA x86_64 platform with 128GB of
-main memory will have over 32 million 4k pages in a single node.  When a large
-fraction of these pages are not evictable for any reason [see below], vmscan
-will spend a lot of time scanning the LRU lists looking for the small fraction
-of pages that are evictable.  This can result in a situation where all CPUs are
-spending 100% of their time in vmscan for hours or days on end, with the system
-completely unresponsive.
-
-The unevictable list addresses the following classes of unevictable pages:
-
- * Those owned by ramfs.
-
- * Those mapped into SHM_LOCK'd shared memory regions.
-
- * Those mapped into VM_LOCKED [mlock()ed] VMAs.
-
-The infrastructure may also be able to handle other conditions that make pages
-unevictable, either by definition or by circumstance, in the future.
-
-
-The Unevictable LRU Page List
------------------------------
-
-The Unevictable LRU page list is a lie.  It was never an LRU-ordered list, but a
-companion to the LRU-ordered anonymous and file, active and inactive page lists;
-and now it is not even a page list.  But following familiar convention, here in
-this document and in the source, we often imagine it as a fifth LRU page list.
-
-The Unevictable LRU infrastructure consists of an additional, per-node, LRU list
-called the "unevictable" list and an associated page flag, PG_unevictable, to
-indicate that the page is being managed on the unevictable list.
-
-The PG_unevictable flag is analogous to, and mutually exclusive with, the
-PG_active flag in that it indicates on which LRU list a page resides when
-PG_lru is set.
-
-The Unevictable LRU infrastructure maintains unevictable pages as if they were
-on an additional LRU list for a few reasons:
-
- (1) We get to "treat unevictable pages just like we treat other pages in the
-     system - which means we get to use the same code to manipulate them, the
-     same code to isolate them (for migrate, etc.), the same code to keep track
-     of the statistics, etc..." [Rik van Riel]
-
- (2) We want to be able to migrate unevictable pages between nodes for memory
-     defragmentation, workload management and memory hotplug.  The Linux kernel
-     can only migrate pages that it can successfully isolate from the LRU
-     lists (or "Movable" pages: outside of consideration here).  If we were to
-     maintain pages elsewhere than on an LRU-like list, where they can be
-     detected by isolate_lru_page(), we would prevent their migration.
-
-The unevictable list does not differentiate between file-backed and anonymous,
-swap-backed pages.  This differentiation is only important while the pages are,
-in fact, evictable.
-
-The unevictable list benefits from the "arrayification" of the per-node LRU
-lists and statistics originally proposed and posted by Christoph Lameter.
-
-
-Memory Control Group Interaction
---------------------------------
-
-The unevictable LRU facility interacts with the memory control group [aka
-memory controller; see Documentation/admin-guide/cgroup-v1/memory.rst] by
-extending the lru_list enum.
-
-The memory controller data structure automatically gets a per-node unevictable
-list as a result of the "arrayification" of the per-node LRU lists (one per
-lru_list enum element).  The memory controller tracks the movement of pages to
-and from the unevictable list.
-
-When a memory control group comes under memory pressure, the controller will
-not attempt to reclaim pages on the unevictable list.  This has a couple of
-effects:
-
- (1) Because the pages are "hidden" from reclaim on the unevictable list, the
-     reclaim process can be more efficient, dealing only with pages that have a
-     chance of being reclaimed.
-
- (2) On the other hand, if too many of the pages charged to the control group
-     are unevictable, the evictable portion of the working set of the tasks in
-     the control group may not fit into the available memory.  This can cause
-     the control group to thrash or to OOM-kill tasks.
-
-
-.. _mark_addr_space_unevict:
-
-Marking Address Spaces Unevictable
-----------------------------------
-
-For facilities such as ramfs none of the pages attached to the address space
-may be evicted.  To prevent eviction of any such pages, the AS_UNEVICTABLE
-address space flag is provided, and this can be manipulated by a filesystem
-using a number of wrapper functions:
-
- * ``void mapping_set_unevictable(struct address_space *mapping);``
-
-       Mark the address space as being completely unevictable.
-
- * ``void mapping_clear_unevictable(struct address_space *mapping);``
-
-       Mark the address space as being evictable.
-
- * ``int mapping_unevictable(struct address_space *mapping);``
-
-       Query the address space, and return true if it is completely
-       unevictable.
-
-These are currently used in three places in the kernel:
-
- (1) By ramfs to mark the address spaces of its inodes when they are created,
-     and this mark remains for the life of the inode.
-
- (2) By SYSV SHM to mark SHM_LOCK'd address spaces until SHM_UNLOCK is called.
-     Note that SHM_LOCK is not required to page in the locked pages if they're
-     swapped out; the application must touch the pages manually if it wants to
-     ensure they're in memory.
-
- (3) By the i915 driver to mark pinned address space until it's unpinned. The
-     amount of unevictable memory marked by i915 driver is roughly the bounded
-     object size in debugfs/dri/0/i915_gem_objects.
-
-
-Detecting Unevictable Pages
----------------------------
-
-The function page_evictable() in mm/internal.h determines whether a page is
-evictable or not using the query function outlined above [see section
-:ref:`Marking address spaces unevictable <mark_addr_space_unevict>`]
-to check the AS_UNEVICTABLE flag.
-
-For address spaces that are so marked after being populated (as SHM regions
-might be), the lock action (e.g. SHM_LOCK) can be lazy, and need not populate
-the page tables for the region as does, for example, mlock(), nor need it make
-any special effort to push any pages in the SHM_LOCK'd area to the unevictable
-list.  Instead, vmscan will do this if and when it encounters the pages during
-a reclamation scan.
-
-On an unlock action (such as SHM_UNLOCK), the unlocker (e.g. shmctl()) must scan
-the pages in the region and "rescue" them from the unevictable list if no other
-condition is keeping them unevictable.  If an unevictable region is destroyed,
-the pages are also "rescued" from the unevictable list in the process of
-freeing them.
-
-page_evictable() also checks for mlocked pages by testing an additional page
-flag, PG_mlocked (as wrapped by PageMlocked()), which is set when a page is
-faulted into a VM_LOCKED VMA, or found in a VMA being VM_LOCKED.
-
-
-Vmscan's Handling of Unevictable Pages
---------------------------------------
-
-If unevictable pages are culled in the fault path, or moved to the unevictable
-list at mlock() or mmap() time, vmscan will not encounter the pages until they
-have become evictable again (via munlock() for example) and have been "rescued"
-from the unevictable list.  However, there may be situations where we decide,
-for the sake of expediency, to leave an unevictable page on one of the regular
-active/inactive LRU lists for vmscan to deal with.  vmscan checks for such
-pages in all of the shrink_{active|inactive|page}_list() functions and will
-"cull" such pages that it encounters: that is, it diverts those pages to the
-unevictable list for the memory cgroup and node being scanned.
-
-There may be situations where a page is mapped into a VM_LOCKED VMA, but the
-page is not marked as PG_mlocked.  Such pages will make it all the way to
-shrink_active_list() or shrink_page_list() where they will be detected when
-vmscan walks the reverse map in page_referenced() or try_to_unmap().  The page
-is culled to the unevictable list when it is released by the shrinker.
-
-To "cull" an unevictable page, vmscan simply puts the page back on the LRU list
-using putback_lru_page() - the inverse operation to isolate_lru_page() - after
-dropping the page lock.  Because the condition which makes the page unevictable
-may change once the page is unlocked, __pagevec_lru_add_fn() will recheck the
-unevictable state of a page before placing it on the unevictable list.
-
-
-MLOCKED Pages
-=============
-
-The unevictable page list is also useful for mlock(), in addition to ramfs and
-SYSV SHM.  Note that mlock() is only available in CONFIG_MMU=y situations; in
-NOMMU situations, all mappings are effectively mlocked.
-
-
-History
--------
-
-The "Unevictable mlocked Pages" infrastructure is based on work originally
-posted by Nick Piggin in an RFC patch entitled "mm: mlocked pages off LRU".
-Nick posted his patch as an alternative to a patch posted by Christoph Lameter
-to achieve the same objective: hiding mlocked pages from vmscan.
-
-In Nick's patch, he used one of the struct page LRU list link fields as a count
-of VM_LOCKED VMAs that map the page (Rik van Riel had the same idea three years
-earlier).  But this use of the link field for a count prevented the management
-of the pages on an LRU list, and thus mlocked pages were not migratable as
-isolate_lru_page() could not detect them, and the LRU list link field was not
-available to the migration subsystem.
-
-Nick resolved this by putting mlocked pages back on the LRU list before
-attempting to isolate them, thus abandoning the count of VM_LOCKED VMAs.  When
-Nick's patch was integrated with the Unevictable LRU work, the count was
-replaced by walking the reverse map when munlocking, to determine whether any
-other VM_LOCKED VMAs still mapped the page.
-
-However, walking the reverse map for each page when munlocking was ugly and
-inefficient, and could lead to catastrophic contention on a file's rmap lock,
-when many processes which had it mlocked were trying to exit.  In 5.18, the
-idea of keeping mlock_count in Unevictable LRU list link field was revived and
-put to work, without preventing the migration of mlocked pages.  This is why
-the "Unevictable LRU list" cannot be a linked list of pages now; but there was
-no use for that linked list anyway - though its size is maintained for meminfo.
-
-
-Basic Management
-----------------
-
-mlocked pages - pages mapped into a VM_LOCKED VMA - are a class of unevictable
-pages.  When such a page has been "noticed" by the memory management subsystem,
-the page is marked with the PG_mlocked flag.  This can be manipulated using the
-PageMlocked() functions.
-
-A PG_mlocked page will be placed on the unevictable list when it is added to
-the LRU.  Such pages can be "noticed" by memory management in several places:
-
- (1) in the mlock()/mlock2()/mlockall() system call handlers;
-
- (2) in the mmap() system call handler when mmapping a region with the
-     MAP_LOCKED flag;
-
- (3) mmapping a region in a task that has called mlockall() with the MCL_FUTURE
-     flag;
-
- (4) in the fault path and when a VM_LOCKED stack segment is expanded; or
-
- (5) as mentioned above, in vmscan:shrink_page_list() when attempting to
-     reclaim a page in a VM_LOCKED VMA by page_referenced() or try_to_unmap().
-
-mlocked pages become unlocked and rescued from the unevictable list when:
-
- (1) mapped in a range unlocked via the munlock()/munlockall() system calls;
-
- (2) munmap()'d out of the last VM_LOCKED VMA that maps the page, including
-     unmapping at task exit;
-
- (3) when the page is truncated from the last VM_LOCKED VMA of an mmapped file;
-     or
-
- (4) before a page is COW'd in a VM_LOCKED VMA.
-
-
-mlock()/mlock2()/mlockall() System Call Handling
-------------------------------------------------
-
-mlock(), mlock2() and mlockall() system call handlers proceed to mlock_fixup()
-for each VMA in the range specified by the call.  In the case of mlockall(),
-this is the entire active address space of the task.  Note that mlock_fixup()
-is used for both mlocking and munlocking a range of memory.  A call to mlock()
-an already VM_LOCKED VMA, or to munlock() a VMA that is not VM_LOCKED, is
-treated as a no-op and mlock_fixup() simply returns.
-
-If the VMA passes some filtering as described in "Filtering Special VMAs"
-below, mlock_fixup() will attempt to merge the VMA with its neighbors or split
-off a subset of the VMA if the range does not cover the entire VMA.  Any pages
-already present in the VMA are then marked as mlocked by mlock_page() via
-mlock_pte_range() via walk_page_range() via mlock_vma_pages_range().
-
-Before returning from the system call, do_mlock() or mlockall() will call
-__mm_populate() to fault in the remaining pages via get_user_pages() and to
-mark those pages as mlocked as they are faulted.
-
-Note that the VMA being mlocked might be mapped with PROT_NONE.  In this case,
-get_user_pages() will be unable to fault in the pages.  That's okay.  If pages
-do end up getting faulted into this VM_LOCKED VMA, they will be handled in the
-fault path - which is also how mlock2()'s MLOCK_ONFAULT areas are handled.
-
-For each PTE (or PMD) being faulted into a VMA, the page add rmap function
-calls mlock_vma_page(), which calls mlock_page() when the VMA is VM_LOCKED
-(unless it is a PTE mapping of a part of a transparent huge page).  Or when
-it is a newly allocated anonymous page, lru_cache_add_inactive_or_unevictable()
-calls mlock_new_page() instead: similar to mlock_page(), but can make better
-judgments, since this page is held exclusively and known not to be on LRU yet.
-
-mlock_page() sets PageMlocked immediately, then places the page on the CPU's
-mlock pagevec, to batch up the rest of the work to be done under lru_lock by
-__mlock_page().  __mlock_page() sets PageUnevictable, initializes mlock_count
-and moves the page to unevictable state ("the unevictable LRU", but with
-mlock_count in place of LRU threading).  Or if the page was already PageLRU
-and PageUnevictable and PageMlocked, it simply increments the mlock_count.
-
-But in practice that may not work ideally: the page may not yet be on an LRU, or
-it may have been temporarily isolated from LRU.  In such cases the mlock_count
-field cannot be touched, but will be set to 0 later when __pagevec_lru_add_fn()
-returns the page to "LRU".  Races prohibit mlock_count from being set to 1 then:
-rather than risk stranding a page indefinitely as unevictable, always err with
-mlock_count on the low side, so that when munlocked the page will be rescued to
-an evictable LRU, then perhaps be mlocked again later if vmscan finds it in a
-VM_LOCKED VMA.
-
-
-Filtering Special VMAs
-----------------------
-
-mlock_fixup() filters several classes of "special" VMAs:
-
-1) VMAs with VM_IO or VM_PFNMAP set are skipped entirely.  The pages behind
-   these mappings are inherently pinned, so we don't need to mark them as
-   mlocked.  In any case, most of the pages have no struct page in which to so
-   mark the page.  Because of this, get_user_pages() will fail for these VMAs,
-   so there is no sense in attempting to visit them.
-
-2) VMAs mapping hugetlbfs page are already effectively pinned into memory.  We
-   neither need nor want to mlock() these pages.  But __mm_populate() includes
-   hugetlbfs ranges, allocating the huge pages and populating the PTEs.
-
-3) VMAs with VM_DONTEXPAND are generally userspace mappings of kernel pages,
-   such as the VDSO page, relay channel pages, etc.  These pages are inherently
-   unevictable and are not managed on the LRU lists.  __mm_populate() includes
-   these ranges, populating the PTEs if not already populated.
-
-4) VMAs with VM_MIXEDMAP set are not marked VM_LOCKED, but __mm_populate()
-   includes these ranges, populating the PTEs if not already populated.
-
-Note that for all of these special VMAs, mlock_fixup() does not set the
-VM_LOCKED flag.  Therefore, we won't have to deal with them later during
-munlock(), munmap() or task exit.  Neither does mlock_fixup() account these
-VMAs against the task's "locked_vm".
-
-
-munlock()/munlockall() System Call Handling
--------------------------------------------
-
-The munlock() and munlockall() system calls are handled by the same
-mlock_fixup() function as mlock(), mlock2() and mlockall() system calls are.
-If called to munlock an already munlocked VMA, mlock_fixup() simply returns.
-Because of the VMA filtering discussed above, VM_LOCKED will not be set in
-any "special" VMAs.  So, those VMAs will be ignored for munlock.
-
-If the VMA is VM_LOCKED, mlock_fixup() again attempts to merge or split off the
-specified range.  All pages in the VMA are then munlocked by munlock_page() via
-mlock_pte_range() via walk_page_range() via mlock_vma_pages_range() - the same
-function used when mlocking a VMA range, with new flags for the VMA indicating
-that it is munlock() being performed.
-
-munlock_page() uses the mlock pagevec to batch up work to be done under
-lru_lock by  __munlock_page().  __munlock_page() decrements the page's
-mlock_count, and when that reaches 0 it clears PageMlocked and clears
-PageUnevictable, moving the page from unevictable state to inactive LRU.
-
-But in practice that may not work ideally: the page may not yet have reached
-"the unevictable LRU", or it may have been temporarily isolated from it.  In
-those cases its mlock_count field is unusable and must be assumed to be 0: so
-that the page will be rescued to an evictable LRU, then perhaps be mlocked
-again later if vmscan finds it in a VM_LOCKED VMA.
-
-
-Migrating MLOCKED Pages
------------------------
-
-A page that is being migrated has been isolated from the LRU lists and is held
-locked across unmapping of the page, updating the page's address space entry
-and copying the contents and state, until the page table entry has been
-replaced with an entry that refers to the new page.  Linux supports migration
-of mlocked pages and other unevictable pages.  PG_mlocked is cleared from the
-the old page when it is unmapped from the last VM_LOCKED VMA, and set when the
-new page is mapped in place of migration entry in a VM_LOCKED VMA.  If the page
-was unevictable because mlocked, PG_unevictable follows PG_mlocked; but if the
-page was unevictable for other reasons, PG_unevictable is copied explicitly.
-
-Note that page migration can race with mlocking or munlocking of the same page.
-There is mostly no problem since page migration requires unmapping all PTEs of
-the old page (including munlock where VM_LOCKED), then mapping in the new page
-(including mlock where VM_LOCKED).  The page table locks provide sufficient
-synchronization.
-
-However, since mlock_vma_pages_range() starts by setting VM_LOCKED on a VMA,
-before mlocking any pages already present, if one of those pages were migrated
-before mlock_pte_range() reached it, it would get counted twice in mlock_count.
-To prevent that, mlock_vma_pages_range() temporarily marks the VMA as VM_IO,
-so that mlock_vma_page() will skip it.
-
-To complete page migration, we place the old and new pages back onto the LRU
-afterwards.  The "unneeded" page - old page on success, new page on failure -
-is freed when the reference count held by the migration process is released.
-
-
-Compacting MLOCKED Pages
-------------------------
-
-The memory map can be scanned for compactable regions and the default behavior
-is to let unevictable pages be moved.  /proc/sys/vm/compact_unevictable_allowed
-controls this behavior (see Documentation/admin-guide/sysctl/vm.rst).  The work
-of compaction is mostly handled by the page migration code and the same work
-flow as described in Migrating MLOCKED Pages will apply.
-
-
-MLOCKING Transparent Huge Pages
--------------------------------
-
-A transparent huge page is represented by a single entry on an LRU list.
-Therefore, we can only make unevictable an entire compound page, not
-individual subpages.
-
-If a user tries to mlock() part of a huge page, and no user mlock()s the
-whole of the huge page, we want the rest of the page to be reclaimable.
-
-We cannot just split the page on partial mlock() as split_huge_page() can
-fail and a new intermittent failure mode for the syscall is undesirable.
-
-We handle this by keeping PTE-mlocked huge pages on evictable LRU lists:
-the PMD on the border of a VM_LOCKED VMA will be split into a PTE table.
-
-This way the huge page is accessible for vmscan.  Under memory pressure the
-page will be split, subpages which belong to VM_LOCKED VMAs will be moved
-to the unevictable LRU and the rest can be reclaimed.
-
-/proc/meminfo's Unevictable and Mlocked amounts do not include those parts
-of a transparent huge page which are mapped only by PTEs in VM_LOCKED VMAs.
-
-
-mmap(MAP_LOCKED) System Call Handling
--------------------------------------
-
-In addition to the mlock(), mlock2() and mlockall() system calls, an application
-can request that a region of memory be mlocked by supplying the MAP_LOCKED flag
-to the mmap() call.  There is one important and subtle difference here, though.
-mmap() + mlock() will fail if the range cannot be faulted in (e.g. because
-mm_populate fails) and returns with ENOMEM while mmap(MAP_LOCKED) will not fail.
-The mmaped area will still have properties of the locked area - pages will not
-get swapped out - but major page faults to fault memory in might still happen.
-
-Furthermore, any mmap() call or brk() call that expands the heap by a task
-that has previously called mlockall() with the MCL_FUTURE flag will result
-in the newly mapped memory being mlocked.  Before the unevictable/mlock
-changes, the kernel simply called make_pages_present() to allocate pages
-and populate the page table.
-
-To mlock a range of memory under the unevictable/mlock infrastructure,
-the mmap() handler and task address space expansion functions call
-populate_vma_page_range() specifying the vma and the address range to mlock.
-
-
-munmap()/exit()/exec() System Call Handling
--------------------------------------------
-
-When unmapping an mlocked region of memory, whether by an explicit call to
-munmap() or via an internal unmap from exit() or exec() processing, we must
-munlock the pages if we're removing the last VM_LOCKED VMA that maps the pages.
-Before the unevictable/mlock changes, mlocking did not mark the pages in any
-way, so unmapping them required no processing.
-
-For each PTE (or PMD) being unmapped from a VMA, page_remove_rmap() calls
-munlock_vma_page(), which calls munlock_page() when the VMA is VM_LOCKED
-(unless it was a PTE mapping of a part of a transparent huge page).
-
-munlock_page() uses the mlock pagevec to batch up work to be done under
-lru_lock by  __munlock_page().  __munlock_page() decrements the page's
-mlock_count, and when that reaches 0 it clears PageMlocked and clears
-PageUnevictable, moving the page from unevictable state to inactive LRU.
-
-But in practice that may not work ideally: the page may not yet have reached
-"the unevictable LRU", or it may have been temporarily isolated from it.  In
-those cases its mlock_count field is unusable and must be assumed to be 0: so
-that the page will be rescued to an evictable LRU, then perhaps be mlocked
-again later if vmscan finds it in a VM_LOCKED VMA.
-
-
-Truncating MLOCKED Pages
-------------------------
-
-File truncation or hole punching forcibly unmaps the deleted pages from
-userspace; truncation even unmaps and deletes any private anonymous pages
-which had been Copied-On-Write from the file pages now being truncated.
-
-Mlocked pages can be munlocked and deleted in this way: like with munmap(),
-for each PTE (or PMD) being unmapped from a VMA, page_remove_rmap() calls
-munlock_vma_page(), which calls munlock_page() when the VMA is VM_LOCKED
-(unless it was a PTE mapping of a part of a transparent huge page).
-
-However, if there is a racing munlock(), since mlock_vma_pages_range() starts
-munlocking by clearing VM_LOCKED from a VMA, before munlocking all the pages
-present, if one of those pages were unmapped by truncation or hole punch before
-mlock_pte_range() reached it, it would not be recognized as mlocked by this VMA,
-and would not be counted out of mlock_count.  In this rare case, a page may
-still appear as PageMlocked after it has been fully unmapped: and it is left to
-release_pages() (or __page_cache_release()) to clear it and update statistics
-before freeing (this event is counted in /proc/vmstat unevictable_pgs_cleared,
-which is usually 0).
-
-
-Page Reclaim in shrink_*_list()
--------------------------------
-
-vmscan's shrink_active_list() culls any obviously unevictable pages -
-i.e. !page_evictable(page) pages - diverting those to the unevictable list.
-However, shrink_active_list() only sees unevictable pages that made it onto the
-active/inactive LRU lists.  Note that these pages do not have PageUnevictable
-set - otherwise they would be on the unevictable list and shrink_active_list()
-would never see them.
-
-Some examples of these unevictable pages on the LRU lists are:
-
- (1) ramfs pages that have been placed on the LRU lists when first allocated.
-
- (2) SHM_LOCK'd shared memory pages.  shmctl(SHM_LOCK) does not attempt to
-     allocate or fault in the pages in the shared memory region.  This happens
-     when an application accesses the page the first time after SHM_LOCK'ing
-     the segment.
-
- (3) pages still mapped into VM_LOCKED VMAs, which should be marked mlocked,
-     but events left mlock_count too low, so they were munlocked too early.
-
-vmscan's shrink_inactive_list() and shrink_page_list() also divert obviously
-unevictable pages found on the inactive lists to the appropriate memory cgroup
-and node unevictable list.
-
-rmap's page_referenced_one(), called via vmscan's shrink_active_list() or
-shrink_page_list(), and rmap's try_to_unmap_one() called via shrink_page_list(),
-check for (3) pages still mapped into VM_LOCKED VMAs, and call mlock_vma_page()
-to correct them.  Such pages are culled to the unevictable list when released
-by the shrinker.
diff --git a/Documentation/vm/vmalloc.rst b/Documentation/vm/vmalloc.rst
deleted file mode 100644 (file)
index 363fe20..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-======================================
-Virtually Contiguous Memory Allocation
-======================================
diff --git a/Documentation/vm/vmalloced-kernel-stacks.rst b/Documentation/vm/vmalloced-kernel-stacks.rst
deleted file mode 100644 (file)
index fc8c678..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-=====================================
-Virtually Mapped Kernel Stack Support
-=====================================
-
-:Author: Shuah Khan <skhan@linuxfoundation.org>
-
-.. contents:: :local:
-
-Overview
---------
-
-This is a compilation of information from the code and original patch
-series that introduced the `Virtually Mapped Kernel Stacks feature
-<https://lwn.net/Articles/694348/>`
-
-Introduction
-------------
-
-Kernel stack overflows are often hard to debug and make the kernel
-susceptible to exploits. Problems could show up at a later time making
-it difficult to isolate and root-cause.
-
-Virtually-mapped kernel stacks with guard pages causes kernel stack
-overflows to be caught immediately rather than causing difficult to
-diagnose corruptions.
-
-HAVE_ARCH_VMAP_STACK and VMAP_STACK configuration options enable
-support for virtually mapped stacks with guard pages. This feature
-causes reliable faults when the stack overflows. The usability of
-the stack trace after overflow and response to the overflow itself
-is architecture dependent.
-
-.. note::
-        As of this writing, arm64, powerpc, riscv, s390, um, and x86 have
-        support for VMAP_STACK.
-
-HAVE_ARCH_VMAP_STACK
---------------------
-
-Architectures that can support Virtually Mapped Kernel Stacks should
-enable this bool configuration option. The requirements are:
-
-- vmalloc space must be large enough to hold many kernel stacks. This
-  may rule out many 32-bit architectures.
-- Stacks in vmalloc space need to work reliably.  For example, if
-  vmap page tables are created on demand, either this mechanism
-  needs to work while the stack points to a virtual address with
-  unpopulated page tables or arch code (switch_to() and switch_mm(),
-  most likely) needs to ensure that the stack's page table entries
-  are populated before running on a possibly unpopulated stack.
-- If the stack overflows into a guard page, something reasonable
-  should happen. The definition of "reasonable" is flexible, but
-  instantly rebooting without logging anything would be unfriendly.
-
-VMAP_STACK
-----------
-
-VMAP_STACK bool configuration option when enabled allocates virtually
-mapped task stacks. This option depends on HAVE_ARCH_VMAP_STACK.
-
-- Enable this if you want the use virtually-mapped kernel stacks
-  with guard pages. This causes kernel stack overflows to be caught
-  immediately rather than causing difficult-to-diagnose corruption.
-
-.. note::
-
-        Using this feature with KASAN requires architecture support
-        for backing virtual mappings with real shadow memory, and
-        KASAN_VMALLOC must be enabled.
-
-.. note::
-
-        VMAP_STACK is enabled, it is not possible to run DMA on stack
-        allocated data.
-
-Kernel configuration options and dependencies keep changing. Refer to
-the latest code base:
-
-`Kconfig <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/Kconfig>`
-
-Allocation
------------
-
-When a new kernel thread is created, thread stack is allocated from
-virtually contiguous memory pages from the page level allocator. These
-pages are mapped into contiguous kernel virtual space with PAGE_KERNEL
-protections.
-
-alloc_thread_stack_node() calls __vmalloc_node_range() to allocate stack
-with PAGE_KERNEL protections.
-
-- Allocated stacks are cached and later reused by new threads, so memcg
-  accounting is performed manually on assigning/releasing stacks to tasks.
-  Hence, __vmalloc_node_range is called without __GFP_ACCOUNT.
-- vm_struct is cached to be able to find when thread free is initiated
-  in interrupt context. free_thread_stack() can be called in interrupt
-  context.
-- On arm64, all VMAP's stacks need to have the same alignment to ensure
-  that VMAP'd stack overflow detection works correctly. Arch specific
-  vmap stack allocator takes care of this detail.
-- This does not address interrupt stacks - according to the original patch
-
-Thread stack allocation is initiated from clone(), fork(), vfork(),
-kernel_thread() via kernel_clone(). Leaving a few hints for searching
-the code base to understand when and how thread stack is allocated.
-
-Bulk of the code is in:
-`kernel/fork.c <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/fork.c>`.
-
-stack_vm_area pointer in task_struct keeps track of the virtually allocated
-stack and a non-null stack_vm_area pointer serves as a indication that the
-virtually mapped kernel stacks are enabled.
-
-::
-
-        struct vm_struct *stack_vm_area;
-
-Stack overflow handling
------------------------
-
-Leading and trailing guard pages help detect stack overflows. When stack
-overflows into the guard pages, handlers have to be careful not overflow
-the stack again. When handlers are called, it is likely that very little
-stack space is left.
-
-On x86, this is done by handling the page fault indicating the kernel
-stack overflow on the double-fault stack.
-
-Testing VMAP allocation with guard pages
-----------------------------------------
-
-How do we ensure that VMAP_STACK is actually allocating with a leading
-and trailing guard page? The following lkdtm tests can help detect any
-regressions.
-
-::
-
-        void lkdtm_STACK_GUARD_PAGE_LEADING()
-        void lkdtm_STACK_GUARD_PAGE_TRAILING()
-
-Conclusions
------------
-
-- A percpu cache of vmalloced stacks appears to be a bit faster than a
-  high-order stack allocation, at least when the cache hits.
-- THREAD_INFO_IN_TASK gets rid of arch-specific thread_info entirely and
-  simply embed the thread_info (containing only flags) and 'int cpu' into
-  task_struct.
-- The thread stack can be free'ed as soon as the task is dead (without
-  waiting for RCU) and then, if vmapped stacks are in use, cache the
-  entire stack for reuse on the same cpu.
diff --git a/Documentation/vm/vmemmap_dedup.rst b/Documentation/vm/vmemmap_dedup.rst
deleted file mode 100644 (file)
index c9c495f..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-=========================================
-A vmemmap diet for HugeTLB and Device DAX
-=========================================
-
-HugeTLB
-=======
-
-The struct page structures (page structs) are used to describe a physical
-page frame. By default, there is a one-to-one mapping from a page frame to
-it's corresponding page struct.
-
-HugeTLB pages consist of multiple base page size pages and is supported by many
-architectures. See Documentation/admin-guide/mm/hugetlbpage.rst for more
-details. On the x86-64 architecture, HugeTLB pages of size 2MB and 1GB are
-currently supported. Since the base page size on x86 is 4KB, a 2MB HugeTLB page
-consists of 512 base pages and a 1GB HugeTLB page consists of 4096 base pages.
-For each base page, there is a corresponding page struct.
-
-Within the HugeTLB subsystem, only the first 4 page structs are used to
-contain unique information about a HugeTLB page. __NR_USED_SUBPAGE provides
-this upper limit. The only 'useful' information in the remaining page structs
-is the compound_head field, and this field is the same for all tail pages.
-
-By removing redundant page structs for HugeTLB pages, memory can be returned
-to the buddy allocator for other uses.
-
-Different architectures support different HugeTLB pages. For example, the
-following table is the HugeTLB page size supported by x86 and arm64
-architectures. Because arm64 supports 4k, 16k, and 64k base pages and
-supports contiguous entries, so it supports many kinds of sizes of HugeTLB
-page.
-
-+--------------+-----------+-----------------------------------------------+
-| Architecture | Page Size |                HugeTLB Page Size              |
-+--------------+-----------+-----------+-----------+-----------+-----------+
-|    x86-64    |    4KB    |    2MB    |    1GB    |           |           |
-+--------------+-----------+-----------+-----------+-----------+-----------+
-|              |    4KB    |   64KB    |    2MB    |    32MB   |    1GB    |
-|              +-----------+-----------+-----------+-----------+-----------+
-|    arm64     |   16KB    |    2MB    |   32MB    |     1GB   |           |
-|              +-----------+-----------+-----------+-----------+-----------+
-|              |   64KB    |    2MB    |  512MB    |    16GB   |           |
-+--------------+-----------+-----------+-----------+-----------+-----------+
-
-When the system boot up, every HugeTLB page has more than one struct page
-structs which size is (unit: pages)::
-
-   struct_size = HugeTLB_Size / PAGE_SIZE * sizeof(struct page) / PAGE_SIZE
-
-Where HugeTLB_Size is the size of the HugeTLB page. We know that the size
-of the HugeTLB page is always n times PAGE_SIZE. So we can get the following
-relationship::
-
-   HugeTLB_Size = n * PAGE_SIZE
-
-Then::
-
-   struct_size = n * PAGE_SIZE / PAGE_SIZE * sizeof(struct page) / PAGE_SIZE
-               = n * sizeof(struct page) / PAGE_SIZE
-
-We can use huge mapping at the pud/pmd level for the HugeTLB page.
-
-For the HugeTLB page of the pmd level mapping, then::
-
-   struct_size = n * sizeof(struct page) / PAGE_SIZE
-               = PAGE_SIZE / sizeof(pte_t) * sizeof(struct page) / PAGE_SIZE
-               = sizeof(struct page) / sizeof(pte_t)
-               = 64 / 8
-               = 8 (pages)
-
-Where n is how many pte entries which one page can contains. So the value of
-n is (PAGE_SIZE / sizeof(pte_t)).
-
-This optimization only supports 64-bit system, so the value of sizeof(pte_t)
-is 8. And this optimization also applicable only when the size of struct page
-is a power of two. In most cases, the size of struct page is 64 bytes (e.g.
-x86-64 and arm64). So if we use pmd level mapping for a HugeTLB page, the
-size of struct page structs of it is 8 page frames which size depends on the
-size of the base page.
-
-For the HugeTLB page of the pud level mapping, then::
-
-   struct_size = PAGE_SIZE / sizeof(pmd_t) * struct_size(pmd)
-               = PAGE_SIZE / 8 * 8 (pages)
-               = PAGE_SIZE (pages)
-
-Where the struct_size(pmd) is the size of the struct page structs of a
-HugeTLB page of the pmd level mapping.
-
-E.g.: A 2MB HugeTLB page on x86_64 consists in 8 page frames while 1GB
-HugeTLB page consists in 4096.
-
-Next, we take the pmd level mapping of the HugeTLB page as an example to
-show the internal implementation of this optimization. There are 8 pages
-struct page structs associated with a HugeTLB page which is pmd mapped.
-
-Here is how things look before optimization::
-
-    HugeTLB                  struct pages(8 pages)         page frame(8 pages)
- +-----------+ ---virt_to_page---> +-----------+   mapping to   +-----------+
- |           |                     |     0     | -------------> |     0     |
- |           |                     +-----------+                +-----------+
- |           |                     |     1     | -------------> |     1     |
- |           |                     +-----------+                +-----------+
- |           |                     |     2     | -------------> |     2     |
- |           |                     +-----------+                +-----------+
- |           |                     |     3     | -------------> |     3     |
- |           |                     +-----------+                +-----------+
- |           |                     |     4     | -------------> |     4     |
- |    PMD    |                     +-----------+                +-----------+
- |   level   |                     |     5     | -------------> |     5     |
- |  mapping  |                     +-----------+                +-----------+
- |           |                     |     6     | -------------> |     6     |
- |           |                     +-----------+                +-----------+
- |           |                     |     7     | -------------> |     7     |
- |           |                     +-----------+                +-----------+
- |           |
- |           |
- |           |
- +-----------+
-
-The value of page->compound_head is the same for all tail pages. The first
-page of page structs (page 0) associated with the HugeTLB page contains the 4
-page structs necessary to describe the HugeTLB. The only use of the remaining
-pages of page structs (page 1 to page 7) is to point to page->compound_head.
-Therefore, we can remap pages 1 to 7 to page 0. Only 1 page of page structs
-will be used for each HugeTLB page. This will allow us to free the remaining
-7 pages to the buddy allocator.
-
-Here is how things look after remapping::
-
-    HugeTLB                  struct pages(8 pages)         page frame(8 pages)
- +-----------+ ---virt_to_page---> +-----------+   mapping to   +-----------+
- |           |                     |     0     | -------------> |     0     |
- |           |                     +-----------+                +-----------+
- |           |                     |     1     | ---------------^ ^ ^ ^ ^ ^ ^
- |           |                     +-----------+                  | | | | | |
- |           |                     |     2     | -----------------+ | | | | |
- |           |                     +-----------+                    | | | | |
- |           |                     |     3     | -------------------+ | | | |
- |           |                     +-----------+                      | | | |
- |           |                     |     4     | ---------------------+ | | |
- |    PMD    |                     +-----------+                        | | |
- |   level   |                     |     5     | -----------------------+ | |
- |  mapping  |                     +-----------+                          | |
- |           |                     |     6     | -------------------------+ |
- |           |                     +-----------+                            |
- |           |                     |     7     | ---------------------------+
- |           |                     +-----------+
- |           |
- |           |
- |           |
- +-----------+
-
-When a HugeTLB is freed to the buddy system, we should allocate 7 pages for
-vmemmap pages and restore the previous mapping relationship.
-
-For the HugeTLB page of the pud level mapping. It is similar to the former.
-We also can use this approach to free (PAGE_SIZE - 1) vmemmap pages.
-
-Apart from the HugeTLB page of the pmd/pud level mapping, some architectures
-(e.g. aarch64) provides a contiguous bit in the translation table entries
-that hints to the MMU to indicate that it is one of a contiguous set of
-entries that can be cached in a single TLB entry.
-
-The contiguous bit is used to increase the mapping size at the pmd and pte
-(last) level. So this type of HugeTLB page can be optimized only when its
-size of the struct page structs is greater than 1 page.
-
-Notice: The head vmemmap page is not freed to the buddy allocator and all
-tail vmemmap pages are mapped to the head vmemmap page frame. So we can see
-more than one struct page struct with PG_head (e.g. 8 per 2 MB HugeTLB page)
-associated with each HugeTLB page. The compound_head() can handle this
-correctly (more details refer to the comment above compound_head()).
-
-Device DAX
-==========
-
-The device-dax interface uses the same tail deduplication technique explained
-in the previous chapter, except when used with the vmemmap in
-the device (altmap).
-
-The following page sizes are supported in DAX: PAGE_SIZE (4K on x86_64),
-PMD_SIZE (2M on x86_64) and PUD_SIZE (1G on x86_64).
-
-The differences with HugeTLB are relatively minor.
-
-It only use 3 page structs for storing all information as opposed
-to 4 on HugeTLB pages.
-
-There's no remapping of vmemmap given that device-dax memory is not part of
-System RAM ranges initialized at boot. Thus the tail page deduplication
-happens at a later stage when we populate the sections. HugeTLB reuses the
-the head vmemmap page representing, whereas device-dax reuses the tail
-vmemmap page. This results in only half of the savings compared to HugeTLB.
-
-Deduplicated tail pages are not mapped read-only.
-
-Here's how things look like on device-dax after the sections are populated::
-
- +-----------+ ---virt_to_page---> +-----------+   mapping to   +-----------+
- |           |                     |     0     | -------------> |     0     |
- |           |                     +-----------+                +-----------+
- |           |                     |     1     | -------------> |     1     |
- |           |                     +-----------+                +-----------+
- |           |                     |     2     | ----------------^ ^ ^ ^ ^ ^
- |           |                     +-----------+                   | | | | |
- |           |                     |     3     | ------------------+ | | | |
- |           |                     +-----------+                     | | | |
- |           |                     |     4     | --------------------+ | | |
- |    PMD    |                     +-----------+                       | | |
- |   level   |                     |     5     | ----------------------+ | |
- |  mapping  |                     +-----------+                         | |
- |           |                     |     6     | ------------------------+ |
- |           |                     +-----------+                           |
- |           |                     |     7     | --------------------------+
- |           |                     +-----------+
- |           |
- |           |
- |           |
- +-----------+
diff --git a/Documentation/vm/z3fold.rst b/Documentation/vm/z3fold.rst
deleted file mode 100644 (file)
index 224e3c6..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-.. _z3fold:
-
-======
-z3fold
-======
-
-z3fold is a special purpose allocator for storing compressed pages.
-It is designed to store up to three compressed pages per physical page.
-It is a zbud derivative which allows for higher compression
-ratio keeping the simplicity and determinism of its predecessor.
-
-The main differences between z3fold and zbud are:
-
-* unlike zbud, z3fold allows for up to PAGE_SIZE allocations
-* z3fold can hold up to 3 compressed pages in its page
-* z3fold doesn't export any API itself and is thus intended to be used
-  via the zpool API.
-
-To keep the determinism and simplicity, z3fold, just like zbud, always
-stores an integral number of compressed pages per page, but it can store
-up to 3 pages unlike zbud which can store at most 2. Therefore the
-compression ratio goes to around 2.7x while zbud's one is around 1.7x.
-
-Unlike zbud (but like zsmalloc for that matter) z3fold_alloc() does not
-return a dereferenceable pointer. Instead, it returns an unsigned long
-handle which encodes actual location of the allocated object.
-
-Keeping effective compression ratio close to zsmalloc's, z3fold doesn't
-depend on MMU enabled and provides more predictable reclaim behavior
-which makes it a better fit for small and response-critical systems.
diff --git a/Documentation/vm/zsmalloc.rst b/Documentation/vm/zsmalloc.rst
deleted file mode 100644 (file)
index 6e79893..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-.. _zsmalloc:
-
-========
-zsmalloc
-========
-
-This allocator is designed for use with zram. Thus, the allocator is
-supposed to work well under low memory conditions. In particular, it
-never attempts higher order page allocation which is very likely to
-fail under memory pressure. On the other hand, if we just use single
-(0-order) pages, it would suffer from very high fragmentation --
-any object of size PAGE_SIZE/2 or larger would occupy an entire page.
-This was one of the major issues with its predecessor (xvmalloc).
-
-To overcome these issues, zsmalloc allocates a bunch of 0-order pages
-and links them together using various 'struct page' fields. These linked
-pages act as a single higher-order page i.e. an object can span 0-order
-page boundaries. The code refers to these linked pages as a single entity
-called zspage.
-
-For simplicity, zsmalloc can only allocate objects of size up to PAGE_SIZE
-since this satisfies the requirements of all its current users (in the
-worst case, page is incompressible and is thus stored "as-is" i.e. in
-uncompressed form). For allocation requests larger than this size, failure
-is returned (see zs_malloc).
-
-Additionally, zs_malloc() does not return a dereferenceable pointer.
-Instead, it returns an opaque handle (unsigned long) which encodes actual
-location of the allocated object. The reason for this indirection is that
-zsmalloc does not keep zspages permanently mapped since that would cause
-issues on 32-bit systems where the VA region for kernel space mappings
-is very small. So, before using the allocating memory, the object has to
-be mapped using zs_map_object() to get a usable pointer and subsequently
-unmapped using zs_unmap_object().
-
-stat
-====
-
-With CONFIG_ZSMALLOC_STAT, we could see zsmalloc internal information via
-``/sys/kernel/debug/zsmalloc/<user name>``. Here is a sample of stat output::
-
- # cat /sys/kernel/debug/zsmalloc/zram0/classes
-
- class  size almost_full almost_empty obj_allocated   obj_used pages_used pages_per_zspage
-    ...
-    ...
-     9   176           0            1           186        129          8                4
-    10   192           1            0          2880       2872        135                3
-    11   208           0            1           819        795         42                2
-    12   224           0            1           219        159         12                4
-    ...
-    ...
-
-
-class
-       index
-size
-       object size zspage stores
-almost_empty
-       the number of ZS_ALMOST_EMPTY zspages(see below)
-almost_full
-       the number of ZS_ALMOST_FULL zspages(see below)
-obj_allocated
-       the number of objects allocated
-obj_used
-       the number of objects allocated to the user
-pages_used
-       the number of pages allocated for the class
-pages_per_zspage
-       the number of 0-order pages to make a zspage
-
-We assign a zspage to ZS_ALMOST_EMPTY fullness group when n <= N / f, where
-
-* n = number of allocated objects
-* N = total number of objects zspage can store
-* f = fullness_threshold_frac(ie, 4 at the moment)
-
-Similarly, we assign zspage to:
-
-* ZS_ALMOST_FULL  when n > N / f
-* ZS_EMPTY        when n == 0
-* ZS_FULL         when n == N
index b995141..f692bd9 100644 (file)
@@ -5668,7 +5668,7 @@ L:        linux-mm@kvack.org
 S:     Maintained
 F:     Documentation/ABI/testing/sysfs-kernel-mm-damon
 F:     Documentation/admin-guide/mm/damon/
-F:     Documentation/vm/damon/
+F:     Documentation/mm/damon/
 F:     include/linux/damon.h
 F:     include/trace/events/damon.h
 F:     mm/damon/
@@ -9252,7 +9252,7 @@ HMM - Heterogeneous Memory Management
 M:     Jérôme Glisse <jglisse@redhat.com>
 L:     linux-mm@kvack.org
 S:     Maintained
-F:     Documentation/vm/hmm.rst
+F:     Documentation/mm/hmm.rst
 F:     include/linux/hmm*
 F:     lib/test_hmm*
 F:     mm/hmm*
@@ -9350,8 +9350,8 @@ L:        linux-mm@kvack.org
 S:     Maintained
 F:     Documentation/ABI/testing/sysfs-kernel-mm-hugepages
 F:     Documentation/admin-guide/mm/hugetlbpage.rst
-F:     Documentation/vm/hugetlbfs_reserv.rst
-F:     Documentation/vm/vmemmap_dedup.rst
+F:     Documentation/mm/hugetlbfs_reserv.rst
+F:     Documentation/mm/vmemmap_dedup.rst
 F:     fs/hugetlbfs/
 F:     include/linux/hugetlb.h
 F:     mm/hugetlb.c
@@ -15338,7 +15338,7 @@ M:      Pasha Tatashin <pasha.tatashin@soleen.com>
 M:     Andrew Morton <akpm@linux-foundation.org>
 L:     linux-mm@kvack.org
 S:     Maintained
-F:     Documentation/vm/page_table_check.rst
+F:     Documentation/mm/page_table_check.rst
 F:     include/linux/page_table_check.h
 F:     mm/page_table_check.c
 
@@ -22480,7 +22480,7 @@ M:      Nitin Gupta <ngupta@vflare.org>
 R:     Sergey Senozhatsky <senozhatsky@chromium.org>
 L:     linux-mm@kvack.org
 S:     Maintained
-F:     Documentation/vm/zsmalloc.rst
+F:     Documentation/mm/zsmalloc.rst
 F:     include/linux/zsmalloc.h
 F:     mm/zsmalloc.c
 
index 170451f..3ea9661 100644 (file)
@@ -116,23 +116,6 @@ struct vm_area_struct;
  * arch/alpha/mm/fault.c)
  */
        /* xwr */
-#define __P000 _PAGE_P(_PAGE_FOE | _PAGE_FOW | _PAGE_FOR)
-#define __P001 _PAGE_P(_PAGE_FOE | _PAGE_FOW)
-#define __P010 _PAGE_P(_PAGE_FOE)
-#define __P011 _PAGE_P(_PAGE_FOE)
-#define __P100 _PAGE_P(_PAGE_FOW | _PAGE_FOR)
-#define __P101 _PAGE_P(_PAGE_FOW)
-#define __P110 _PAGE_P(0)
-#define __P111 _PAGE_P(0)
-
-#define __S000 _PAGE_S(_PAGE_FOE | _PAGE_FOW | _PAGE_FOR)
-#define __S001 _PAGE_S(_PAGE_FOE | _PAGE_FOW)
-#define __S010 _PAGE_S(_PAGE_FOE)
-#define __S011 _PAGE_S(_PAGE_FOE)
-#define __S100 _PAGE_S(_PAGE_FOW | _PAGE_FOR)
-#define __S101 _PAGE_S(_PAGE_FOW)
-#define __S110 _PAGE_S(0)
-#define __S111 _PAGE_S(0)
 
 /*
  * pgprot_noncached() is only for infiniband pci support, and a real
index ec20c10..ef427a6 100644 (file)
@@ -155,6 +155,10 @@ retry:
        if (fault_signal_pending(fault, regs))
                return;
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               return;
+
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
index 7511723..a155180 100644 (file)
@@ -280,3 +280,25 @@ mem_init(void)
        high_memory = (void *) __va(max_low_pfn * PAGE_SIZE);
        memblock_free_all();
 }
+
+static const pgprot_t protection_map[16] = {
+       [VM_NONE]                                       = _PAGE_P(_PAGE_FOE | _PAGE_FOW |
+                                                                 _PAGE_FOR),
+       [VM_READ]                                       = _PAGE_P(_PAGE_FOE | _PAGE_FOW),
+       [VM_WRITE]                                      = _PAGE_P(_PAGE_FOE),
+       [VM_WRITE | VM_READ]                            = _PAGE_P(_PAGE_FOE),
+       [VM_EXEC]                                       = _PAGE_P(_PAGE_FOW | _PAGE_FOR),
+       [VM_EXEC | VM_READ]                             = _PAGE_P(_PAGE_FOW),
+       [VM_EXEC | VM_WRITE]                            = _PAGE_P(0),
+       [VM_EXEC | VM_WRITE | VM_READ]                  = _PAGE_P(0),
+       [VM_SHARED]                                     = _PAGE_S(_PAGE_FOE | _PAGE_FOW |
+                                                                 _PAGE_FOR),
+       [VM_SHARED | VM_READ]                           = _PAGE_S(_PAGE_FOE | _PAGE_FOW),
+       [VM_SHARED | VM_WRITE]                          = _PAGE_S(_PAGE_FOE),
+       [VM_SHARED | VM_WRITE | VM_READ]                = _PAGE_S(_PAGE_FOE),
+       [VM_SHARED | VM_EXEC]                           = _PAGE_S(_PAGE_FOW | _PAGE_FOR),
+       [VM_SHARED | VM_EXEC | VM_READ]                 = _PAGE_S(_PAGE_FOW),
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = _PAGE_S(0),
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = _PAGE_S(0)
+};
+DECLARE_VM_GET_PAGE_PROT
index 183d23b..b23be55 100644 (file)
  *     This is to enable COW mechanism
  */
        /* xwr */
-#define __P000  PAGE_U_NONE
-#define __P001  PAGE_U_R
-#define __P010  PAGE_U_R       /* Pvt-W => !W */
-#define __P011  PAGE_U_R       /* Pvt-W => !W */
-#define __P100  PAGE_U_X_R     /* X => R */
-#define __P101  PAGE_U_X_R
-#define __P110  PAGE_U_X_R     /* Pvt-W => !W and X => R */
-#define __P111  PAGE_U_X_R     /* Pvt-W => !W */
-
-#define __S000  PAGE_U_NONE
-#define __S001  PAGE_U_R
-#define __S010  PAGE_U_W_R     /* W => R */
-#define __S011  PAGE_U_W_R
-#define __S100  PAGE_U_X_R     /* X => R */
-#define __S101  PAGE_U_X_R
-#define __S110  PAGE_U_X_W_R   /* X => R */
-#define __S111  PAGE_U_X_W_R
-
 #ifndef __ASSEMBLY__
 
 #define pte_write(pte)         (pte_val(pte) & _PAGE_WRITE)
index dad27e4..5ca59a4 100644 (file)
@@ -146,6 +146,10 @@ retry:
                return;
        }
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               return;
+
        /*
         * Fault retry nuances, mmap_lock already relinquished by core mm
         */
index 722d26b..fce5fa2 100644 (file)
@@ -74,3 +74,23 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr,
        info.align_offset = pgoff << PAGE_SHIFT;
        return vm_unmapped_area(&info);
 }
+
+static const pgprot_t protection_map[16] = {
+       [VM_NONE]                                       = PAGE_U_NONE,
+       [VM_READ]                                       = PAGE_U_R,
+       [VM_WRITE]                                      = PAGE_U_R,
+       [VM_WRITE | VM_READ]                            = PAGE_U_R,
+       [VM_EXEC]                                       = PAGE_U_X_R,
+       [VM_EXEC | VM_READ]                             = PAGE_U_X_R,
+       [VM_EXEC | VM_WRITE]                            = PAGE_U_X_R,
+       [VM_EXEC | VM_WRITE | VM_READ]                  = PAGE_U_X_R,
+       [VM_SHARED]                                     = PAGE_U_NONE,
+       [VM_SHARED | VM_READ]                           = PAGE_U_R,
+       [VM_SHARED | VM_WRITE]                          = PAGE_U_W_R,
+       [VM_SHARED | VM_WRITE | VM_READ]                = PAGE_U_W_R,
+       [VM_SHARED | VM_EXEC]                           = PAGE_U_X_R,
+       [VM_SHARED | VM_EXEC | VM_READ]                 = PAGE_U_X_R,
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = PAGE_U_X_W_R,
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = PAGE_U_X_W_R
+};
+DECLARE_VM_GET_PAGE_PROT
index cd1f84b..78a5320 100644 (file)
@@ -137,23 +137,6 @@ extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
  *  2) If we could do execute protection, then read is implied
  *  3) write implies read permissions
  */
-#define __P000  __PAGE_NONE
-#define __P001  __PAGE_READONLY
-#define __P010  __PAGE_COPY
-#define __P011  __PAGE_COPY
-#define __P100  __PAGE_READONLY_EXEC
-#define __P101  __PAGE_READONLY_EXEC
-#define __P110  __PAGE_COPY_EXEC
-#define __P111  __PAGE_COPY_EXEC
-
-#define __S000  __PAGE_NONE
-#define __S001  __PAGE_READONLY
-#define __S010  __PAGE_SHARED
-#define __S011  __PAGE_SHARED
-#define __S100  __PAGE_READONLY_EXEC
-#define __S101  __PAGE_READONLY_EXEC
-#define __S110  __PAGE_SHARED_EXEC
-#define __S111  __PAGE_SHARED_EXEC
 
 #ifndef __ASSEMBLY__
 /*
index 500612d..29e2900 100644 (file)
 #ifdef CONFIG_ARM_LPAE
        /* LPAE requires an additional page for the PGD */
 #define PG_DIR_SIZE    0x5000
-#define PMD_ORDER      3
+#define PMD_ENTRY_ORDER        3       /* PMD entry size is 2^PMD_ENTRY_ORDER */
 #else
 #define PG_DIR_SIZE    0x4000
-#define PMD_ORDER      2
+#define PMD_ENTRY_ORDER        2
 #endif
 
        .globl  swapper_pg_dir
@@ -240,7 +240,7 @@ __create_page_tables:
        mov     r6, r6, lsr #SECTION_SHIFT
 
 1:     orr     r3, r7, r5, lsl #SECTION_SHIFT  @ flags + kernel base
-       str     r3, [r4, r5, lsl #PMD_ORDER]    @ identity mapping
+       str     r3, [r4, r5, lsl #PMD_ENTRY_ORDER]      @ identity mapping
        cmp     r5, r6
        addlo   r5, r5, #1                      @ next section
        blo     1b
@@ -250,7 +250,7 @@ __create_page_tables:
         * set two variables to indicate the physical start and end of the
         * kernel.
         */
-       add     r0, r4, #KERNEL_OFFSET >> (SECTION_SHIFT - PMD_ORDER)
+       add     r0, r4, #KERNEL_OFFSET >> (SECTION_SHIFT - PMD_ENTRY_ORDER)
        ldr     r6, =(_end - 1)
        adr_l   r5, kernel_sec_start            @ _pa(kernel_sec_start)
 #if defined CONFIG_CPU_ENDIAN_BE8 || defined CONFIG_CPU_ENDIAN_BE32
@@ -259,8 +259,8 @@ __create_page_tables:
        str     r8, [r5]                        @ Save physical start of kernel (LE)
 #endif
        orr     r3, r8, r7                      @ Add the MMU flags
-       add     r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)
-1:     str     r3, [r0], #1 << PMD_ORDER
+       add     r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ENTRY_ORDER)
+1:     str     r3, [r0], #1 << PMD_ENTRY_ORDER
        add     r3, r3, #1 << SECTION_SHIFT
        cmp     r0, r6
        bls     1b
@@ -280,14 +280,14 @@ __create_page_tables:
        mov     r3, pc
        mov     r3, r3, lsr #SECTION_SHIFT
        orr     r3, r7, r3, lsl #SECTION_SHIFT
-       add     r0, r4,  #(XIP_START & 0xff000000) >> (SECTION_SHIFT - PMD_ORDER)
-       str     r3, [r0, #((XIP_START & 0x00f00000) >> SECTION_SHIFT) << PMD_ORDER]!
+       add     r0, r4,  #(XIP_START & 0xff000000) >> (SECTION_SHIFT - PMD_ENTRY_ORDER)
+       str     r3, [r0, #((XIP_START & 0x00f00000) >> SECTION_SHIFT) << PMD_ENTRY_ORDER]!
        ldr     r6, =(_edata_loc - 1)
-       add     r0, r0, #1 << PMD_ORDER
-       add     r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)
+       add     r0, r0, #1 << PMD_ENTRY_ORDER
+       add     r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ENTRY_ORDER)
 1:     cmp     r0, r6
        add     r3, r3, #1 << SECTION_SHIFT
-       strls   r3, [r0], #1 << PMD_ORDER
+       strls   r3, [r0], #1 << PMD_ENTRY_ORDER
        bls     1b
 #endif
 
@@ -297,10 +297,10 @@ __create_page_tables:
         */
        mov     r0, r2, lsr #SECTION_SHIFT
        cmp     r2, #0
-       ldrne   r3, =FDT_FIXED_BASE >> (SECTION_SHIFT - PMD_ORDER)
+       ldrne   r3, =FDT_FIXED_BASE >> (SECTION_SHIFT - PMD_ENTRY_ORDER)
        addne   r3, r3, r4
        orrne   r6, r7, r0, lsl #SECTION_SHIFT
-       strne   r6, [r3], #1 << PMD_ORDER
+       strne   r6, [r3], #1 << PMD_ENTRY_ORDER
        addne   r6, r6, #1 << SECTION_SHIFT
        strne   r6, [r3]
 
@@ -319,7 +319,7 @@ __create_page_tables:
        addruart r7, r3, r0
 
        mov     r3, r3, lsr #SECTION_SHIFT
-       mov     r3, r3, lsl #PMD_ORDER
+       mov     r3, r3, lsl #PMD_ENTRY_ORDER
 
        add     r0, r4, r3
        mov     r3, r7, lsr #SECTION_SHIFT
@@ -349,7 +349,7 @@ __create_page_tables:
         * If we're using the NetWinder or CATS, we also need to map
         * in the 16550-type serial port for the debug messages
         */
-       add     r0, r4, #0xff000000 >> (SECTION_SHIFT - PMD_ORDER)
+       add     r0, r4, #0xff000000 >> (SECTION_SHIFT - PMD_ENTRY_ORDER)
        orr     r3, r7, #0x7c000000
        str     r3, [r0]
 #endif
@@ -359,10 +359,10 @@ __create_page_tables:
         * Similar reasons here - for debug.  This is
         * only for Acorn RiscPC architectures.
         */
-       add     r0, r4, #0x02000000 >> (SECTION_SHIFT - PMD_ORDER)
+       add     r0, r4, #0x02000000 >> (SECTION_SHIFT - PMD_ENTRY_ORDER)
        orr     r3, r7, #0x02000000
        str     r3, [r0]
-       add     r0, r4, #0xd8000000 >> (SECTION_SHIFT - PMD_ORDER)
+       add     r0, r4, #0xd8000000 >> (SECTION_SHIFT - PMD_ENTRY_ORDER)
        str     r3, [r0]
 #endif
 #endif
index c30b689..14eecaa 100644 (file)
@@ -237,7 +237,7 @@ static int __init test_size_treshold(void)
        if (!dst_page)
                goto no_dst;
        kernel_ptr = page_address(src_page);
-       user_ptr = vmap(&dst_page, 1, VM_IOREMAP, __pgprot(__P010));
+       user_ptr = vmap(&dst_page, 1, VM_IOREMAP, __pgprot(__PAGE_COPY));
        if (!user_ptr)
                goto no_vmap;
 
index a062e07..46cccd6 100644 (file)
@@ -322,6 +322,10 @@ retry:
                return 0;
        }
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               return 0;
+
        if (!(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_RETRY) {
                        flags |= FAULT_FLAG_TRIED;
index cd17e32..a49f0b9 100644 (file)
@@ -412,6 +412,26 @@ void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot)
        local_flush_tlb_kernel_range(vaddr, vaddr + PAGE_SIZE);
 }
 
+static pgprot_t protection_map[16] __ro_after_init = {
+       [VM_NONE]                                       = __PAGE_NONE,
+       [VM_READ]                                       = __PAGE_READONLY,
+       [VM_WRITE]                                      = __PAGE_COPY,
+       [VM_WRITE | VM_READ]                            = __PAGE_COPY,
+       [VM_EXEC]                                       = __PAGE_READONLY_EXEC,
+       [VM_EXEC | VM_READ]                             = __PAGE_READONLY_EXEC,
+       [VM_EXEC | VM_WRITE]                            = __PAGE_COPY_EXEC,
+       [VM_EXEC | VM_WRITE | VM_READ]                  = __PAGE_COPY_EXEC,
+       [VM_SHARED]                                     = __PAGE_NONE,
+       [VM_SHARED | VM_READ]                           = __PAGE_READONLY,
+       [VM_SHARED | VM_WRITE]                          = __PAGE_SHARED,
+       [VM_SHARED | VM_WRITE | VM_READ]                = __PAGE_SHARED,
+       [VM_SHARED | VM_EXEC]                           = __PAGE_READONLY_EXEC,
+       [VM_SHARED | VM_EXEC | VM_READ]                 = __PAGE_READONLY_EXEC,
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = __PAGE_SHARED_EXEC,
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = __PAGE_SHARED_EXEC
+};
+DECLARE_VM_GET_PAGE_PROT
+
 /*
  * Adjust the PMD section entries according to the CPU in use.
  */
index 6bb5012..571cc23 100644 (file)
@@ -45,7 +45,6 @@ config ARM64
        select ARCH_HAS_SYSCALL_WRAPPER
        select ARCH_HAS_TEARDOWN_DMA_OPS if IOMMU_SUPPORT
        select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
-       select ARCH_HAS_VM_GET_PAGE_PROT
        select ARCH_HAS_ZONE_DMA_SET if EXPERT
        select ARCH_HAVE_ELF_PROT
        select ARCH_HAVE_NMI_SAFE_CMPXCHG
index 1fd2846..d20f5da 100644 (file)
@@ -46,9 +46,6 @@ extern void huge_pte_clear(struct mm_struct *mm, unsigned long addr,
                           pte_t *ptep, unsigned long sz);
 #define __HAVE_ARCH_HUGE_PTEP_GET
 extern pte_t huge_ptep_get(pte_t *ptep);
-extern void set_huge_swap_pte_at(struct mm_struct *mm, unsigned long addr,
-                                pte_t *ptep, pte_t pte, unsigned long sz);
-#define set_huge_swap_pte_at set_huge_swap_pte_at
 
 void __init arm64_hugetlb_cma_reserve(void);
 
index 4e4c171..9dd08cd 100644 (file)
@@ -381,6 +381,15 @@ static inline bool defer_reserve_crashkernel(void)
 # define INIT_MEMBLOCK_RESERVED_REGIONS        (INIT_MEMBLOCK_REGIONS + NR_CPUS + 1)
 #endif
 
+/*
+ * memory regions which marked with flag MEMBLOCK_NOMAP(for example, the memory
+ * of the EFI_UNUSABLE_MEMORY type) may divide a continuous memory block into
+ * multiple parts. As a result, the number of memory regions is large.
+ */
+#ifdef CONFIG_EFI
+#define INIT_MEMBLOCK_MEMORY_REGIONS   (INIT_MEMBLOCK_REGIONS * 8)
+#endif
+
 #include <asm-generic/memory_model.h>
 
 #endif /* __ASM_MEMORY_H */
index 62e0ebe..9b16511 100644 (file)
@@ -89,24 +89,6 @@ extern bool arm64_use_ng_mappings;
 #define PAGE_READONLY_EXEC     __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN)
 #define PAGE_EXECONLY          __pgprot(_PAGE_DEFAULT | PTE_RDONLY | PTE_NG | PTE_PXN)
 
-#define __P000  PAGE_NONE
-#define __P001  PAGE_READONLY
-#define __P010  PAGE_READONLY
-#define __P011  PAGE_READONLY
-#define __P100  PAGE_READONLY_EXEC     /* PAGE_EXECONLY if Enhanced PAN */
-#define __P101  PAGE_READONLY_EXEC
-#define __P110  PAGE_READONLY_EXEC
-#define __P111  PAGE_READONLY_EXEC
-
-#define __S000  PAGE_NONE
-#define __S001  PAGE_READONLY
-#define __S010  PAGE_SHARED
-#define __S011  PAGE_SHARED
-#define __S100  PAGE_READONLY_EXEC     /* PAGE_EXECONLY if Enhanced PAN */
-#define __S101  PAGE_READONLY_EXEC
-#define __S110  PAGE_SHARED_EXEC
-#define __S111  PAGE_SHARED_EXEC
-
 #endif /* __ASSEMBLY__ */
 
 #endif /* __ASM_PGTABLE_PROT_H */
index cdf3ffa..c33f1fa 100644 (file)
@@ -608,6 +608,10 @@ retry:
                return 0;
        }
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               return 0;
+
        if (fault & VM_FAULT_RETRY) {
                mm_flags |= FAULT_FLAG_TRIED;
                goto retry;
index 5307ffd..0795028 100644 (file)
@@ -241,6 +241,13 @@ static void clear_flush(struct mm_struct *mm,
        flush_tlb_range(&vma, saddr, addr);
 }
 
+static inline struct folio *hugetlb_swap_entry_to_folio(swp_entry_t entry)
+{
+       VM_BUG_ON(!is_migration_entry(entry) && !is_hwpoison_entry(entry));
+
+       return page_folio(pfn_to_page(swp_offset(entry)));
+}
+
 void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
                            pte_t *ptep, pte_t pte)
 {
@@ -250,11 +257,16 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
        unsigned long pfn, dpfn;
        pgprot_t hugeprot;
 
-       /*
-        * Code needs to be expanded to handle huge swap and migration
-        * entries. Needed for HUGETLB and MEMORY_FAILURE.
-        */
-       WARN_ON(!pte_present(pte));
+       if (!pte_present(pte)) {
+               struct folio *folio;
+
+               folio = hugetlb_swap_entry_to_folio(pte_to_swp_entry(pte));
+               ncontig = num_contig_ptes(folio_size(folio), &pgsize);
+
+               for (i = 0; i < ncontig; i++, ptep++)
+                       set_pte_at(mm, addr, ptep, pte);
+               return;
+       }
 
        if (!pte_cont(pte)) {
                set_pte_at(mm, addr, ptep, pte);
@@ -272,18 +284,6 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
                set_pte_at(mm, addr, ptep, pfn_pte(pfn, hugeprot));
 }
 
-void set_huge_swap_pte_at(struct mm_struct *mm, unsigned long addr,
-                         pte_t *ptep, pte_t pte, unsigned long sz)
-{
-       int i, ncontig;
-       size_t pgsize;
-
-       ncontig = num_contig_ptes(sz, &pgsize);
-
-       for (i = 0; i < ncontig; i++, ptep++)
-               set_pte(ptep, pte);
-}
-
 pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,
                      unsigned long addr, unsigned long sz)
 {
@@ -371,6 +371,28 @@ pte_t *huge_pte_offset(struct mm_struct *mm,
        return NULL;
 }
 
+unsigned long hugetlb_mask_last_page(struct hstate *h)
+{
+       unsigned long hp_size = huge_page_size(h);
+
+       switch (hp_size) {
+#ifndef __PAGETABLE_PMD_FOLDED
+       case PUD_SIZE:
+               return PGDIR_SIZE - PUD_SIZE;
+#endif
+       case CONT_PMD_SIZE:
+               return PUD_SIZE - CONT_PMD_SIZE;
+       case PMD_SIZE:
+               return PUD_SIZE - PMD_SIZE;
+       case CONT_PTE_SIZE:
+               return PMD_SIZE - CONT_PTE_SIZE;
+       default:
+               break;
+       }
+
+       return 0UL;
+}
+
 pte_t arch_make_huge_pte(pte_t entry, unsigned int shift, vm_flags_t flags)
 {
        size_t pagesize = 1UL << shift;
index 78e9490..8f5b7ce 100644 (file)
 #include <asm/cpufeature.h>
 #include <asm/page.h>
 
+static pgprot_t protection_map[16] __ro_after_init = {
+       [VM_NONE]                                       = PAGE_NONE,
+       [VM_READ]                                       = PAGE_READONLY,
+       [VM_WRITE]                                      = PAGE_READONLY,
+       [VM_WRITE | VM_READ]                            = PAGE_READONLY,
+       /* PAGE_EXECONLY if Enhanced PAN */
+       [VM_EXEC]                                       = PAGE_READONLY_EXEC,
+       [VM_EXEC | VM_READ]                             = PAGE_READONLY_EXEC,
+       [VM_EXEC | VM_WRITE]                            = PAGE_READONLY_EXEC,
+       [VM_EXEC | VM_WRITE | VM_READ]                  = PAGE_READONLY_EXEC,
+       [VM_SHARED]                                     = PAGE_NONE,
+       [VM_SHARED | VM_READ]                           = PAGE_READONLY,
+       [VM_SHARED | VM_WRITE]                          = PAGE_SHARED,
+       [VM_SHARED | VM_WRITE | VM_READ]                = PAGE_SHARED,
+       /* PAGE_EXECONLY if Enhanced PAN */
+       [VM_SHARED | VM_EXEC]                           = PAGE_READONLY_EXEC,
+       [VM_SHARED | VM_EXEC | VM_READ]                 = PAGE_READONLY_EXEC,
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = PAGE_SHARED_EXEC,
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = PAGE_SHARED_EXEC
+};
+
 /*
  * You really shouldn't be using read() or write() on /dev/mem.  This might go
  * away in the future.
index bbbd069..7d57e5d 100644 (file)
@@ -44,7 +44,7 @@ static inline pgd_t *pgd_alloc(struct mm_struct *mm)
        pgd_t *ret;
        pgd_t *init;
 
-       ret = (pgd_t *) __get_free_pages(GFP_KERNEL, PGD_ORDER);
+       ret = (pgd_t *) __get_free_page(GFP_KERNEL);
        if (ret) {
                init = pgd_offset(&init_mm, 0UL);
                pgd_init((unsigned long *)ret);
index bbe2451..c3d9b92 100644 (file)
 /*
  * C-SKY is two-level paging structure:
  */
-#define PGD_ORDER      0
-#define PTE_ORDER      0
 
-#define PTRS_PER_PGD   ((PAGE_SIZE << PGD_ORDER) / sizeof(pgd_t))
+#define PTRS_PER_PGD   (PAGE_SIZE / sizeof(pgd_t))
 #define PTRS_PER_PMD   1
-#define PTRS_PER_PTE   ((PAGE_SIZE << PTE_ORDER) / sizeof(pte_t))
+#define PTRS_PER_PTE   (PAGE_SIZE / sizeof(pte_t))
 
 #define pte_ERROR(e) \
        pr_err("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, (e).pte_low)
 #define MAX_SWAPFILES_CHECK() \
                BUILD_BUG_ON(MAX_SWAPFILES_SHIFT != 5)
 
-#define __P000 PAGE_NONE
-#define __P001 PAGE_READ
-#define __P010 PAGE_READ
-#define __P011 PAGE_READ
-#define __P100 PAGE_READ
-#define __P101 PAGE_READ
-#define __P110 PAGE_READ
-#define __P111 PAGE_READ
-
-#define __S000 PAGE_NONE
-#define __S001 PAGE_READ
-#define __S010 PAGE_WRITE
-#define __S011 PAGE_WRITE
-#define __S100 PAGE_READ
-#define __S101 PAGE_READ
-#define __S110 PAGE_WRITE
-#define __S111 PAGE_WRITE
-
 extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)];
 #define ZERO_PAGE(vaddr)       (virt_to_page(empty_zero_page))
 
index 7215a46..e15f736 100644 (file)
@@ -285,6 +285,10 @@ good_area:
                return;
        }
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               return;
+
        if (unlikely((fault & VM_FAULT_RETRY) && (flags & FAULT_FLAG_ALLOW_RETRY))) {
                flags |= FAULT_FLAG_TRIED;
 
index bf2004a..bde7cab 100644 (file)
@@ -197,3 +197,23 @@ void __init fixaddr_init(void)
        vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
        fixrange_init(vaddr, vaddr + PMD_SIZE, swapper_pg_dir);
 }
+
+static const pgprot_t protection_map[16] = {
+       [VM_NONE]                                       = PAGE_NONE,
+       [VM_READ]                                       = PAGE_READ,
+       [VM_WRITE]                                      = PAGE_READ,
+       [VM_WRITE | VM_READ]                            = PAGE_READ,
+       [VM_EXEC]                                       = PAGE_READ,
+       [VM_EXEC | VM_READ]                             = PAGE_READ,
+       [VM_EXEC | VM_WRITE]                            = PAGE_READ,
+       [VM_EXEC | VM_WRITE | VM_READ]                  = PAGE_READ,
+       [VM_SHARED]                                     = PAGE_NONE,
+       [VM_SHARED | VM_READ]                           = PAGE_READ,
+       [VM_SHARED | VM_WRITE]                          = PAGE_WRITE,
+       [VM_SHARED | VM_WRITE | VM_READ]                = PAGE_WRITE,
+       [VM_SHARED | VM_EXEC]                           = PAGE_READ,
+       [VM_SHARED | VM_EXEC | VM_READ]                 = PAGE_READ,
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = PAGE_WRITE,
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = PAGE_WRITE
+};
+DECLARE_VM_GET_PAGE_PROT
index 0610724..f7048c1 100644 (file)
@@ -126,33 +126,6 @@ extern unsigned long _dflt_cache_att;
  */
 #define CACHEDEF       (CACHE_DEFAULT << 6)
 
-/* Private (copy-on-write) page protections. */
-#define __P000 __pgprot(_PAGE_PRESENT | _PAGE_USER | CACHEDEF)
-#define __P001 __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_READ | CACHEDEF)
-#define __P010 __P000  /* Write-only copy-on-write */
-#define __P011 __P001  /* Read/Write copy-on-write */
-#define __P100 __pgprot(_PAGE_PRESENT | _PAGE_USER | \
-                       _PAGE_EXECUTE | CACHEDEF)
-#define __P101 __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_EXECUTE | \
-                       _PAGE_READ | CACHEDEF)
-#define __P110 __P100  /* Write/execute copy-on-write */
-#define __P111 __P101  /* Read/Write/Execute, copy-on-write */
-
-/* Shared page protections. */
-#define __S000 __P000
-#define __S001 __P001
-#define __S010 __pgprot(_PAGE_PRESENT | _PAGE_USER | \
-                       _PAGE_WRITE | CACHEDEF)
-#define __S011 __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_READ | \
-                       _PAGE_WRITE | CACHEDEF)
-#define __S100 __pgprot(_PAGE_PRESENT | _PAGE_USER | \
-                       _PAGE_EXECUTE | CACHEDEF)
-#define __S101 __P101
-#define __S110 __pgprot(_PAGE_PRESENT | _PAGE_USER | \
-                       _PAGE_EXECUTE | _PAGE_WRITE | CACHEDEF)
-#define __S111 __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_READ | \
-                       _PAGE_EXECUTE | _PAGE_WRITE | CACHEDEF)
-
 extern pgd_t swapper_pg_dir[PTRS_PER_PGD];  /* located in head.S */
 
 /*  HUGETLB not working currently  */
index 3167a3b..146115c 100644 (file)
@@ -234,3 +234,45 @@ void __init setup_arch_memory(void)
         *  which is called by start_kernel() later on in the process
         */
 }
+
+static const pgprot_t protection_map[16] = {
+       [VM_NONE]                                       = __pgprot(_PAGE_PRESENT | _PAGE_USER |
+                                                                  CACHEDEF),
+       [VM_READ]                                       = __pgprot(_PAGE_PRESENT | _PAGE_USER |
+                                                                  _PAGE_READ | CACHEDEF),
+       [VM_WRITE]                                      = __pgprot(_PAGE_PRESENT | _PAGE_USER |
+                                                                  CACHEDEF),
+       [VM_WRITE | VM_READ]                            = __pgprot(_PAGE_PRESENT | _PAGE_USER |
+                                                                  _PAGE_READ | CACHEDEF),
+       [VM_EXEC]                                       = __pgprot(_PAGE_PRESENT | _PAGE_USER |
+                                                                  _PAGE_EXECUTE | CACHEDEF),
+       [VM_EXEC | VM_READ]                             = __pgprot(_PAGE_PRESENT | _PAGE_USER |
+                                                                  _PAGE_EXECUTE | _PAGE_READ |
+                                                                  CACHEDEF),
+       [VM_EXEC | VM_WRITE]                            = __pgprot(_PAGE_PRESENT | _PAGE_USER |
+                                                                  _PAGE_EXECUTE | CACHEDEF),
+       [VM_EXEC | VM_WRITE | VM_READ]                  = __pgprot(_PAGE_PRESENT | _PAGE_USER |
+                                                                  _PAGE_EXECUTE | _PAGE_READ |
+                                                                  CACHEDEF),
+       [VM_SHARED]                                     = __pgprot(_PAGE_PRESENT | _PAGE_USER |
+                                                                  CACHEDEF),
+       [VM_SHARED | VM_READ]                           = __pgprot(_PAGE_PRESENT | _PAGE_USER |
+                                                                  _PAGE_READ | CACHEDEF),
+       [VM_SHARED | VM_WRITE]                          = __pgprot(_PAGE_PRESENT | _PAGE_USER |
+                                                                  _PAGE_WRITE | CACHEDEF),
+       [VM_SHARED | VM_WRITE | VM_READ]                = __pgprot(_PAGE_PRESENT | _PAGE_USER |
+                                                                  _PAGE_READ | _PAGE_WRITE |
+                                                                  CACHEDEF),
+       [VM_SHARED | VM_EXEC]                           = __pgprot(_PAGE_PRESENT | _PAGE_USER |
+                                                                  _PAGE_EXECUTE | CACHEDEF),
+       [VM_SHARED | VM_EXEC | VM_READ]                 = __pgprot(_PAGE_PRESENT | _PAGE_USER |
+                                                                  _PAGE_EXECUTE | _PAGE_READ |
+                                                                  CACHEDEF),
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = __pgprot(_PAGE_PRESENT | _PAGE_USER |
+                                                                  _PAGE_EXECUTE | _PAGE_WRITE |
+                                                                  CACHEDEF),
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = __pgprot(_PAGE_PRESENT | _PAGE_USER |
+                                                                  _PAGE_READ | _PAGE_EXECUTE |
+                                                                  _PAGE_WRITE | CACHEDEF)
+};
+DECLARE_VM_GET_PAGE_PROT
index 4fac4b9..f73c7cb 100644 (file)
@@ -96,6 +96,10 @@ good_area:
        if (fault_signal_pending(fault, regs))
                return;
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               return;
+
        /* The most common case -- we are done. */
        if (likely(!(fault & VM_FAULT_ERROR))) {
                if (fault & VM_FAULT_RETRY) {
index 7aa8f23..6925e28 100644 (file)
  * attempts to write to the page.
  */
        /* xwr */
-#define __P000 PAGE_NONE
-#define __P001 PAGE_READONLY
-#define __P010 PAGE_READONLY   /* write to priv pg -> copy & make writable */
-#define __P011 PAGE_READONLY   /* ditto */
-#define __P100 __pgprot(__ACCESS_BITS | _PAGE_PL_3 | _PAGE_AR_X_RX)
-#define __P101 __pgprot(__ACCESS_BITS | _PAGE_PL_3 | _PAGE_AR_RX)
-#define __P110 PAGE_COPY_EXEC
-#define __P111 PAGE_COPY_EXEC
-
-#define __S000 PAGE_NONE
-#define __S001 PAGE_READONLY
-#define __S010 PAGE_SHARED     /* we don't have (and don't need) write-only */
-#define __S011 PAGE_SHARED
-#define __S100 __pgprot(__ACCESS_BITS | _PAGE_PL_3 | _PAGE_AR_X_RX)
-#define __S101 __pgprot(__ACCESS_BITS | _PAGE_PL_3 | _PAGE_AR_RX)
-#define __S110 __pgprot(__ACCESS_BITS | _PAGE_PL_3 | _PAGE_AR_RWX)
-#define __S111 __pgprot(__ACCESS_BITS | _PAGE_PL_3 | _PAGE_AR_RWX)
-
 #define pgd_ERROR(e)   printk("%s:%d: bad pgd %016lx.\n", __FILE__, __LINE__, pgd_val(e))
 #if CONFIG_PGTABLE_LEVELS == 4
 #define pud_ERROR(e)   printk("%s:%d: bad pud %016lx.\n", __FILE__, __LINE__, pud_val(e))
index 07379d1..ef78c2d 100644 (file)
@@ -139,6 +139,10 @@ retry:
        if (fault_signal_pending(fault, regs))
                return;
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               return;
+
        if (unlikely(fault & VM_FAULT_ERROR)) {
                /*
                 * We ran out of memory, or some other thing happened
index 855d949..fc4e421 100644 (file)
@@ -273,7 +273,7 @@ static int __init gate_vma_init(void)
        gate_vma.vm_start = FIXADDR_USER_START;
        gate_vma.vm_end = FIXADDR_USER_END;
        gate_vma.vm_flags = VM_READ | VM_MAYREAD | VM_EXEC | VM_MAYEXEC;
-       gate_vma.vm_page_prot = __P101;
+       gate_vma.vm_page_prot = __pgprot(__ACCESS_BITS | _PAGE_PL_3 | _PAGE_AR_RX);
 
        return 0;
 }
@@ -490,3 +490,29 @@ void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap)
        __remove_pages(start_pfn, nr_pages, altmap);
 }
 #endif
+
+static const pgprot_t protection_map[16] = {
+       [VM_NONE]                                       = PAGE_NONE,
+       [VM_READ]                                       = PAGE_READONLY,
+       [VM_WRITE]                                      = PAGE_READONLY,
+       [VM_WRITE | VM_READ]                            = PAGE_READONLY,
+       [VM_EXEC]                                       = __pgprot(__ACCESS_BITS | _PAGE_PL_3 |
+                                                                  _PAGE_AR_X_RX),
+       [VM_EXEC | VM_READ]                             = __pgprot(__ACCESS_BITS | _PAGE_PL_3 |
+                                                                  _PAGE_AR_RX),
+       [VM_EXEC | VM_WRITE]                            = PAGE_COPY_EXEC,
+       [VM_EXEC | VM_WRITE | VM_READ]                  = PAGE_COPY_EXEC,
+       [VM_SHARED]                                     = PAGE_NONE,
+       [VM_SHARED | VM_READ]                           = PAGE_READONLY,
+       [VM_SHARED | VM_WRITE]                          = PAGE_SHARED,
+       [VM_SHARED | VM_WRITE | VM_READ]                = PAGE_SHARED,
+       [VM_SHARED | VM_EXEC]                           = __pgprot(__ACCESS_BITS | _PAGE_PL_3 |
+                                                                  _PAGE_AR_X_RX),
+       [VM_SHARED | VM_EXEC | VM_READ]                 = __pgprot(__ACCESS_BITS | _PAGE_PL_3 |
+                                                                  _PAGE_AR_RX),
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = __pgprot(__ACCESS_BITS | _PAGE_PL_3 |
+                                                                  _PAGE_AR_RWX),
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = __pgprot(__ACCESS_BITS | _PAGE_PL_3 |
+                                                                  _PAGE_AR_RWX)
+};
+DECLARE_VM_GET_PAGE_PROT
index fc24658..ae15610 100644 (file)
@@ -407,7 +407,7 @@ config ARCH_SPARSEMEM_ENABLE
          Say Y to support efficient handling of sparse physical memory,
          for architectures which are either NUMA (Non-Uniform Memory Access)
          or have huge holes in the physical address space for other reasons.
-         See <file:Documentation/vm/numa.rst> for more.
+         See <file:Documentation/mm/numa.rst> for more.
 
 config ARCH_ENABLE_THP_MIGRATION
        def_bool y
index b0a57b2..4bfeb3c 100644 (file)
@@ -66,12 +66,12 @@ static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long address)
        pmd_t *pmd;
        struct page *pg;
 
-       pg = alloc_pages(GFP_KERNEL_ACCOUNT, PMD_ORDER);
+       pg = alloc_page(GFP_KERNEL_ACCOUNT);
        if (!pg)
                return NULL;
 
        if (!pgtable_pmd_page_ctor(pg)) {
-               __free_pages(pg, PMD_ORDER);
+               __free_page(pg);
                return NULL;
        }
 
@@ -90,7 +90,7 @@ static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long address)
 {
        pud_t *pud;
 
-       pud = (pud_t *) __get_free_pages(GFP_KERNEL, PUD_ORDER);
+       pud = (pud_t *) __get_free_page(GFP_KERNEL);
        if (pud)
                pud_init((unsigned long)pud, (unsigned long)invalid_pmd_table);
        return pud;
index 3badd11..9ca147a 100644 (file)
                                 _PAGE_GLOBAL | _PAGE_KERN |  _CACHE_SUC)
 #define PAGE_KERNEL_WUC __pgprot(_PAGE_PRESENT | __READABLE | __WRITEABLE | \
                                 _PAGE_GLOBAL | _PAGE_KERN |  _CACHE_WUC)
-
-#define __P000 __pgprot(_CACHE_CC | _PAGE_USER | _PAGE_PROTNONE | _PAGE_NO_EXEC | _PAGE_NO_READ)
-#define __P001 __pgprot(_CACHE_CC | _PAGE_VALID | _PAGE_USER | _PAGE_PRESENT | _PAGE_NO_EXEC)
-#define __P010 __pgprot(_CACHE_CC | _PAGE_VALID | _PAGE_USER | _PAGE_PRESENT | _PAGE_NO_EXEC)
-#define __P011 __pgprot(_CACHE_CC | _PAGE_VALID | _PAGE_USER | _PAGE_PRESENT | _PAGE_NO_EXEC)
-#define __P100 __pgprot(_CACHE_CC | _PAGE_VALID | _PAGE_USER | _PAGE_PRESENT)
-#define __P101 __pgprot(_CACHE_CC | _PAGE_VALID | _PAGE_USER | _PAGE_PRESENT)
-#define __P110 __pgprot(_CACHE_CC | _PAGE_VALID | _PAGE_USER | _PAGE_PRESENT)
-#define __P111 __pgprot(_CACHE_CC | _PAGE_VALID | _PAGE_USER | _PAGE_PRESENT)
-
-#define __S000 __pgprot(_CACHE_CC | _PAGE_USER | _PAGE_PROTNONE | _PAGE_NO_EXEC | _PAGE_NO_READ)
-#define __S001 __pgprot(_CACHE_CC | _PAGE_VALID | _PAGE_USER | _PAGE_PRESENT | _PAGE_NO_EXEC)
-#define __S010 __pgprot(_CACHE_CC | _PAGE_VALID | _PAGE_USER | _PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_WRITE)
-#define __S011 __pgprot(_CACHE_CC | _PAGE_VALID | _PAGE_USER | _PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_WRITE)
-#define __S100 __pgprot(_CACHE_CC | _PAGE_VALID | _PAGE_USER | _PAGE_PRESENT)
-#define __S101 __pgprot(_CACHE_CC | _PAGE_VALID | _PAGE_USER | _PAGE_PRESENT)
-#define __S110 __pgprot(_CACHE_CC | _PAGE_VALID | _PAGE_USER | _PAGE_PRESENT | _PAGE_WRITE)
-#define __S111 __pgprot(_CACHE_CC | _PAGE_VALID | _PAGE_USER | _PAGE_PRESENT | _PAGE_WRITE)
-
 #ifndef __ASSEMBLY__
 
 #define pgprot_noncached pgprot_noncached
index d9e86cf..e03443a 100644 (file)
 #include <asm-generic/pgtable-nop4d.h>
 #endif
 
-#define PGD_ORDER              0
-#define PUD_ORDER              0
-#define PMD_ORDER              0
-#define PTE_ORDER              0
-
 #if CONFIG_PGTABLE_LEVELS == 2
-#define PGDIR_SHIFT    (PAGE_SHIFT + (PAGE_SHIFT + PTE_ORDER - 3))
+#define PGDIR_SHIFT    (PAGE_SHIFT + (PAGE_SHIFT - 3))
 #elif CONFIG_PGTABLE_LEVELS == 3
-#define PMD_SHIFT      (PAGE_SHIFT + (PAGE_SHIFT + PTE_ORDER - 3))
+#define PMD_SHIFT      (PAGE_SHIFT + (PAGE_SHIFT - 3))
 #define PMD_SIZE       (1UL << PMD_SHIFT)
 #define PMD_MASK       (~(PMD_SIZE-1))
-#define PGDIR_SHIFT    (PMD_SHIFT + (PAGE_SHIFT + PMD_ORDER - 3))
+#define PGDIR_SHIFT    (PMD_SHIFT + (PAGE_SHIFT - 3))
 #elif CONFIG_PGTABLE_LEVELS == 4
-#define PMD_SHIFT      (PAGE_SHIFT + (PAGE_SHIFT + PTE_ORDER - 3))
+#define PMD_SHIFT      (PAGE_SHIFT + (PAGE_SHIFT - 3))
 #define PMD_SIZE       (1UL << PMD_SHIFT)
 #define PMD_MASK       (~(PMD_SIZE-1))
-#define PUD_SHIFT      (PMD_SHIFT + (PAGE_SHIFT + PMD_ORDER - 3))
+#define PUD_SHIFT      (PMD_SHIFT + (PAGE_SHIFT - 3))
 #define PUD_SIZE       (1UL << PUD_SHIFT)
 #define PUD_MASK       (~(PUD_SIZE-1))
-#define PGDIR_SHIFT    (PUD_SHIFT + (PAGE_SHIFT + PUD_ORDER - 3))
+#define PGDIR_SHIFT    (PUD_SHIFT + (PAGE_SHIFT - 3))
 #endif
 
 #define PGDIR_SIZE     (1UL << PGDIR_SHIFT)
 #define PGDIR_MASK     (~(PGDIR_SIZE-1))
 
-#define VA_BITS                (PGDIR_SHIFT + (PAGE_SHIFT + PGD_ORDER - 3))
+#define VA_BITS                (PGDIR_SHIFT + (PAGE_SHIFT - 3))
 
-#define PTRS_PER_PGD   ((PAGE_SIZE << PGD_ORDER) >> 3)
+#define PTRS_PER_PGD   (PAGE_SIZE >> 3)
 #if CONFIG_PGTABLE_LEVELS > 3
-#define PTRS_PER_PUD   ((PAGE_SIZE << PUD_ORDER) >> 3)
+#define PTRS_PER_PUD   (PAGE_SIZE >> 3)
 #endif
 #if CONFIG_PGTABLE_LEVELS > 2
-#define PTRS_PER_PMD   ((PAGE_SIZE << PMD_ORDER) >> 3)
+#define PTRS_PER_PMD   (PAGE_SIZE >> 3)
 #endif
-#define PTRS_PER_PTE   ((PAGE_SIZE << PTE_ORDER) >> 3)
+#define PTRS_PER_PTE   (PAGE_SIZE >> 3)
 
 #define USER_PTRS_PER_PGD       ((TASK_SIZE64 / PGDIR_SIZE)?(TASK_SIZE64 / PGDIR_SIZE):1)
 
index 20cd9e1..d256b81 100644 (file)
@@ -189,12 +189,6 @@ void output_mm_defines(void)
 #endif
        DEFINE(_PTE_T_LOG2, PTE_T_LOG2);
        BLANK();
-       DEFINE(_PGD_ORDER, PGD_ORDER);
-#ifndef __PAGETABLE_PMD_FOLDED
-       DEFINE(_PMD_ORDER, PMD_ORDER);
-#endif
-       DEFINE(_PTE_ORDER, PTE_ORDER);
-       BLANK();
        DEFINE(_PMD_SHIFT, PMD_SHIFT);
        DEFINE(_PGDIR_SHIFT, PGDIR_SHIFT);
        BLANK();
index 9e5ce5a..e8c68dc 100644 (file)
@@ -139,3 +139,49 @@ void cpu_cache_init(void)
 
        shm_align_mask = PAGE_SIZE - 1;
 }
+
+static const pgprot_t protection_map[16] = {
+       [VM_NONE]                                       = __pgprot(_CACHE_CC | _PAGE_USER |
+                                                                  _PAGE_PROTNONE | _PAGE_NO_EXEC |
+                                                                  _PAGE_NO_READ),
+       [VM_READ]                                       = __pgprot(_CACHE_CC | _PAGE_VALID |
+                                                                  _PAGE_USER | _PAGE_PRESENT |
+                                                                  _PAGE_NO_EXEC),
+       [VM_WRITE]                                      = __pgprot(_CACHE_CC | _PAGE_VALID |
+                                                                  _PAGE_USER | _PAGE_PRESENT |
+                                                                  _PAGE_NO_EXEC),
+       [VM_WRITE | VM_READ]                            = __pgprot(_CACHE_CC | _PAGE_VALID |
+                                                                  _PAGE_USER | _PAGE_PRESENT |
+                                                                  _PAGE_NO_EXEC),
+       [VM_EXEC]                                       = __pgprot(_CACHE_CC | _PAGE_VALID |
+                                                                  _PAGE_USER | _PAGE_PRESENT),
+       [VM_EXEC | VM_READ]                             = __pgprot(_CACHE_CC | _PAGE_VALID |
+                                                                  _PAGE_USER | _PAGE_PRESENT),
+       [VM_EXEC | VM_WRITE]                            = __pgprot(_CACHE_CC | _PAGE_VALID |
+                                                                  _PAGE_USER | _PAGE_PRESENT),
+       [VM_EXEC | VM_WRITE | VM_READ]                  = __pgprot(_CACHE_CC | _PAGE_VALID |
+                                                                  _PAGE_USER | _PAGE_PRESENT),
+       [VM_SHARED]                                     = __pgprot(_CACHE_CC | _PAGE_USER |
+                                                                  _PAGE_PROTNONE | _PAGE_NO_EXEC |
+                                                                  _PAGE_NO_READ),
+       [VM_SHARED | VM_READ]                           = __pgprot(_CACHE_CC | _PAGE_VALID |
+                                                                  _PAGE_USER | _PAGE_PRESENT |
+                                                                  _PAGE_NO_EXEC),
+       [VM_SHARED | VM_WRITE]                          = __pgprot(_CACHE_CC | _PAGE_VALID |
+                                                                  _PAGE_USER | _PAGE_PRESENT |
+                                                                  _PAGE_NO_EXEC | _PAGE_WRITE),
+       [VM_SHARED | VM_WRITE | VM_READ]                = __pgprot(_CACHE_CC | _PAGE_VALID |
+                                                                  _PAGE_USER | _PAGE_PRESENT |
+                                                                  _PAGE_NO_EXEC | _PAGE_WRITE),
+       [VM_SHARED | VM_EXEC]                           = __pgprot(_CACHE_CC | _PAGE_VALID |
+                                                                  _PAGE_USER | _PAGE_PRESENT),
+       [VM_SHARED | VM_EXEC | VM_READ]                 = __pgprot(_CACHE_CC | _PAGE_VALID |
+                                                                  _PAGE_USER | _PAGE_PRESENT),
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = __pgprot(_CACHE_CC | _PAGE_VALID |
+                                                                  _PAGE_USER | _PAGE_PRESENT |
+                                                                  _PAGE_WRITE),
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = __pgprot(_CACHE_CC | _PAGE_VALID |
+                                                                  _PAGE_USER | _PAGE_PRESENT |
+                                                                  _PAGE_WRITE)
+};
+DECLARE_VM_GET_PAGE_PROT
index 0569647..ee179cc 100644 (file)
@@ -13,7 +13,7 @@ pgd_t *pgd_alloc(struct mm_struct *mm)
 {
        pgd_t *ret, *init;
 
-       ret = (pgd_t *) __get_free_pages(GFP_KERNEL, PGD_ORDER);
+       ret = (pgd_t *) __get_free_page(GFP_KERNEL);
        if (ret) {
                init = pgd_offset(&init_mm, 0UL);
                pgd_init((unsigned long)ret);
index de19fa2..3974333 100644 (file)
@@ -83,7 +83,7 @@ vmalloc_done_load:
        bnez    t0, tlb_huge_update_load
 
        csrrd   t0, LOONGARCH_CSR_BADV
-       srli.d  t0, t0, (PAGE_SHIFT + PTE_ORDER)
+       srli.d  t0, t0, PAGE_SHIFT
        andi    t0, t0, (PTRS_PER_PTE - 1)
        slli.d  t0, t0, _PTE_T_LOG2
        add.d   t1, ra, t0
@@ -247,7 +247,7 @@ vmalloc_done_store:
        bnez    t0, tlb_huge_update_store
 
        csrrd   t0, LOONGARCH_CSR_BADV
-       srli.d  t0, t0, (PAGE_SHIFT + PTE_ORDER)
+       srli.d  t0, t0, PAGE_SHIFT
        andi    t0, t0, (PTRS_PER_PTE - 1)
        slli.d  t0, t0, _PTE_T_LOG2
        add.d   t1, ra, t0
@@ -414,7 +414,7 @@ vmalloc_done_modify:
        bnez    t0, tlb_huge_update_modify
 
        csrrd   t0, LOONGARCH_CSR_BADV
-       srli.d  t0, t0, (PAGE_SHIFT + PTE_ORDER)
+       srli.d  t0, t0, PAGE_SHIFT
        andi    t0, t0, (PTRS_PER_PTE - 1)
        slli.d  t0, t0, _PTE_T_LOG2
        add.d   t1, ra, t0
index 94f38d7..b619b22 100644 (file)
                                 | CF_PAGE_READABLE \
                                 | CF_PAGE_DIRTY)
 
-/*
- * Page protections for initialising protection_map. See mm/mmap.c
- * for use. In general, the bit positions are xwr, and P-items are
- * private, the S-items are shared.
- */
-#define __P000         PAGE_NONE
-#define __P001         __pgprot(CF_PAGE_VALID \
-                                | CF_PAGE_ACCESSED \
-                                | CF_PAGE_READABLE)
-#define __P010         __pgprot(CF_PAGE_VALID \
-                                | CF_PAGE_ACCESSED \
-                                | CF_PAGE_WRITABLE)
-#define __P011         __pgprot(CF_PAGE_VALID \
-                                | CF_PAGE_ACCESSED \
-                                | CF_PAGE_READABLE \
-                                | CF_PAGE_WRITABLE)
-#define __P100         __pgprot(CF_PAGE_VALID \
-                                | CF_PAGE_ACCESSED \
-                                | CF_PAGE_EXEC)
-#define __P101         __pgprot(CF_PAGE_VALID \
-                                | CF_PAGE_ACCESSED \
-                                | CF_PAGE_READABLE \
-                                | CF_PAGE_EXEC)
-#define __P110         __pgprot(CF_PAGE_VALID \
-                                | CF_PAGE_ACCESSED \
-                                | CF_PAGE_WRITABLE \
-                                | CF_PAGE_EXEC)
-#define __P111         __pgprot(CF_PAGE_VALID \
-                                | CF_PAGE_ACCESSED \
-                                | CF_PAGE_READABLE \
-                                | CF_PAGE_WRITABLE \
-                                | CF_PAGE_EXEC)
-
-#define __S000         PAGE_NONE
-#define __S001         __pgprot(CF_PAGE_VALID \
-                                | CF_PAGE_ACCESSED \
-                                | CF_PAGE_READABLE)
-#define __S010         PAGE_SHARED
-#define __S011         __pgprot(CF_PAGE_VALID \
-                                | CF_PAGE_ACCESSED \
-                                | CF_PAGE_SHARED \
-                                | CF_PAGE_READABLE)
-#define __S100         __pgprot(CF_PAGE_VALID \
-                                | CF_PAGE_ACCESSED \
-                                | CF_PAGE_EXEC)
-#define __S101         __pgprot(CF_PAGE_VALID \
-                                | CF_PAGE_ACCESSED \
-                                | CF_PAGE_READABLE \
-                                | CF_PAGE_EXEC)
-#define __S110         __pgprot(CF_PAGE_VALID \
-                                | CF_PAGE_ACCESSED \
-                                | CF_PAGE_SHARED \
-                                | CF_PAGE_EXEC)
-#define __S111         __pgprot(CF_PAGE_VALID \
-                                | CF_PAGE_ACCESSED \
-                                | CF_PAGE_SHARED \
-                                | CF_PAGE_READABLE \
-                                | CF_PAGE_EXEC)
-
 #define PTE_MASK       PAGE_MASK
 #define CF_PAGE_CHG_MASK (PTE_MASK | CF_PAGE_ACCESSED | CF_PAGE_DIRTY)
 
index 7c9b56e..7ac3d64 100644 (file)
@@ -76,35 +76,6 @@ extern unsigned long mm_cachebits;
 #define PAGE_READONLY  __pgprot(_PAGE_PRESENT | _PAGE_RONLY | _PAGE_ACCESSED | mm_cachebits)
 #define PAGE_KERNEL    __pgprot(_PAGE_PRESENT | _PAGE_DIRTY | _PAGE_ACCESSED | mm_cachebits)
 
-/* Alternate definitions that are compile time constants, for
-   initializing protection_map.  The cachebits are fixed later.  */
-#define PAGE_NONE_C    __pgprot(_PAGE_PROTNONE | _PAGE_ACCESSED)
-#define PAGE_SHARED_C  __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED)
-#define PAGE_COPY_C    __pgprot(_PAGE_PRESENT | _PAGE_RONLY | _PAGE_ACCESSED)
-#define PAGE_READONLY_C        __pgprot(_PAGE_PRESENT | _PAGE_RONLY | _PAGE_ACCESSED)
-
-/*
- * The m68k can't do page protection for execute, and considers that the same are read.
- * Also, write permissions imply read permissions. This is the closest we can get..
- */
-#define __P000 PAGE_NONE_C
-#define __P001 PAGE_READONLY_C
-#define __P010 PAGE_COPY_C
-#define __P011 PAGE_COPY_C
-#define __P100 PAGE_READONLY_C
-#define __P101 PAGE_READONLY_C
-#define __P110 PAGE_COPY_C
-#define __P111 PAGE_COPY_C
-
-#define __S000 PAGE_NONE_C
-#define __S001 PAGE_READONLY_C
-#define __S010 PAGE_SHARED_C
-#define __S011 PAGE_SHARED_C
-#define __S100 PAGE_READONLY_C
-#define __S101 PAGE_READONLY_C
-#define __S110 PAGE_SHARED_C
-#define __S111 PAGE_SHARED_C
-
 #define pmd_pgtable(pmd) ((pgtable_t)pmd_page_vaddr(pmd))
 
 /*
index 5e4e753..90d57e5 100644 (file)
                                 | SUN3_PAGE_SYSTEM \
                                 | SUN3_PAGE_NOCACHE)
 
-/*
- * Page protections for initialising protection_map. The sun3 has only two
- * protection settings, valid (implying read and execute) and writeable. These
- * are as close as we can get...
- */
-#define __P000 PAGE_NONE
-#define __P001 PAGE_READONLY
-#define __P010 PAGE_COPY
-#define __P011 PAGE_COPY
-#define __P100 PAGE_READONLY
-#define __P101 PAGE_READONLY
-#define __P110 PAGE_COPY
-#define __P111 PAGE_COPY
-
-#define __S000 PAGE_NONE
-#define __S001 PAGE_READONLY
-#define __S010 PAGE_SHARED
-#define __S011 PAGE_SHARED
-#define __S100 PAGE_READONLY
-#define __S101 PAGE_READONLY
-#define __S110 PAGE_SHARED
-#define __S111 PAGE_SHARED
-
 /* Use these fake page-protections on PMDs. */
 #define SUN3_PMD_VALID (0x00000001)
 #define SUN3_PMD_MASK  (0x0000003F)
index 71aa9f6..4d2837e 100644 (file)
@@ -141,6 +141,10 @@ good_area:
        if (fault_signal_pending(fault, regs))
                return 0;
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               return 0;
+
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
index 6f1f251..70aa097 100644 (file)
@@ -234,3 +234,58 @@ void steal_context(void)
        destroy_context(mm);
 }
 
+static const pgprot_t protection_map[16] = {
+       [VM_NONE]                                       = PAGE_NONE,
+       [VM_READ]                                       = __pgprot(CF_PAGE_VALID |
+                                                                  CF_PAGE_ACCESSED |
+                                                                  CF_PAGE_READABLE),
+       [VM_WRITE]                                      = __pgprot(CF_PAGE_VALID |
+                                                                  CF_PAGE_ACCESSED |
+                                                                  CF_PAGE_WRITABLE),
+       [VM_WRITE | VM_READ]                            = __pgprot(CF_PAGE_VALID |
+                                                                  CF_PAGE_ACCESSED |
+                                                                  CF_PAGE_READABLE |
+                                                                  CF_PAGE_WRITABLE),
+       [VM_EXEC]                                       = __pgprot(CF_PAGE_VALID |
+                                                                  CF_PAGE_ACCESSED |
+                                                                  CF_PAGE_EXEC),
+       [VM_EXEC | VM_READ]                             = __pgprot(CF_PAGE_VALID |
+                                                                  CF_PAGE_ACCESSED |
+                                                                  CF_PAGE_READABLE |
+                                                                  CF_PAGE_EXEC),
+       [VM_EXEC | VM_WRITE]                            = __pgprot(CF_PAGE_VALID |
+                                                                  CF_PAGE_ACCESSED |
+                                                                  CF_PAGE_WRITABLE |
+                                                                  CF_PAGE_EXEC),
+       [VM_EXEC | VM_WRITE | VM_READ]                  =  __pgprot(CF_PAGE_VALID |
+                                                                   CF_PAGE_ACCESSED |
+                                                                   CF_PAGE_READABLE |
+                                                                   CF_PAGE_WRITABLE |
+                                                                   CF_PAGE_EXEC),
+       [VM_SHARED]                                     = PAGE_NONE,
+       [VM_SHARED | VM_READ]                           = __pgprot(CF_PAGE_VALID |
+                                                                  CF_PAGE_ACCESSED |
+                                                                  CF_PAGE_READABLE),
+       [VM_SHARED | VM_WRITE]                          = PAGE_SHARED,
+       [VM_SHARED | VM_WRITE | VM_READ]                = __pgprot(CF_PAGE_VALID |
+                                                                  CF_PAGE_ACCESSED |
+                                                                  CF_PAGE_READABLE |
+                                                                  CF_PAGE_SHARED),
+       [VM_SHARED | VM_EXEC]                           = __pgprot(CF_PAGE_VALID |
+                                                                  CF_PAGE_ACCESSED |
+                                                                  CF_PAGE_EXEC),
+       [VM_SHARED | VM_EXEC | VM_READ]                 = __pgprot(CF_PAGE_VALID |
+                                                                  CF_PAGE_ACCESSED |
+                                                                  CF_PAGE_READABLE |
+                                                                  CF_PAGE_EXEC),
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = __pgprot(CF_PAGE_VALID |
+                                                                  CF_PAGE_ACCESSED |
+                                                                  CF_PAGE_SHARED |
+                                                                  CF_PAGE_EXEC),
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = __pgprot(CF_PAGE_VALID |
+                                                                  CF_PAGE_ACCESSED |
+                                                                  CF_PAGE_READABLE |
+                                                                  CF_PAGE_SHARED |
+                                                                  CF_PAGE_EXEC)
+};
+DECLARE_VM_GET_PAGE_PROT
index df7f797..2a37563 100644 (file)
@@ -382,6 +382,35 @@ static void __init map_node(int node)
 #endif
 }
 
+/*
+ * Alternate definitions that are compile time constants, for
+ * initializing protection_map.  The cachebits are fixed later.
+ */
+#define PAGE_NONE_C    __pgprot(_PAGE_PROTNONE | _PAGE_ACCESSED)
+#define PAGE_SHARED_C  __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED)
+#define PAGE_COPY_C    __pgprot(_PAGE_PRESENT | _PAGE_RONLY | _PAGE_ACCESSED)
+#define PAGE_READONLY_C        __pgprot(_PAGE_PRESENT | _PAGE_RONLY | _PAGE_ACCESSED)
+
+static pgprot_t protection_map[16] __ro_after_init = {
+       [VM_NONE]                                       = PAGE_NONE_C,
+       [VM_READ]                                       = PAGE_READONLY_C,
+       [VM_WRITE]                                      = PAGE_COPY_C,
+       [VM_WRITE | VM_READ]                            = PAGE_COPY_C,
+       [VM_EXEC]                                       = PAGE_READONLY_C,
+       [VM_EXEC | VM_READ]                             = PAGE_READONLY_C,
+       [VM_EXEC | VM_WRITE]                            = PAGE_COPY_C,
+       [VM_EXEC | VM_WRITE | VM_READ]                  = PAGE_COPY_C,
+       [VM_SHARED]                                     = PAGE_NONE_C,
+       [VM_SHARED | VM_READ]                           = PAGE_READONLY_C,
+       [VM_SHARED | VM_WRITE]                          = PAGE_SHARED_C,
+       [VM_SHARED | VM_WRITE | VM_READ]                = PAGE_SHARED_C,
+       [VM_SHARED | VM_EXEC]                           = PAGE_READONLY_C,
+       [VM_SHARED | VM_EXEC | VM_READ]                 = PAGE_READONLY_C,
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = PAGE_SHARED_C,
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = PAGE_SHARED_C
+};
+DECLARE_VM_GET_PAGE_PROT
+
 /*
  * paging_init() continues the virtual memory environment setup which
  * was begun by the code in arch/head.S.
index dad4942..b619d0d 100644 (file)
@@ -95,3 +95,23 @@ void __init paging_init(void)
 
 
 }
+
+static const pgprot_t protection_map[16] = {
+       [VM_NONE]                                       = PAGE_NONE,
+       [VM_READ]                                       = PAGE_READONLY,
+       [VM_WRITE]                                      = PAGE_COPY,
+       [VM_WRITE | VM_READ]                            = PAGE_COPY,
+       [VM_EXEC]                                       = PAGE_READONLY,
+       [VM_EXEC | VM_READ]                             = PAGE_READONLY,
+       [VM_EXEC | VM_WRITE]                            = PAGE_COPY,
+       [VM_EXEC | VM_WRITE | VM_READ]                  = PAGE_COPY,
+       [VM_SHARED]                                     = PAGE_NONE,
+       [VM_SHARED | VM_READ]                           = PAGE_READONLY,
+       [VM_SHARED | VM_WRITE]                          = PAGE_SHARED,
+       [VM_SHARED | VM_WRITE | VM_READ]                = PAGE_SHARED,
+       [VM_SHARED | VM_EXEC]                           = PAGE_READONLY,
+       [VM_SHARED | VM_EXEC | VM_READ]                 = PAGE_READONLY,
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = PAGE_SHARED,
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = PAGE_SHARED
+};
+DECLARE_VM_GET_PAGE_PROT
index 0c72646..ba348e9 100644 (file)
@@ -204,23 +204,6 @@ extern pte_t *va_to_pte(unsigned long address);
  * We consider execute permission the same as read.
  * Also, write permissions imply read permissions.
  */
-#define __P000 PAGE_NONE
-#define __P001 PAGE_READONLY_X
-#define __P010 PAGE_COPY
-#define __P011 PAGE_COPY_X
-#define __P100 PAGE_READONLY
-#define __P101 PAGE_READONLY_X
-#define __P110 PAGE_COPY
-#define __P111 PAGE_COPY_X
-
-#define __S000 PAGE_NONE
-#define __S001 PAGE_READONLY_X
-#define __S010 PAGE_SHARED
-#define __S011 PAGE_SHARED_X
-#define __S100 PAGE_READONLY
-#define __S101 PAGE_READONLY_X
-#define __S110 PAGE_SHARED
-#define __S111 PAGE_SHARED_X
 
 #ifndef __ASSEMBLY__
 /*
index a9626e6..5c40c3e 100644 (file)
@@ -222,6 +222,10 @@ good_area:
        if (fault_signal_pending(fault, regs))
                return;
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               return;
+
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
index f4e5034..353fabd 100644 (file)
@@ -285,3 +285,23 @@ void * __ref zalloc_maybe_bootmem(size_t size, gfp_t mask)
 
        return p;
 }
+
+static const pgprot_t protection_map[16] = {
+       [VM_NONE]                                       = PAGE_NONE,
+       [VM_READ]                                       = PAGE_READONLY_X,
+       [VM_WRITE]                                      = PAGE_COPY,
+       [VM_WRITE | VM_READ]                            = PAGE_COPY_X,
+       [VM_EXEC]                                       = PAGE_READONLY,
+       [VM_EXEC | VM_READ]                             = PAGE_READONLY_X,
+       [VM_EXEC | VM_WRITE]                            = PAGE_COPY,
+       [VM_EXEC | VM_WRITE | VM_READ]                  = PAGE_COPY_X,
+       [VM_SHARED]                                     = PAGE_NONE,
+       [VM_SHARED | VM_READ]                           = PAGE_READONLY_X,
+       [VM_SHARED | VM_WRITE]                          = PAGE_SHARED,
+       [VM_SHARED | VM_WRITE | VM_READ]                = PAGE_SHARED_X,
+       [VM_SHARED | VM_EXEC]                           = PAGE_READONLY,
+       [VM_SHARED | VM_EXEC | VM_READ]                 = PAGE_READONLY_X,
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = PAGE_SHARED,
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = PAGE_SHARED_X
+};
+DECLARE_VM_GET_PAGE_PROT
index 867e9c3..7960357 100644 (file)
@@ -51,7 +51,7 @@ extern pgd_t *pgd_alloc(struct mm_struct *mm);
 
 static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
 {
-       free_pages((unsigned long)pgd, PGD_ORDER);
+       free_pages((unsigned long)pgd, PGD_TABLE_ORDER);
 }
 
 #define __pte_free_tlb(tlb,pte,address)                        \
@@ -67,12 +67,12 @@ static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long address)
        pmd_t *pmd;
        struct page *pg;
 
-       pg = alloc_pages(GFP_KERNEL_ACCOUNT, PMD_ORDER);
+       pg = alloc_pages(GFP_KERNEL_ACCOUNT, PMD_TABLE_ORDER);
        if (!pg)
                return NULL;
 
        if (!pgtable_pmd_page_ctor(pg)) {
-               __free_pages(pg, PMD_ORDER);
+               __free_pages(pg, PMD_TABLE_ORDER);
                return NULL;
        }
 
@@ -91,7 +91,7 @@ static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long address)
 {
        pud_t *pud;
 
-       pud = (pud_t *) __get_free_pages(GFP_KERNEL, PUD_ORDER);
+       pud = (pud_t *) __get_free_pages(GFP_KERNEL, PUD_TABLE_ORDER);
        if (pud)
                pud_init((unsigned long)pud, (unsigned long)invalid_pmd_table);
        return pud;
index 95df9c2..495c603 100644 (file)
@@ -62,9 +62,9 @@ extern int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1,
 
 /* PGDIR_SHIFT determines what a third-level page table entry can map */
 #if defined(CONFIG_MIPS_HUGE_TLB_SUPPORT) && !defined(CONFIG_PHYS_ADDR_T_64BIT)
-# define PGDIR_SHIFT   (2 * PAGE_SHIFT + PTE_ORDER - PTE_T_LOG2 - 1)
+# define PGDIR_SHIFT   (2 * PAGE_SHIFT - PTE_T_LOG2 - 1)
 #else
-# define PGDIR_SHIFT   (2 * PAGE_SHIFT + PTE_ORDER - PTE_T_LOG2)
+# define PGDIR_SHIFT   (2 * PAGE_SHIFT - PTE_T_LOG2)
 #endif
 
 #define PGDIR_SIZE     (1UL << PGDIR_SHIFT)
@@ -75,21 +75,20 @@ extern int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1,
  * we don't really have any PUD/PMD directory physically.
  */
 #if defined(CONFIG_MIPS_HUGE_TLB_SUPPORT) && !defined(CONFIG_PHYS_ADDR_T_64BIT)
-# define __PGD_ORDER   (32 - 3 * PAGE_SHIFT + PGD_T_LOG2 + PTE_T_LOG2 + 1)
+# define __PGD_TABLE_ORDER (32 - 3 * PAGE_SHIFT + PGD_T_LOG2 + PTE_T_LOG2 + 1)
 #else
-# define __PGD_ORDER   (32 - 3 * PAGE_SHIFT + PGD_T_LOG2 + PTE_T_LOG2)
+# define __PGD_TABLE_ORDER (32 - 3 * PAGE_SHIFT + PGD_T_LOG2 + PTE_T_LOG2)
 #endif
 
-#define PGD_ORDER      (__PGD_ORDER >= 0 ? __PGD_ORDER : 0)
-#define PUD_ORDER      aieeee_attempt_to_allocate_pud
-#define PMD_ORDER      aieeee_attempt_to_allocate_pmd
-#define PTE_ORDER      0
+#define PGD_TABLE_ORDER        (__PGD_TABLE_ORDER >= 0 ? __PGD_TABLE_ORDER : 0)
+#define PUD_TABLE_ORDER        aieeee_attempt_to_allocate_pud
+#define PMD_TABLE_ORDER        aieeee_attempt_to_allocate_pmd
 
 #define PTRS_PER_PGD   (USER_PTRS_PER_PGD * 2)
 #if defined(CONFIG_MIPS_HUGE_TLB_SUPPORT) && !defined(CONFIG_PHYS_ADDR_T_64BIT)
-# define PTRS_PER_PTE  ((PAGE_SIZE << PTE_ORDER) / sizeof(pte_t) / 2)
+# define PTRS_PER_PTE  (PAGE_SIZE / sizeof(pte_t) / 2)
 #else
-# define PTRS_PER_PTE  ((PAGE_SIZE << PTE_ORDER) / sizeof(pte_t))
+# define PTRS_PER_PTE  (PAGE_SIZE / sizeof(pte_t))
 #endif
 
 #define USER_PTRS_PER_PGD      (0x80000000UL/PGDIR_SIZE)
index 41921ac..a259ca4 100644 (file)
 
 /* PGDIR_SHIFT determines what a third-level page table entry can map */
 #ifdef __PAGETABLE_PMD_FOLDED
-#define PGDIR_SHIFT    (PAGE_SHIFT + PAGE_SHIFT + PTE_ORDER - 3)
+#define PGDIR_SHIFT    (PAGE_SHIFT + PAGE_SHIFT - 3)
 #else
 
 /* PMD_SHIFT determines the size of the area a second-level page table can map */
-#define PMD_SHIFT      (PAGE_SHIFT + (PAGE_SHIFT + PTE_ORDER - 3))
+#define PMD_SHIFT      (PAGE_SHIFT + (PAGE_SHIFT - 3))
 #define PMD_SIZE       (1UL << PMD_SHIFT)
 #define PMD_MASK       (~(PMD_SIZE-1))
 
 # ifdef __PAGETABLE_PUD_FOLDED
-# define PGDIR_SHIFT   (PMD_SHIFT + (PAGE_SHIFT + PMD_ORDER - 3))
+# define PGDIR_SHIFT   (PMD_SHIFT + (PAGE_SHIFT + PMD_TABLE_ORDER - 3))
 # endif
 #endif
 
 #ifndef __PAGETABLE_PUD_FOLDED
-#define PUD_SHIFT      (PMD_SHIFT + (PAGE_SHIFT + PMD_ORDER - 3))
+#define PUD_SHIFT      (PMD_SHIFT + (PAGE_SHIFT + PMD_TABLE_ORDER - 3))
 #define PUD_SIZE       (1UL << PUD_SHIFT)
 #define PUD_MASK       (~(PUD_SIZE-1))
-#define PGDIR_SHIFT    (PUD_SHIFT + (PAGE_SHIFT + PUD_ORDER - 3))
+#define PGDIR_SHIFT    (PUD_SHIFT + (PAGE_SHIFT + PUD_TABLE_ORDER - 3))
 #endif
 
 #define PGDIR_SIZE     (1UL << PGDIR_SHIFT)
  */
 #ifdef CONFIG_PAGE_SIZE_4KB
 # ifdef CONFIG_MIPS_VA_BITS_48
-#  define PGD_ORDER            0
-#  define PUD_ORDER            0
+#  define PGD_TABLE_ORDER      0
+#  define PUD_TABLE_ORDER      0
 # else
-#  define PGD_ORDER            1
-#  define PUD_ORDER            aieeee_attempt_to_allocate_pud
+#  define PGD_TABLE_ORDER      1
+#  define PUD_TABLE_ORDER      aieeee_attempt_to_allocate_pud
 # endif
-#define PMD_ORDER              0
-#define PTE_ORDER              0
+#define PMD_TABLE_ORDER                0
 #endif
 #ifdef CONFIG_PAGE_SIZE_8KB
-#define PGD_ORDER              0
-#define PUD_ORDER              aieeee_attempt_to_allocate_pud
-#define PMD_ORDER              0
-#define PTE_ORDER              0
+#define PGD_TABLE_ORDER                0
+#define PUD_TABLE_ORDER                aieeee_attempt_to_allocate_pud
+#define PMD_TABLE_ORDER                0
 #endif
 #ifdef CONFIG_PAGE_SIZE_16KB
 #ifdef CONFIG_MIPS_VA_BITS_48
-#define PGD_ORDER               1
+#define PGD_TABLE_ORDER                1
 #else
-#define PGD_ORDER               0
+#define PGD_TABLE_ORDER                0
 #endif
-#define PUD_ORDER              aieeee_attempt_to_allocate_pud
-#define PMD_ORDER              0
-#define PTE_ORDER              0
+#define PUD_TABLE_ORDER                aieeee_attempt_to_allocate_pud
+#define PMD_TABLE_ORDER                0
 #endif
 #ifdef CONFIG_PAGE_SIZE_32KB
-#define PGD_ORDER              0
-#define PUD_ORDER              aieeee_attempt_to_allocate_pud
-#define PMD_ORDER              0
-#define PTE_ORDER              0
+#define PGD_TABLE_ORDER                0
+#define PUD_TABLE_ORDER                aieeee_attempt_to_allocate_pud
+#define PMD_TABLE_ORDER                0
 #endif
 #ifdef CONFIG_PAGE_SIZE_64KB
-#define PGD_ORDER              0
-#define PUD_ORDER              aieeee_attempt_to_allocate_pud
+#define PGD_TABLE_ORDER                0
+#define PUD_TABLE_ORDER                aieeee_attempt_to_allocate_pud
 #ifdef CONFIG_MIPS_VA_BITS_48
-#define PMD_ORDER              0
+#define PMD_TABLE_ORDER                0
 #else
-#define PMD_ORDER              aieeee_attempt_to_allocate_pmd
+#define PMD_TABLE_ORDER                aieeee_attempt_to_allocate_pmd
 #endif
-#define PTE_ORDER              0
 #endif
 
-#define PTRS_PER_PGD   ((PAGE_SIZE << PGD_ORDER) / sizeof(pgd_t))
+#define PTRS_PER_PGD   ((PAGE_SIZE << PGD_TABLE_ORDER) / sizeof(pgd_t))
 #ifndef __PAGETABLE_PUD_FOLDED
-#define PTRS_PER_PUD   ((PAGE_SIZE << PUD_ORDER) / sizeof(pud_t))
+#define PTRS_PER_PUD   ((PAGE_SIZE << PUD_TABLE_ORDER) / sizeof(pud_t))
 #endif
 #ifndef __PAGETABLE_PMD_FOLDED
-#define PTRS_PER_PMD   ((PAGE_SIZE << PMD_ORDER) / sizeof(pmd_t))
+#define PTRS_PER_PMD   ((PAGE_SIZE << PMD_TABLE_ORDER) / sizeof(pmd_t))
 #endif
-#define PTRS_PER_PTE   ((PAGE_SIZE << PTE_ORDER) / sizeof(pte_t))
+#define PTRS_PER_PTE   (PAGE_SIZE / sizeof(pte_t))
 
 #define USER_PTRS_PER_PGD       ((TASK_SIZE64 / PGDIR_SIZE)?(TASK_SIZE64 / PGDIR_SIZE):1)
 
index 374c632..6caec38 100644 (file)
@@ -41,28 +41,6 @@ struct vm_area_struct;
  * by reasonable means..
  */
 
-/*
- * Dummy values to fill the table in mmap.c
- * The real values will be generated at runtime
- */
-#define __P000 __pgprot(0)
-#define __P001 __pgprot(0)
-#define __P010 __pgprot(0)
-#define __P011 __pgprot(0)
-#define __P100 __pgprot(0)
-#define __P101 __pgprot(0)
-#define __P110 __pgprot(0)
-#define __P111 __pgprot(0)
-
-#define __S000 __pgprot(0)
-#define __S001 __pgprot(0)
-#define __S010 __pgprot(0)
-#define __S011 __pgprot(0)
-#define __S100 __pgprot(0)
-#define __S101 __pgprot(0)
-#define __S110 __pgprot(0)
-#define __S111 __pgprot(0)
-
 extern unsigned long _page_cachable_default;
 extern void __update_cache(unsigned long address, pte_t pte);
 
index 04ca752..c450189 100644 (file)
@@ -196,11 +196,6 @@ void output_mm_defines(void)
 #endif
        DEFINE(_PTE_T_LOG2, PTE_T_LOG2);
        BLANK();
-       DEFINE(_PGD_ORDER, PGD_ORDER);
-#ifndef __PAGETABLE_PMD_FOLDED
-       DEFINE(_PMD_ORDER, PMD_ORDER);
-#endif
-       DEFINE(_PTE_ORDER, PTE_ORDER);
        BLANK();
        DEFINE(_PMD_SHIFT, PMD_SHIFT);
        DEFINE(_PGDIR_SHIFT, PGDIR_SHIFT);
index d5f7362..dc023a9 100644 (file)
@@ -230,7 +230,7 @@ void mips_mt_set_cpuoptions(void)
 
 struct class *mt_class;
 
-static int __init mt_init(void)
+static int __init mips_mt_init(void)
 {
        struct class *mtc;
 
@@ -243,4 +243,4 @@ static int __init mt_init(void)
        return 0;
 }
 
-subsys_initcall(mt_init);
+subsys_initcall(mips_mt_init);
index 1bfd1b5..db17e87 100644 (file)
@@ -80,7 +80,7 @@ pgd_t *kvm_pgd_alloc(void)
 {
        pgd_t *ret;
 
-       ret = (pgd_t *)__get_free_pages(GFP_KERNEL, PGD_ORDER);
+       ret = (pgd_t *)__get_free_pages(GFP_KERNEL, PGD_TABLE_ORDER);
        if (ret)
                kvm_pgd_init(ret);
 
index 7be7240..11b3e7d 100644 (file)
@@ -159,6 +159,9 @@ EXPORT_SYMBOL(_page_cachable_default);
 
 #define PM(p)  __pgprot(_page_cachable_default | (p))
 
+static pgprot_t protection_map[16] __ro_after_init;
+DECLARE_VM_GET_PAGE_PROT
+
 static inline void setup_protection_map(void)
 {
        protection_map[0]  = PM(_PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_NO_READ);
index b08bc55..a27045f 100644 (file)
@@ -162,6 +162,10 @@ good_area:
                return;
        }
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               return;
+
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
index 05560b0..3b75906 100644 (file)
@@ -12,7 +12,7 @@ pgd_t *pgd_alloc(struct mm_struct *mm)
 {
        pgd_t *ret, *init;
 
-       ret = (pgd_t *) __get_free_pages(GFP_KERNEL, PGD_ORDER);
+       ret = (pgd_t *) __get_free_pages(GFP_KERNEL, PGD_TABLE_ORDER);
        if (ret) {
                init = pgd_offset(&init_mm, 0UL);
                pgd_init((unsigned long)ret);
index 8dbbd99..a57519a 100644 (file)
@@ -818,7 +818,7 @@ void build_get_pmde64(u32 **p, struct uasm_label **l, struct uasm_reloc **r,
                 * everything but the lower xuseg addresses goes down
                 * the module_alloc/vmalloc path.
                 */
-               uasm_i_dsrl_safe(p, ptr, tmp, PGDIR_SHIFT + PGD_ORDER + PAGE_SHIFT - 3);
+               uasm_i_dsrl_safe(p, ptr, tmp, PGDIR_SHIFT + PGD_TABLE_ORDER + PAGE_SHIFT - 3);
                uasm_il_bnez(p, r, ptr, label_vmalloc);
        } else {
                uasm_il_bltz(p, r, tmp, label_vmalloc);
@@ -1127,7 +1127,7 @@ build_fast_tlb_refill_handler (u32 **p, struct uasm_label **l,
                        UASM_i_SW(p, scratch, scratchpad_offset(0), 0);
 
                uasm_i_dsrl_safe(p, scratch, tmp,
-                                PGDIR_SHIFT + PGD_ORDER + PAGE_SHIFT - 3);
+                                PGDIR_SHIFT + PGD_TABLE_ORDER + PAGE_SHIFT - 3);
                uasm_il_bnez(p, r, scratch, label_vmalloc);
 
                if (pgd_reg == -1) {
@@ -1493,12 +1493,12 @@ static void setup_pw(void)
 #endif
        pgd_i = PGDIR_SHIFT;  /* 1st level PGD */
 #ifndef __PAGETABLE_PMD_FOLDED
-       pgd_w = PGDIR_SHIFT - PMD_SHIFT + PGD_ORDER;
+       pgd_w = PGDIR_SHIFT - PMD_SHIFT + PGD_TABLE_ORDER;
 
        pmd_i = PMD_SHIFT;    /* 2nd level PMD */
        pmd_w = PMD_SHIFT - PAGE_SHIFT;
 #else
-       pgd_w = PGDIR_SHIFT - PAGE_SHIFT + PGD_ORDER;
+       pgd_w = PGDIR_SHIFT - PAGE_SHIFT + PGD_TABLE_ORDER;
 #endif
 
        pt_i  = PAGE_SHIFT;    /* 3rd level PTE */
@@ -1536,7 +1536,7 @@ static void build_loongson3_tlb_refill_handler(void)
 
        if (check_for_high_segbits) {
                uasm_i_dmfc0(&p, K0, C0_BADVADDR);
-               uasm_i_dsrl_safe(&p, K1, K0, PGDIR_SHIFT + PGD_ORDER + PAGE_SHIFT - 3);
+               uasm_i_dsrl_safe(&p, K1, K0, PGDIR_SHIFT + PGD_TABLE_ORDER + PAGE_SHIFT - 3);
                uasm_il_beqz(&p, &r, K1, label_vmalloc);
                uasm_i_nop(&p);
 
@@ -2065,7 +2065,7 @@ build_r4000_tlbchange_handler_head(u32 **p, struct uasm_label **l,
 
        UASM_i_MFC0(p, wr.r1, C0_BADVADDR);
        UASM_i_LW(p, wr.r2, 0, wr.r2);
-       UASM_i_SRL(p, wr.r1, wr.r1, PAGE_SHIFT + PTE_ORDER - PTE_T_LOG2);
+       UASM_i_SRL(p, wr.r1, wr.r1, PAGE_SHIFT - PTE_T_LOG2);
        uasm_i_andi(p, wr.r1, wr.r1, (PTRS_PER_PTE - 1) << PTE_T_LOG2);
        UASM_i_ADDU(p, wr.r2, wr.r2, wr.r1);
 
@@ -2611,7 +2611,7 @@ void build_tlb_refill_handler(void)
        check_pabits();
 
 #ifdef CONFIG_64BIT
-       check_for_high_segbits = current_cpu_data.vmbits > (PGDIR_SHIFT + PGD_ORDER + PAGE_SHIFT - 3);
+       check_for_high_segbits = current_cpu_data.vmbits > (PGDIR_SHIFT + PGD_TABLE_ORDER + PAGE_SHIFT - 3);
 #endif
 
        if (cpu_has_3kex) {
index 262d060..b3d45e8 100644 (file)
@@ -40,24 +40,8 @@ struct mm_struct;
  */
 
 /* Remove W bit on private pages for COW support */
-#define __P000 MKP(0, 0, 0)
-#define __P001 MKP(0, 0, 1)
-#define __P010 MKP(0, 0, 0)    /* COW */
-#define __P011 MKP(0, 0, 1)    /* COW */
-#define __P100 MKP(1, 0, 0)
-#define __P101 MKP(1, 0, 1)
-#define __P110 MKP(1, 0, 0)    /* COW */
-#define __P111 MKP(1, 0, 1)    /* COW */
 
 /* Shared pages can have exact HW mapping */
-#define __S000 MKP(0, 0, 0)
-#define __S001 MKP(0, 0, 1)
-#define __S010 MKP(0, 1, 0)
-#define __S011 MKP(0, 1, 1)
-#define __S100 MKP(1, 0, 0)
-#define __S101 MKP(1, 0, 1)
-#define __S110 MKP(1, 1, 0)
-#define __S111 MKP(1, 1, 1)
 
 /* Used all over the kernel */
 #define PAGE_KERNEL __pgprot(_PAGE_PRESENT | _PAGE_CACHED | _PAGE_READ | \
@@ -68,11 +52,8 @@ struct mm_struct;
 
 #define PAGE_COPY MKP(0, 0, 1)
 
-#define PGD_ORDER      0
-#define PTE_ORDER      0
-
-#define PTRS_PER_PGD   ((PAGE_SIZE << PGD_ORDER) / sizeof(pgd_t))
-#define PTRS_PER_PTE   ((PAGE_SIZE << PTE_ORDER) / sizeof(pte_t))
+#define PTRS_PER_PGD   (PAGE_SIZE / sizeof(pgd_t))
+#define PTRS_PER_PTE   (PAGE_SIZE / sizeof(pte_t))
 
 #define USER_PTRS_PER_PGD      \
        (CONFIG_NIOS2_KERNEL_MMU_REGION_BASE / PGDIR_SIZE)
index a32f14c..edaca0a 100644 (file)
@@ -139,6 +139,10 @@ good_area:
        if (fault_signal_pending(fault, regs))
                return;
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               return;
+
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
index 613fcaa..7bc82ee 100644 (file)
@@ -78,9 +78,8 @@ void __init mmu_init(void)
        flush_tlb_all();
 }
 
-#define __page_aligned(order) __aligned(PAGE_SIZE << (order))
-pgd_t swapper_pg_dir[PTRS_PER_PGD] __page_aligned(PGD_ORDER);
-pte_t invalid_pte_table[PTRS_PER_PTE] __page_aligned(PTE_ORDER);
+pgd_t swapper_pg_dir[PTRS_PER_PGD] __aligned(PAGE_SIZE);
+pte_t invalid_pte_table[PTRS_PER_PTE] __aligned(PAGE_SIZE);
 static struct page *kuser_page[1];
 
 static int alloc_kuser_page(void)
@@ -124,3 +123,23 @@ const char *arch_vma_name(struct vm_area_struct *vma)
 {
        return (vma->vm_start == KUSER_BASE) ? "[kuser]" : NULL;
 }
+
+static const pgprot_t protection_map[16] = {
+       [VM_NONE]                                       = MKP(0, 0, 0),
+       [VM_READ]                                       = MKP(0, 0, 1),
+       [VM_WRITE]                                      = MKP(0, 0, 0),
+       [VM_WRITE | VM_READ]                            = MKP(0, 0, 1),
+       [VM_EXEC]                                       = MKP(1, 0, 0),
+       [VM_EXEC | VM_READ]                             = MKP(1, 0, 1),
+       [VM_EXEC | VM_WRITE]                            = MKP(1, 0, 0),
+       [VM_EXEC | VM_WRITE | VM_READ]                  = MKP(1, 0, 1),
+       [VM_SHARED]                                     = MKP(0, 0, 0),
+       [VM_SHARED | VM_READ]                           = MKP(0, 0, 1),
+       [VM_SHARED | VM_WRITE]                          = MKP(0, 1, 0),
+       [VM_SHARED | VM_WRITE | VM_READ]                = MKP(0, 1, 1),
+       [VM_SHARED | VM_EXEC]                           = MKP(1, 0, 0),
+       [VM_SHARED | VM_EXEC | VM_READ]                 = MKP(1, 0, 1),
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = MKP(1, 1, 0),
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = MKP(1, 1, 1)
+};
+DECLARE_VM_GET_PAGE_PROT
index 9b587fd..7c76e8a 100644 (file)
@@ -54,7 +54,7 @@ pgd_t *pgd_alloc(struct mm_struct *mm)
 {
        pgd_t *ret, *init;
 
-       ret = (pgd_t *) __get_free_pages(GFP_KERNEL, PGD_ORDER);
+       ret = (pgd_t *) __get_free_page(GFP_KERNEL);
        if (ret) {
                init = pgd_offset(&init_mm, 0UL);
                pgd_init(ret);
index c3abbf7..dcae8ae 100644 (file)
@@ -176,24 +176,6 @@ extern void paging_init(void);
        __pgprot(_PAGE_ALL | _PAGE_SRE | _PAGE_SWE \
                 | _PAGE_SHARED | _PAGE_DIRTY | _PAGE_EXEC | _PAGE_CI)
 
-#define __P000 PAGE_NONE
-#define __P001 PAGE_READONLY_X
-#define __P010 PAGE_COPY
-#define __P011 PAGE_COPY_X
-#define __P100 PAGE_READONLY
-#define __P101 PAGE_READONLY_X
-#define __P110 PAGE_COPY
-#define __P111 PAGE_COPY_X
-
-#define __S000 PAGE_NONE
-#define __S001 PAGE_READONLY_X
-#define __S010 PAGE_SHARED
-#define __S011 PAGE_SHARED_X
-#define __S100 PAGE_READONLY
-#define __S101 PAGE_READONLY_X
-#define __S110 PAGE_SHARED
-#define __S111 PAGE_SHARED_X
-
 /* zero page used for uninitialized stuff */
 extern unsigned long empty_zero_page[2048];
 #define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page))
index 53b760a..b4762d6 100644 (file)
@@ -165,6 +165,10 @@ good_area:
        if (fault_signal_pending(fault, regs))
                return;
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               return;
+
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
index 3a021ab..d531ab8 100644 (file)
@@ -208,3 +208,23 @@ void __init mem_init(void)
        mem_init_done = 1;
        return;
 }
+
+static const pgprot_t protection_map[16] = {
+       [VM_NONE]                                       = PAGE_NONE,
+       [VM_READ]                                       = PAGE_READONLY_X,
+       [VM_WRITE]                                      = PAGE_COPY,
+       [VM_WRITE | VM_READ]                            = PAGE_COPY_X,
+       [VM_EXEC]                                       = PAGE_READONLY,
+       [VM_EXEC | VM_READ]                             = PAGE_READONLY_X,
+       [VM_EXEC | VM_WRITE]                            = PAGE_COPY,
+       [VM_EXEC | VM_WRITE | VM_READ]                  = PAGE_COPY_X,
+       [VM_SHARED]                                     = PAGE_NONE,
+       [VM_SHARED | VM_READ]                           = PAGE_READONLY_X,
+       [VM_SHARED | VM_WRITE]                          = PAGE_SHARED,
+       [VM_SHARED | VM_WRITE | VM_READ]                = PAGE_SHARED_X,
+       [VM_SHARED | VM_EXEC]                           = PAGE_READONLY,
+       [VM_SHARED | VM_EXEC | VM_READ]                 = PAGE_READONLY_X,
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = PAGE_SHARED,
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = PAGE_SHARED_X
+};
+DECLARE_VM_GET_PAGE_PROT
index 54b6337..e3e142b 100644 (file)
@@ -20,18 +20,18 @@ static inline pgd_t *pgd_alloc(struct mm_struct *mm)
 {
        pgd_t *pgd;
 
-       pgd = (pgd_t *) __get_free_pages(GFP_KERNEL, PGD_ORDER);
+       pgd = (pgd_t *) __get_free_pages(GFP_KERNEL, PGD_TABLE_ORDER);
        if (unlikely(pgd == NULL))
                return NULL;
 
-       memset(pgd, 0, PAGE_SIZE << PGD_ORDER);
+       memset(pgd, 0, PAGE_SIZE << PGD_TABLE_ORDER);
 
        return pgd;
 }
 
 static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
 {
-       free_pages((unsigned long)pgd, PGD_ORDER);
+       free_pages((unsigned long)pgd, PGD_TABLE_ORDER);
 }
 
 #if CONFIG_PGTABLE_LEVELS == 3
index 69765a6..df7b931 100644 (file)
@@ -118,9 +118,9 @@ extern void __update_cache(pte_t pte);
 
 #if CONFIG_PGTABLE_LEVELS == 3
 #define PMD_TABLE_ORDER        1
-#define PGD_ORDER      0
+#define PGD_TABLE_ORDER        0
 #else
-#define PGD_ORDER      1
+#define PGD_TABLE_ORDER        1
 #endif
 
 /* Definitions for 3rd level (we use PLD here for Page Lower directory
@@ -144,10 +144,10 @@ extern void __update_cache(pte_t pte);
 
 /* Definitions for 1st level */
 #define PGDIR_SHIFT    (PLD_SHIFT + BITS_PER_PTE + BITS_PER_PMD)
-#if (PGDIR_SHIFT + PAGE_SHIFT + PGD_ORDER - BITS_PER_PGD_ENTRY) > BITS_PER_LONG
+#if (PGDIR_SHIFT + PAGE_SHIFT + PGD_TABLE_ORDER - BITS_PER_PGD_ENTRY) > BITS_PER_LONG
 #define BITS_PER_PGD   (BITS_PER_LONG - PGDIR_SHIFT)
 #else
-#define BITS_PER_PGD   (PAGE_SHIFT + PGD_ORDER - BITS_PER_PGD_ENTRY)
+#define BITS_PER_PGD   (PAGE_SHIFT + PGD_TABLE_ORDER - BITS_PER_PGD_ENTRY)
 #endif
 #define PGDIR_SIZE     (1UL << PGDIR_SHIFT)
 #define PGDIR_MASK     (~(PGDIR_SIZE-1))
@@ -271,24 +271,6 @@ extern void __update_cache(pte_t pte);
  */
 
         /*xwr*/
-#define __P000  PAGE_NONE
-#define __P001  PAGE_READONLY
-#define __P010  __P000 /* copy on write */
-#define __P011  __P001 /* copy on write */
-#define __P100  PAGE_EXECREAD
-#define __P101  PAGE_EXECREAD
-#define __P110  __P100 /* copy on write */
-#define __P111  __P101 /* copy on write */
-
-#define __S000  PAGE_NONE
-#define __S001  PAGE_READONLY
-#define __S010  PAGE_WRITEONLY
-#define __S011  PAGE_SHARED
-#define __S100  PAGE_EXECREAD
-#define __S101  PAGE_EXECREAD
-#define __S110  PAGE_RWX
-#define __S111  PAGE_RWX
-
 
 extern pgd_t swapper_pg_dir[]; /* declared in init_task.c */
 
index c9d48bc..869204e 100644 (file)
@@ -311,6 +311,10 @@ good_area:
        if (fault_signal_pending(fault, regs))
                return;
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               return;
+
        if (unlikely(fault & VM_FAULT_ERROR)) {
                /*
                 * We hit a shared mapping outside of the file, or some
index 8e2f7b8..b0c43f3 100644 (file)
@@ -871,3 +871,23 @@ void flush_tlb_all(void)
        spin_unlock(&sid_lock);
 }
 #endif
+
+static const pgprot_t protection_map[16] = {
+       [VM_NONE]                                       = PAGE_NONE,
+       [VM_READ]                                       = PAGE_READONLY,
+       [VM_WRITE]                                      = PAGE_NONE,
+       [VM_WRITE | VM_READ]                            = PAGE_READONLY,
+       [VM_EXEC]                                       = PAGE_EXECREAD,
+       [VM_EXEC | VM_READ]                             = PAGE_EXECREAD,
+       [VM_EXEC | VM_WRITE]                            = PAGE_EXECREAD,
+       [VM_EXEC | VM_WRITE | VM_READ]                  = PAGE_EXECREAD,
+       [VM_SHARED]                                     = PAGE_NONE,
+       [VM_SHARED | VM_READ]                           = PAGE_READONLY,
+       [VM_SHARED | VM_WRITE]                          = PAGE_WRITEONLY,
+       [VM_SHARED | VM_WRITE | VM_READ]                = PAGE_SHARED,
+       [VM_SHARED | VM_EXEC]                           = PAGE_EXECREAD,
+       [VM_SHARED | VM_EXEC | VM_READ]                 = PAGE_EXECREAD,
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = PAGE_RWX,
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = PAGE_RWX
+};
+DECLARE_VM_GET_PAGE_PROT
index 88a7381..af612d4 100644 (file)
@@ -140,7 +140,6 @@ config PPC
        select ARCH_HAS_TICK_BROADCAST          if GENERIC_CLOCKEVENTS_BROADCAST
        select ARCH_HAS_UACCESS_FLUSHCACHE
        select ARCH_HAS_UBSAN_SANITIZE_ALL
-       select ARCH_HAS_VM_GET_PAGE_PROT        if PPC_BOOK3S_64
        select ARCH_HAVE_NMI_SAFE_CMPXCHG
        select ARCH_KEEP_MEMBLOCK
        select ARCH_MIGHT_HAVE_PC_PARPORT
index cb9d5fd..392ff48 100644 (file)
@@ -1273,7 +1273,7 @@ static inline void pmdp_set_wrprotect(struct mm_struct *mm, unsigned long addr,
  * should return true.
  * We should not call this on a hugetlb entry. We should check for HugeTLB
  * entry using vma->vm_flags
- * The page table walk rule is explained in Documentation/vm/transhuge.rst
+ * The page table walk rule is explained in Documentation/mm/transhuge.rst
  */
 static inline int pmd_trans_huge(pmd_t pmd)
 {
index d564d0e..33f4bf8 100644 (file)
@@ -20,25 +20,6 @@ struct mm_struct;
 #include <asm/nohash/pgtable.h>
 #endif /* !CONFIG_PPC_BOOK3S */
 
-/* Note due to the way vm flags are laid out, the bits are XWR */
-#define __P000 PAGE_NONE
-#define __P001 PAGE_READONLY
-#define __P010 PAGE_COPY
-#define __P011 PAGE_COPY
-#define __P100 PAGE_READONLY_X
-#define __P101 PAGE_READONLY_X
-#define __P110 PAGE_COPY_X
-#define __P111 PAGE_COPY_X
-
-#define __S000 PAGE_NONE
-#define __S001 PAGE_READONLY
-#define __S010 PAGE_SHARED
-#define __S011 PAGE_SHARED
-#define __S100 PAGE_READONLY_X
-#define __S101 PAGE_READONLY_X
-#define __S110 PAGE_SHARED_X
-#define __S111 PAGE_SHARED_X
-
 #ifndef __ASSEMBLY__
 
 #ifndef MAX_PTRS_PER_PGD
@@ -79,6 +60,7 @@ extern void paging_init(void);
 void poking_init(void);
 
 extern unsigned long ioremap_bot;
+extern const pgprot_t protection_map[16];
 
 /*
  * kern_addr_valid is intended to indicate whether an address is a valid
index c1cb21a..7c507fb 100644 (file)
@@ -65,6 +65,11 @@ int copro_handle_mm_fault(struct mm_struct *mm, unsigned long ea,
 
        ret = 0;
        *flt = handle_mm_fault(vma, ea, is_write ? FAULT_FLAG_WRITE : 0, NULL);
+
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (*flt & VM_FAULT_COMPLETED)
+               return 0;
+
        if (unlikely(*flt & VM_FAULT_ERROR)) {
                if (*flt & VM_FAULT_OOM) {
                        ret = -ENOMEM;
index d53fed4..0140054 100644 (file)
@@ -511,6 +511,10 @@ retry:
        if (fault_signal_pending(fault, regs))
                return user_mode(regs) ? 0 : SIGBUS;
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               goto out;
+
        /*
         * Handle the retry right now, the mmap_lock has been released in that
         * case.
@@ -525,6 +529,7 @@ retry:
        if (unlikely(fault & VM_FAULT_ERROR))
                return mm_fault_error(regs, address, fault);
 
+out:
        /*
         * Major/minor page fault accounting.
         */
index e6166b7..cb2dcdb 100644 (file)
@@ -472,3 +472,27 @@ out:
        return ret_pte;
 }
 EXPORT_SYMBOL_GPL(__find_linux_pte);
+
+/* Note due to the way vm flags are laid out, the bits are XWR */
+const pgprot_t protection_map[16] = {
+       [VM_NONE]                                       = PAGE_NONE,
+       [VM_READ]                                       = PAGE_READONLY,
+       [VM_WRITE]                                      = PAGE_COPY,
+       [VM_WRITE | VM_READ]                            = PAGE_COPY,
+       [VM_EXEC]                                       = PAGE_READONLY_X,
+       [VM_EXEC | VM_READ]                             = PAGE_READONLY_X,
+       [VM_EXEC | VM_WRITE]                            = PAGE_COPY_X,
+       [VM_EXEC | VM_WRITE | VM_READ]                  = PAGE_COPY_X,
+       [VM_SHARED]                                     = PAGE_NONE,
+       [VM_SHARED | VM_READ]                           = PAGE_READONLY,
+       [VM_SHARED | VM_WRITE]                          = PAGE_SHARED,
+       [VM_SHARED | VM_WRITE | VM_READ]                = PAGE_SHARED,
+       [VM_SHARED | VM_EXEC]                           = PAGE_READONLY_X,
+       [VM_SHARED | VM_EXEC | VM_READ]                 = PAGE_READONLY_X,
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = PAGE_SHARED_X,
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = PAGE_SHARED_X
+};
+
+#ifndef CONFIG_PPC_BOOK3S_64
+DECLARE_VM_GET_PAGE_PROT
+#endif
index 5dbd661..7ec9369 100644 (file)
@@ -186,26 +186,6 @@ extern struct pt_alloc_ops pt_ops __initdata;
 
 extern pgd_t swapper_pg_dir[];
 
-/* MAP_PRIVATE permissions: xwr (copy-on-write) */
-#define __P000 PAGE_NONE
-#define __P001 PAGE_READ
-#define __P010 PAGE_COPY
-#define __P011 PAGE_COPY
-#define __P100 PAGE_EXEC
-#define __P101 PAGE_READ_EXEC
-#define __P110 PAGE_COPY_EXEC
-#define __P111 PAGE_COPY_READ_EXEC
-
-/* MAP_SHARED permissions: xwr */
-#define __S000 PAGE_NONE
-#define __S001 PAGE_READ
-#define __S010 PAGE_SHARED
-#define __S011 PAGE_SHARED
-#define __S100 PAGE_EXEC
-#define __S101 PAGE_READ_EXEC
-#define __S110 PAGE_SHARED_EXEC
-#define __S111 PAGE_SHARED_EXEC
-
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
 static inline int pmd_present(pmd_t pmd)
 {
index 40694f0..f2fbd14 100644 (file)
@@ -326,6 +326,10 @@ good_area:
        if (fault_signal_pending(fault, regs))
                return;
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               return;
+
        if (unlikely(fault & VM_FAULT_RETRY)) {
                flags |= FAULT_FLAG_TRIED;
 
index d466ec6..a88b7dc 100644 (file)
@@ -288,6 +288,26 @@ static pmd_t __maybe_unused early_dtb_pmd[PTRS_PER_PMD] __initdata __aligned(PAG
 #define early_pg_dir           ((pgd_t *)XIP_FIXUP(early_pg_dir))
 #endif /* CONFIG_XIP_KERNEL */
 
+static const pgprot_t protection_map[16] = {
+       [VM_NONE]                                       = PAGE_NONE,
+       [VM_READ]                                       = PAGE_READ,
+       [VM_WRITE]                                      = PAGE_COPY,
+       [VM_WRITE | VM_READ]                            = PAGE_COPY,
+       [VM_EXEC]                                       = PAGE_EXEC,
+       [VM_EXEC | VM_READ]                             = PAGE_READ_EXEC,
+       [VM_EXEC | VM_WRITE]                            = PAGE_COPY_EXEC,
+       [VM_EXEC | VM_WRITE | VM_READ]                  = PAGE_COPY_READ_EXEC,
+       [VM_SHARED]                                     = PAGE_NONE,
+       [VM_SHARED | VM_READ]                           = PAGE_READ,
+       [VM_SHARED | VM_WRITE]                          = PAGE_SHARED,
+       [VM_SHARED | VM_WRITE | VM_READ]                = PAGE_SHARED,
+       [VM_SHARED | VM_EXEC]                           = PAGE_EXEC,
+       [VM_SHARED | VM_EXEC | VM_READ]                 = PAGE_READ_EXEC,
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = PAGE_SHARED_EXEC,
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = PAGE_SHARED_EXEC
+};
+DECLARE_VM_GET_PAGE_PROT
+
 void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot)
 {
        unsigned long addr = __fix_to_virt(idx);
index cf81acf..f019df1 100644 (file)
@@ -424,23 +424,6 @@ static inline int is_module_addr(void *addr)
  * implies read permission.
  */
          /*xwr*/
-#define __P000 PAGE_NONE
-#define __P001 PAGE_RO
-#define __P010 PAGE_RO
-#define __P011 PAGE_RO
-#define __P100 PAGE_RX
-#define __P101 PAGE_RX
-#define __P110 PAGE_RX
-#define __P111 PAGE_RX
-
-#define __S000 PAGE_NONE
-#define __S001 PAGE_RO
-#define __S010 PAGE_RW
-#define __S011 PAGE_RW
-#define __S100 PAGE_RX
-#define __S101 PAGE_RX
-#define __S110 PAGE_RWX
-#define __S111 PAGE_RWX
 
 /*
  * Segment entry (large page) protection definitions.
index ee7871f..1344994 100644 (file)
@@ -433,6 +433,17 @@ retry:
                        goto out_up;
                goto out;
        }
+
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED) {
+               if (gmap) {
+                       mmap_read_lock(mm);
+                       goto out_gmap;
+               }
+               fault = 0;
+               goto out;
+       }
+
        if (unlikely(fault & VM_FAULT_ERROR))
                goto out_up;
 
@@ -452,6 +463,7 @@ retry:
                mmap_read_lock(mm);
                goto retry;
        }
+out_gmap:
        if (IS_ENABLED(CONFIG_PGSTE) && gmap) {
                address =  __gmap_link(gmap, current->thread.gmap_addr,
                                       address);
index d545f5c..5980ce3 100644 (file)
@@ -188,3 +188,23 @@ void arch_pick_mmap_layout(struct mm_struct *mm, struct rlimit *rlim_stack)
                mm->get_unmapped_area = arch_get_unmapped_area_topdown;
        }
 }
+
+static const pgprot_t protection_map[16] = {
+       [VM_NONE]                                       = PAGE_NONE,
+       [VM_READ]                                       = PAGE_RO,
+       [VM_WRITE]                                      = PAGE_RO,
+       [VM_WRITE | VM_READ]                            = PAGE_RO,
+       [VM_EXEC]                                       = PAGE_RX,
+       [VM_EXEC | VM_READ]                             = PAGE_RX,
+       [VM_EXEC | VM_WRITE]                            = PAGE_RX,
+       [VM_EXEC | VM_WRITE | VM_READ]                  = PAGE_RX,
+       [VM_SHARED]                                     = PAGE_NONE,
+       [VM_SHARED | VM_READ]                           = PAGE_RO,
+       [VM_SHARED | VM_WRITE]                          = PAGE_RW,
+       [VM_SHARED | VM_WRITE | VM_READ]                = PAGE_RW,
+       [VM_SHARED | VM_EXEC]                           = PAGE_RX,
+       [VM_SHARED | VM_EXEC | VM_READ]                 = PAGE_RX,
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = PAGE_RWX,
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = PAGE_RWX
+};
+DECLARE_VM_GET_PAGE_PROT
index d7ddb1e..6fb9ec5 100644 (file)
@@ -89,23 +89,6 @@ static inline unsigned long phys_addr_mask(void)
  * completely separate permission bits for user and kernel space.
  */
         /*xwr*/
-#define __P000 PAGE_NONE
-#define __P001 PAGE_READONLY
-#define __P010 PAGE_COPY
-#define __P011 PAGE_COPY
-#define __P100 PAGE_EXECREAD
-#define __P101 PAGE_EXECREAD
-#define __P110 PAGE_COPY
-#define __P111 PAGE_COPY
-
-#define __S000 PAGE_NONE
-#define __S001 PAGE_READONLY
-#define __S010 PAGE_WRITEONLY
-#define __S011 PAGE_SHARED
-#define __S100 PAGE_EXECREAD
-#define __S101 PAGE_EXECREAD
-#define __S110 PAGE_RWX
-#define __S111 PAGE_RWX
 
 typedef pte_t *pte_addr_t;
 
index e175667..acd2f5e 100644 (file)
@@ -485,6 +485,10 @@ good_area:
                if (mm_fault_error(regs, error_code, address, fault))
                        return;
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               return;
+
        if (fault & VM_FAULT_RETRY) {
                flags |= FAULT_FLAG_TRIED;
 
index 6a1a129..b821998 100644 (file)
@@ -19,6 +19,26 @@ unsigned long shm_align_mask = PAGE_SIZE - 1;        /* Sane caches */
 EXPORT_SYMBOL(shm_align_mask);
 
 #ifdef CONFIG_MMU
+static const pgprot_t protection_map[16] = {
+       [VM_NONE]                                       = PAGE_NONE,
+       [VM_READ]                                       = PAGE_READONLY,
+       [VM_WRITE]                                      = PAGE_COPY,
+       [VM_WRITE | VM_READ]                            = PAGE_COPY,
+       [VM_EXEC]                                       = PAGE_EXECREAD,
+       [VM_EXEC | VM_READ]                             = PAGE_EXECREAD,
+       [VM_EXEC | VM_WRITE]                            = PAGE_COPY,
+       [VM_EXEC | VM_WRITE | VM_READ]                  = PAGE_COPY,
+       [VM_SHARED]                                     = PAGE_NONE,
+       [VM_SHARED | VM_READ]                           = PAGE_READONLY,
+       [VM_SHARED | VM_WRITE]                          = PAGE_WRITEONLY,
+       [VM_SHARED | VM_WRITE | VM_READ]                = PAGE_SHARED,
+       [VM_SHARED | VM_EXEC]                           = PAGE_EXECREAD,
+       [VM_SHARED | VM_EXEC | VM_READ]                 = PAGE_EXECREAD,
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = PAGE_RWX,
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = PAGE_RWX
+};
+DECLARE_VM_GET_PAGE_PROT
+
 /*
  * To avoid cache aliases, we map the shared page with same color.
  */
index 02f0a60..1c852bb 100644 (file)
@@ -86,7 +86,6 @@ config SPARC64
        select PERF_USE_VMALLOC
        select ARCH_HAVE_NMI_SAFE_CMPXCHG
        select HAVE_C_RECORDMCOUNT
-       select ARCH_HAS_VM_GET_PAGE_PROT
        select HAVE_ARCH_AUDITSYSCALL
        select ARCH_SUPPORTS_ATOMIC_RMW
        select ARCH_SUPPORTS_DEBUG_PAGEALLOC
index 4866625..8ff5490 100644 (file)
@@ -64,25 +64,6 @@ void paging_init(void);
 
 extern unsigned long ptr_in_current_pgd;
 
-/*         xwr */
-#define __P000  PAGE_NONE
-#define __P001  PAGE_READONLY
-#define __P010  PAGE_COPY
-#define __P011  PAGE_COPY
-#define __P100  PAGE_READONLY
-#define __P101  PAGE_READONLY
-#define __P110  PAGE_COPY
-#define __P111  PAGE_COPY
-
-#define __S000 PAGE_NONE
-#define __S001 PAGE_READONLY
-#define __S010 PAGE_SHARED
-#define __S011 PAGE_SHARED
-#define __S100 PAGE_READONLY
-#define __S101 PAGE_READONLY
-#define __S110 PAGE_SHARED
-#define __S111 PAGE_SHARED
-
 /* First physical page can be anywhere, the following is needed so that
  * va-->pa and vice versa conversions work properly without performance
  * hit for all __pa()/__va() operations.
index 4679e45..a779418 100644 (file)
@@ -187,25 +187,6 @@ bool kern_addr_valid(unsigned long addr);
 #define _PAGE_SZHUGE_4U        _PAGE_SZ4MB_4U
 #define _PAGE_SZHUGE_4V        _PAGE_SZ4MB_4V
 
-/* These are actually filled in at boot time by sun4{u,v}_pgprot_init() */
-#define __P000 __pgprot(0)
-#define __P001 __pgprot(0)
-#define __P010 __pgprot(0)
-#define __P011 __pgprot(0)
-#define __P100 __pgprot(0)
-#define __P101 __pgprot(0)
-#define __P110 __pgprot(0)
-#define __P111 __pgprot(0)
-
-#define __S000 __pgprot(0)
-#define __S001 __pgprot(0)
-#define __S010 __pgprot(0)
-#define __S011 __pgprot(0)
-#define __S100 __pgprot(0)
-#define __S101 __pgprot(0)
-#define __S110 __pgprot(0)
-#define __S111 __pgprot(0)
-
 #ifndef __ASSEMBLY__
 
 pte_t mk_pte_io(unsigned long, pgprot_t, int, unsigned long);
index ad569d9..91259f2 100644 (file)
@@ -190,6 +190,10 @@ good_area:
        if (fault_signal_pending(fault, regs))
                return;
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               return;
+
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
index 253e070..4acc12e 100644 (file)
@@ -427,6 +427,10 @@ good_area:
        if (fault_signal_pending(fault, regs))
                goto exit_exception;
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               goto lock_released;
+
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
@@ -449,6 +453,7 @@ good_area:
        }
        mmap_read_unlock(mm);
 
+lock_released:
        mm_rss = get_mm_rss(mm);
 #if defined(CONFIG_TRANSPARENT_HUGEPAGE)
        mm_rss -= (mm->context.thp_pte_count * (HPAGE_SIZE / PAGE_SIZE));
index 1e9f577..d88e774 100644 (file)
@@ -302,3 +302,23 @@ void sparc_flush_page_to_ram(struct page *page)
                __flush_page_to_ram(vaddr);
 }
 EXPORT_SYMBOL(sparc_flush_page_to_ram);
+
+static const pgprot_t protection_map[16] = {
+       [VM_NONE]                                       = PAGE_NONE,
+       [VM_READ]                                       = PAGE_READONLY,
+       [VM_WRITE]                                      = PAGE_COPY,
+       [VM_WRITE | VM_READ]                            = PAGE_COPY,
+       [VM_EXEC]                                       = PAGE_READONLY,
+       [VM_EXEC | VM_READ]                             = PAGE_READONLY,
+       [VM_EXEC | VM_WRITE]                            = PAGE_COPY,
+       [VM_EXEC | VM_WRITE | VM_READ]                  = PAGE_COPY,
+       [VM_SHARED]                                     = PAGE_NONE,
+       [VM_SHARED | VM_READ]                           = PAGE_READONLY,
+       [VM_SHARED | VM_WRITE]                          = PAGE_SHARED,
+       [VM_SHARED | VM_WRITE | VM_READ]                = PAGE_SHARED,
+       [VM_SHARED | VM_EXEC]                           = PAGE_READONLY,
+       [VM_SHARED | VM_EXEC | VM_READ]                 = PAGE_READONLY,
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = PAGE_SHARED,
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = PAGE_SHARED
+};
+DECLARE_VM_GET_PAGE_PROT
index f6174df..d6faee2 100644 (file)
@@ -2634,6 +2634,9 @@ void vmemmap_free(unsigned long start, unsigned long end,
 }
 #endif /* CONFIG_SPARSEMEM_VMEMMAP */
 
+/* These are actually filled in at boot time by sun4{u,v}_pgprot_init() */
+static pgprot_t protection_map[16] __ro_after_init;
+
 static void prot_init_common(unsigned long page_none,
                             unsigned long page_shared,
                             unsigned long page_copy,
index 167e236..66bc3f9 100644 (file)
@@ -68,23 +68,6 @@ extern unsigned long end_iomem;
  * Also, write permissions imply read permissions. This is the closest we can
  * get..
  */
-#define __P000 PAGE_NONE
-#define __P001 PAGE_READONLY
-#define __P010 PAGE_COPY
-#define __P011 PAGE_COPY
-#define __P100 PAGE_READONLY
-#define __P101 PAGE_READONLY
-#define __P110 PAGE_COPY
-#define __P111 PAGE_COPY
-
-#define __S000 PAGE_NONE
-#define __S001 PAGE_READONLY
-#define __S010 PAGE_SHARED
-#define __S011 PAGE_SHARED
-#define __S100 PAGE_READONLY
-#define __S101 PAGE_READONLY
-#define __S110 PAGE_SHARED
-#define __S111 PAGE_SHARED
 
 /*
  * ZERO_PAGE is a global shared page that is always zero: used
index 276a1f0..38d5a71 100644 (file)
@@ -216,3 +216,23 @@ void *uml_kmalloc(int size, int flags)
 {
        return kmalloc(size, flags);
 }
+
+static const pgprot_t protection_map[16] = {
+       [VM_NONE]                                       = PAGE_NONE,
+       [VM_READ]                                       = PAGE_READONLY,
+       [VM_WRITE]                                      = PAGE_COPY,
+       [VM_WRITE | VM_READ]                            = PAGE_COPY,
+       [VM_EXEC]                                       = PAGE_READONLY,
+       [VM_EXEC | VM_READ]                             = PAGE_READONLY,
+       [VM_EXEC | VM_WRITE]                            = PAGE_COPY,
+       [VM_EXEC | VM_WRITE | VM_READ]                  = PAGE_COPY,
+       [VM_SHARED]                                     = PAGE_NONE,
+       [VM_SHARED | VM_READ]                           = PAGE_READONLY,
+       [VM_SHARED | VM_WRITE]                          = PAGE_SHARED,
+       [VM_SHARED | VM_WRITE | VM_READ]                = PAGE_SHARED,
+       [VM_SHARED | VM_EXEC]                           = PAGE_READONLY,
+       [VM_SHARED | VM_EXEC | VM_READ]                 = PAGE_READONLY,
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = PAGE_SHARED,
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = PAGE_SHARED
+};
+DECLARE_VM_GET_PAGE_PROT
index d1d5d0b..d3ce21c 100644 (file)
@@ -76,6 +76,10 @@ good_area:
                if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
                        goto out_nosemaphore;
 
+               /* The fault is fully completed (including releasing mmap lock) */
+               if (fault & VM_FAULT_COMPLETED)
+                       return 0;
+
                if (unlikely(fault & VM_FAULT_ERROR)) {
                        if (fault & VM_FAULT_OOM) {
                                goto out_of_memory;
index cca4c0a..f9920f1 100644 (file)
@@ -94,7 +94,6 @@ config X86
        select ARCH_HAS_SYNC_CORE_BEFORE_USERMODE
        select ARCH_HAS_SYSCALL_WRAPPER
        select ARCH_HAS_UBSAN_SANITIZE_ALL
-       select ARCH_HAS_VM_GET_PAGE_PROT
        select ARCH_HAS_DEBUG_WX
        select ARCH_HAS_ZONE_DMA_SET if EXPERT
        select ARCH_HAVE_NMI_SAFE_CMPXCHG
index 88ceaf3..72ca905 100644 (file)
@@ -89,6 +89,8 @@ static inline void mem_encrypt_free_decrypted_mem(void) { }
 /* Architecture __weak replacement functions */
 void __init mem_encrypt_init(void);
 
+void add_encrypt_protection_map(void);
+
 /*
  * The __sme_pa() and __sme_pa_nodebug() macros are meant for use when
  * writing to or comparing values from the cr3 register.  Having the
index bdaf839..aa174fe 100644 (file)
@@ -230,25 +230,6 @@ enum page_cache_mode {
 
 #endif /* __ASSEMBLY__ */
 
-/*         xwr */
-#define __P000 PAGE_NONE
-#define __P001 PAGE_READONLY
-#define __P010 PAGE_COPY
-#define __P011 PAGE_COPY
-#define __P100 PAGE_READONLY_EXEC
-#define __P101 PAGE_READONLY_EXEC
-#define __P110 PAGE_COPY_EXEC
-#define __P111 PAGE_COPY_EXEC
-
-#define __S000 PAGE_NONE
-#define __S001 PAGE_READONLY
-#define __S010 PAGE_SHARED
-#define __S011 PAGE_SHARED
-#define __S100 PAGE_READONLY_EXEC
-#define __S101 PAGE_READONLY_EXEC
-#define __S110 PAGE_SHARED_EXEC
-#define __S111 PAGE_SHARED_EXEC
-
 /*
  * early identity mapping  pte attrib macros.
  */
index 4236a28..06ac8c7 100644 (file)
@@ -6740,7 +6740,7 @@ int kvm_mmu_vendor_module_init(void)
        if (percpu_counter_init(&kvm_total_used_mmu_pages, 0, GFP_KERNEL))
                goto out;
 
-       ret = register_shrinker(&mmu_shrinker);
+       ret = register_shrinker(&mmu_shrinker, "x86-mmu");
        if (ret)
                goto out;
 
index 971977c..fa71a5d 100644 (file)
@@ -1408,6 +1408,10 @@ good_area:
                return;
        }
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               return;
+
        /*
         * If we need to retry the mmap_lock has already been released,
         * and if there is a fatal signal pending there is no guarantee
index a0d023c..509408d 100644 (file)
 #include <asm/tlbflush.h>
 #include <asm/elf.h>
 
-#if 0  /* This is just for testing */
-struct page *
-follow_huge_addr(struct mm_struct *mm, unsigned long address, int write)
-{
-       unsigned long start = address;
-       int length = 1;
-       int nr;
-       struct page *page;
-       struct vm_area_struct *vma;
-
-       vma = find_vma(mm, addr);
-       if (!vma || !is_vm_hugetlb_page(vma))
-               return ERR_PTR(-EINVAL);
-
-       pte = huge_pte_offset(mm, address, vma_mmu_pagesize(vma));
-
-       /* hugetlb should be locked, and hence, prefaulted */
-       WARN_ON(!pte || pte_none(*pte));
-
-       page = &pte_page(*pte)[vpfn % (HPAGE_SIZE/PAGE_SIZE)];
-
-       WARN_ON(!PageHead(page));
-
-       return page;
-}
-
-int pmd_huge(pmd_t pmd)
-{
-       return 0;
-}
-
-int pud_huge(pud_t pud)
-{
-       return 0;
-}
-
-#else
-
 /*
  * pmd_huge() returns 1 if @pmd is hugetlb related entry, that is normal
  * hugetlb entry or non-present (migration or hwpoisoned) hugetlb entry.
@@ -72,7 +34,6 @@ int pud_huge(pud_t pud)
 {
        return !!(pud_val(pud) & _PAGE_PSE);
 }
-#endif
 
 #ifdef CONFIG_HUGETLB_PAGE
 static unsigned long hugetlb_get_unmapped_area_bottomup(struct file *file,
index 9745268..9c4d8db 100644 (file)
@@ -26,6 +26,7 @@
 #include <asm/tlbflush.h>
 #include <asm/fixmap.h>
 #include <asm/setup.h>
+#include <asm/mem_encrypt.h>
 #include <asm/bootparam.h>
 #include <asm/set_memory.h>
 #include <asm/cacheflush.h>
@@ -486,8 +487,6 @@ void __init early_set_mem_enc_dec_hypercall(unsigned long vaddr, int npages, boo
 
 void __init sme_early_init(void)
 {
-       unsigned int i;
-
        if (!sme_me_mask)
                return;
 
@@ -496,8 +495,7 @@ void __init sme_early_init(void)
        __supported_pte_mask = __sme_set(__supported_pte_mask);
 
        /* Update the protection map with memory encryption mask */
-       for (i = 0; i < ARRAY_SIZE(protection_map); i++)
-               protection_map[i] = pgprot_encrypted(protection_map[i]);
+       add_encrypt_protection_map();
 
        x86_platform.guest.enc_status_change_prepare = amd_enc_status_change_prepare;
        x86_platform.guest.enc_status_change_finish  = amd_enc_status_change_finish;
index 7637427..c84bd95 100644 (file)
@@ -3,6 +3,34 @@
 #include <linux/export.h>
 #include <linux/mm.h>
 #include <asm/pgtable.h>
+#include <asm/mem_encrypt.h>
+
+static pgprot_t protection_map[16] __ro_after_init = {
+       [VM_NONE]                                       = PAGE_NONE,
+       [VM_READ]                                       = PAGE_READONLY,
+       [VM_WRITE]                                      = PAGE_COPY,
+       [VM_WRITE | VM_READ]                            = PAGE_COPY,
+       [VM_EXEC]                                       = PAGE_READONLY_EXEC,
+       [VM_EXEC | VM_READ]                             = PAGE_READONLY_EXEC,
+       [VM_EXEC | VM_WRITE]                            = PAGE_COPY_EXEC,
+       [VM_EXEC | VM_WRITE | VM_READ]                  = PAGE_COPY_EXEC,
+       [VM_SHARED]                                     = PAGE_NONE,
+       [VM_SHARED | VM_READ]                           = PAGE_READONLY,
+       [VM_SHARED | VM_WRITE]                          = PAGE_SHARED,
+       [VM_SHARED | VM_WRITE | VM_READ]                = PAGE_SHARED,
+       [VM_SHARED | VM_EXEC]                           = PAGE_READONLY_EXEC,
+       [VM_SHARED | VM_EXEC | VM_READ]                 = PAGE_READONLY_EXEC,
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = PAGE_SHARED_EXEC,
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = PAGE_SHARED_EXEC
+};
+
+void add_encrypt_protection_map(void)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(protection_map); i++)
+               protection_map[i] = pgprot_encrypted(protection_map[i]);
+}
 
 pgprot_t vm_get_page_prot(unsigned long vm_flags)
 {
index 19c5dbd..cafd01f 100644 (file)
@@ -17,7 +17,7 @@ static int __init gate_vma_init(void)
        gate_vma.vm_start = FIXADDR_USER_START;
        gate_vma.vm_end = FIXADDR_USER_END;
        gate_vma.vm_flags = VM_READ | VM_MAYREAD | VM_EXEC | VM_MAYEXEC;
-       gate_vma.vm_page_prot = __P101;
+       gate_vma.vm_page_prot = PAGE_READONLY;
 
        return 0;
 }
index eeb2de3..7fc0f91 100644 (file)
@@ -29,7 +29,7 @@
 static inline pgd_t*
 pgd_alloc(struct mm_struct *mm)
 {
-       return (pgd_t*) __get_free_pages(GFP_KERNEL | __GFP_ZERO, PGD_ORDER);
+       return (pgd_t*) __get_free_page(GFP_KERNEL | __GFP_ZERO);
 }
 
 static inline void ptes_clear(pte_t *ptep)
index 0a91376..54f577c 100644 (file)
@@ -57,7 +57,6 @@
 #define PTRS_PER_PTE           1024
 #define PTRS_PER_PTE_SHIFT     10
 #define PTRS_PER_PGD           1024
-#define PGD_ORDER              0
 #define USER_PTRS_PER_PGD      (TASK_SIZE/PGDIR_SIZE)
 #define FIRST_USER_PGD_NR      (FIRST_USER_ADDRESS >> PGDIR_SHIFT)
 
  * What follows is the closest we can get by reasonable means..
  * See linux/mm/mmap.c for protection_map[] array that uses these definitions.
  */
-#define __P000 PAGE_NONE               /* private --- */
-#define __P001 PAGE_READONLY           /* private --r */
-#define __P010 PAGE_COPY               /* private -w- */
-#define __P011 PAGE_COPY               /* private -wr */
-#define __P100 PAGE_READONLY_EXEC      /* private x-- */
-#define __P101 PAGE_READONLY_EXEC      /* private x-r */
-#define __P110 PAGE_COPY_EXEC          /* private xw- */
-#define __P111 PAGE_COPY_EXEC          /* private xwr */
-
-#define __S000 PAGE_NONE               /* shared  --- */
-#define __S001 PAGE_READONLY           /* shared  --r */
-#define __S010 PAGE_SHARED             /* shared  -w- */
-#define __S011 PAGE_SHARED             /* shared  -wr */
-#define __S100 PAGE_READONLY_EXEC      /* shared  x-- */
-#define __S101 PAGE_READONLY_EXEC      /* shared  x-r */
-#define __S110 PAGE_SHARED_EXEC        /* shared  xw- */
-#define __S111 PAGE_SHARED_EXEC        /* shared  xwr */
-
 #ifndef __ASSEMBLY__
 
 #define pte_ERROR(e) \
index 16f0a5f..8c781b0 100644 (file)
@@ -172,6 +172,10 @@ good_area:
                return;
        }
 
+       /* The fault is fully completed (including releasing mmap lock) */
+       if (fault & VM_FAULT_COMPLETED)
+               return;
+
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
index 6a32b2c..b2587a1 100644 (file)
@@ -216,3 +216,25 @@ static int __init parse_memmap_opt(char *str)
        return 0;
 }
 early_param("memmap", parse_memmap_opt);
+
+#ifdef CONFIG_MMU
+static const pgprot_t protection_map[16] = {
+       [VM_NONE]                                       = PAGE_NONE,
+       [VM_READ]                                       = PAGE_READONLY,
+       [VM_WRITE]                                      = PAGE_COPY,
+       [VM_WRITE | VM_READ]                            = PAGE_COPY,
+       [VM_EXEC]                                       = PAGE_READONLY_EXEC,
+       [VM_EXEC | VM_READ]                             = PAGE_READONLY_EXEC,
+       [VM_EXEC | VM_WRITE]                            = PAGE_COPY_EXEC,
+       [VM_EXEC | VM_WRITE | VM_READ]                  = PAGE_COPY_EXEC,
+       [VM_SHARED]                                     = PAGE_NONE,
+       [VM_SHARED | VM_READ]                           = PAGE_READONLY,
+       [VM_SHARED | VM_WRITE]                          = PAGE_SHARED,
+       [VM_SHARED | VM_WRITE | VM_READ]                = PAGE_SHARED,
+       [VM_SHARED | VM_EXEC]                           = PAGE_READONLY_EXEC,
+       [VM_SHARED | VM_EXEC | VM_READ]                 = PAGE_READONLY_EXEC,
+       [VM_SHARED | VM_EXEC | VM_WRITE]                = PAGE_SHARED_EXEC,
+       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = PAGE_SHARED_EXEC
+};
+DECLARE_VM_GET_PAGE_PROT
+#endif
index 5649a03..1014beb 100644 (file)
@@ -213,7 +213,7 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
 
        if (mm) {
                mmap_read_lock(mm);
-               vma = alloc->vma;
+               vma = vma_lookup(mm, alloc->vma_addr);
        }
 
        if (!vma && need_mm) {
@@ -313,16 +313,22 @@ err_no_vma:
 static inline void binder_alloc_set_vma(struct binder_alloc *alloc,
                struct vm_area_struct *vma)
 {
-       if (vma)
-               alloc->vma_vm_mm = vma->vm_mm;
+       unsigned long vm_start = 0;
+
        /*
-        * If we see alloc->vma is not NULL, buffer data structures set up
-        * completely. Look at smp_rmb side binder_alloc_get_vma.
-        * We also want to guarantee new alloc->vma_vm_mm is always visible
-        * if alloc->vma is set.
+        * Allow clearing the vma with holding just the read lock to allow
+        * munmapping downgrade of the write lock before freeing and closing the
+        * file using binder_alloc_vma_close().
         */
-       smp_wmb();
-       alloc->vma = vma;
+       if (vma) {
+               vm_start = vma->vm_start;
+               alloc->vma_vm_mm = vma->vm_mm;
+               mmap_assert_write_locked(alloc->vma_vm_mm);
+       } else {
+               mmap_assert_locked(alloc->vma_vm_mm);
+       }
+
+       alloc->vma_addr = vm_start;
 }
 
 static inline struct vm_area_struct *binder_alloc_get_vma(
@@ -330,11 +336,9 @@ static inline struct vm_area_struct *binder_alloc_get_vma(
 {
        struct vm_area_struct *vma = NULL;
 
-       if (alloc->vma) {
-               /* Look at description in binder_alloc_set_vma */
-               smp_rmb();
-               vma = alloc->vma;
-       }
+       if (alloc->vma_addr)
+               vma = vma_lookup(alloc->vma_vm_mm, alloc->vma_addr);
+
        return vma;
 }
 
@@ -817,7 +821,8 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc)
 
        buffers = 0;
        mutex_lock(&alloc->mutex);
-       BUG_ON(alloc->vma);
+       BUG_ON(alloc->vma_addr &&
+              vma_lookup(alloc->vma_vm_mm, alloc->vma_addr));
 
        while ((n = rb_first(&alloc->allocated_buffers))) {
                buffer = rb_entry(n, struct binder_buffer, rb_node);
@@ -1084,7 +1089,7 @@ int binder_alloc_shrinker_init(void)
        int ret = list_lru_init(&binder_alloc_lru);
 
        if (ret == 0) {
-               ret = register_shrinker(&binder_shrinker);
+               ret = register_shrinker(&binder_shrinker, "android-binder");
                if (ret)
                        list_lru_destroy(&binder_alloc_lru);
        }
index 7dea57a..1e4fd37 100644 (file)
@@ -100,7 +100,7 @@ struct binder_lru_page {
  */
 struct binder_alloc {
        struct mutex mutex;
-       struct vm_area_struct *vma;
+       unsigned long vma_addr;
        struct mm_struct *vma_vm_mm;
        void __user *buffer;
        struct list_head buffers;
index c2b323b..43a8810 100644 (file)
@@ -287,7 +287,7 @@ void binder_selftest_alloc(struct binder_alloc *alloc)
        if (!binder_selftest_run)
                return;
        mutex_lock(&binder_selftest_lock);
-       if (!binder_selftest_run || !alloc->vma)
+       if (!binder_selftest_run || !alloc->vma_addr)
                goto done;
        pr_info("STARTED\n");
        binder_selftest_alloc_offset(alloc, end_offset, 0);
index 052aa3f..0916de9 100644 (file)
@@ -63,12 +63,6 @@ static int zcomp_strm_init(struct zcomp_strm *zstrm, struct zcomp *comp)
 
 bool zcomp_available_algorithm(const char *comp)
 {
-       int i;
-
-       i = sysfs_match_string(backends, comp);
-       if (i >= 0)
-               return true;
-
        /*
         * Crypto does not ignore a trailing new line symbol,
         * so make sure you don't supply a string containing
@@ -217,6 +211,11 @@ struct zcomp *zcomp_create(const char *compress)
        struct zcomp *comp;
        int error;
 
+       /*
+        * Crypto API will execute /sbin/modprobe if the compression module
+        * is not loaded yet. We must do it here, otherwise we are about to
+        * call /sbin/modprobe under CPU hot-plug lock.
+        */
        if (!zcomp_available_algorithm(compress))
                return ERR_PTR(-EINVAL);
 
index 4abeb26..92cb929 100644 (file)
@@ -52,7 +52,9 @@ static unsigned int num_devices = 1;
 static size_t huge_class_size;
 
 static const struct block_device_operations zram_devops;
+#ifdef CONFIG_ZRAM_WRITEBACK
 static const struct block_device_operations zram_wb_devops;
+#endif
 
 static void zram_free_page(struct zram *zram, size_t index);
 static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec,
@@ -1387,9 +1389,9 @@ static int __zram_bvec_write(struct zram *zram, struct bio_vec *bvec,
                        __GFP_HIGHMEM |
                        __GFP_MOVABLE);
 
-       if (unlikely(!handle)) {
+       if (IS_ERR((void *)handle)) {
                zcomp_stream_put(zram->comp);
-               return -ENOMEM;
+               return PTR_ERR((void *)handle);
        }
 
        alloced_pages = zs_get_total_pages(zram->mem_pool);
index 50a08b2..9b5e2a5 100644 (file)
@@ -22,6 +22,8 @@
  * @private: dax driver private data
  * @flags: state and boolean properties
  * @ops: operations for this device
+ * @holder_data: holder of a dax_device: could be filesystem or mapped device
+ * @holder_ops: operations for the inner holder
  */
 struct dax_device {
        struct inode inode;
@@ -29,6 +31,8 @@ struct dax_device {
        void *private;
        unsigned long flags;
        const struct dax_operations *ops;
+       void *holder_data;
+       const struct dax_holder_operations *holder_ops;
 };
 
 static dev_t dax_devt;
@@ -71,8 +75,11 @@ EXPORT_SYMBOL_GPL(dax_remove_host);
  * fs_dax_get_by_bdev() - temporary lookup mechanism for filesystem-dax
  * @bdev: block device to find a dax_device for
  * @start_off: returns the byte offset into the dax_device that @bdev starts
+ * @holder: filesystem or mapped device inside the dax_device
+ * @ops: operations for the inner holder
  */
-struct dax_device *fs_dax_get_by_bdev(struct block_device *bdev, u64 *start_off)
+struct dax_device *fs_dax_get_by_bdev(struct block_device *bdev, u64 *start_off,
+               void *holder, const struct dax_holder_operations *ops)
 {
        struct dax_device *dax_dev;
        u64 part_size;
@@ -92,11 +99,26 @@ struct dax_device *fs_dax_get_by_bdev(struct block_device *bdev, u64 *start_off)
        dax_dev = xa_load(&dax_hosts, (unsigned long)bdev->bd_disk);
        if (!dax_dev || !dax_alive(dax_dev) || !igrab(&dax_dev->inode))
                dax_dev = NULL;
+       else if (holder) {
+               if (!cmpxchg(&dax_dev->holder_data, NULL, holder))
+                       dax_dev->holder_ops = ops;
+               else
+                       dax_dev = NULL;
+       }
        dax_read_unlock(id);
 
        return dax_dev;
 }
 EXPORT_SYMBOL_GPL(fs_dax_get_by_bdev);
+
+void fs_put_dax(struct dax_device *dax_dev, void *holder)
+{
+       if (dax_dev && holder &&
+           cmpxchg(&dax_dev->holder_data, holder, NULL) == holder)
+               dax_dev->holder_ops = NULL;
+       put_dax(dax_dev);
+}
+EXPORT_SYMBOL_GPL(fs_put_dax);
 #endif /* CONFIG_BLOCK && CONFIG_FS_DAX */
 
 enum dax_device_flags {
@@ -204,6 +226,29 @@ size_t dax_recovery_write(struct dax_device *dax_dev, pgoff_t pgoff,
 }
 EXPORT_SYMBOL_GPL(dax_recovery_write);
 
+int dax_holder_notify_failure(struct dax_device *dax_dev, u64 off,
+                             u64 len, int mf_flags)
+{
+       int rc, id;
+
+       id = dax_read_lock();
+       if (!dax_alive(dax_dev)) {
+               rc = -ENXIO;
+               goto out;
+       }
+
+       if (!dax_dev->holder_ops) {
+               rc = -EOPNOTSUPP;
+               goto out;
+       }
+
+       rc = dax_dev->holder_ops->notify_failure(dax_dev, off, len, mf_flags);
+out:
+       dax_read_unlock(id);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(dax_holder_notify_failure);
+
 #ifdef CONFIG_ARCH_HAS_PMEM_API
 void arch_wb_cache_pmem(void *addr, size_t size);
 void dax_flush(struct dax_device *dax_dev, void *addr, size_t size)
@@ -277,8 +322,15 @@ void kill_dax(struct dax_device *dax_dev)
        if (!dax_dev)
                return;
 
+       if (dax_dev->holder_data != NULL)
+               dax_holder_notify_failure(dax_dev, 0, U64_MAX, 0);
+
        clear_bit(DAXDEV_ALIVE, &dax_dev->flags);
        synchronize_srcu(&dax_srcu);
+
+       /* clear holder data */
+       dax_dev->holder_ops = NULL;
+       dax_dev->holder_data = NULL;
 }
 EXPORT_SYMBOL_GPL(kill_dax);
 
@@ -420,6 +472,19 @@ void put_dax(struct dax_device *dax_dev)
 }
 EXPORT_SYMBOL_GPL(put_dax);
 
+/**
+ * dax_holder() - obtain the holder of a dax device
+ * @dax_dev: a dax_device instance
+
+ * Return: the holder's data which represents the holder if registered,
+ * otherwize NULL.
+ */
+void *dax_holder(struct dax_device *dax_dev)
+{
+       return dax_dev->holder_data;
+}
+EXPORT_SYMBOL_GPL(dax_holder);
+
 /**
  * inode_dax: convert a public inode into its dax_dev
  * @inode: An inode with i_cdev pointing to a dax_dev
index 373e5bf..b059a77 100644 (file)
@@ -685,13 +685,15 @@ svm_migrate_vma_to_ram(struct amdgpu_device *adev, struct svm_range *prange,
        migrate.vma = vma;
        migrate.start = start;
        migrate.end = end;
-       migrate.flags = MIGRATE_VMA_SELECT_DEVICE_PRIVATE;
        migrate.pgmap_owner = SVM_ADEV_PGMAP_OWNER(adev);
+       if (adev->gmc.xgmi.connected_to_cpu)
+               migrate.flags = MIGRATE_VMA_SELECT_DEVICE_COHERENT;
+       else
+               migrate.flags = MIGRATE_VMA_SELECT_DEVICE_PRIVATE;
 
        buf = kvcalloc(npages,
                       2 * sizeof(*migrate.src) + sizeof(uint64_t) + sizeof(dma_addr_t),
                       GFP_KERNEL);
-
        if (!buf)
                goto out;
 
@@ -974,7 +976,7 @@ int svm_migrate_init(struct amdgpu_device *adev)
 {
        struct kfd_dev *kfddev = adev->kfd.dev;
        struct dev_pagemap *pgmap;
-       struct resource *res;
+       struct resource *res = NULL;
        unsigned long size;
        void *r;
 
@@ -989,28 +991,34 @@ int svm_migrate_init(struct amdgpu_device *adev)
         * should remove reserved size
         */
        size = ALIGN(adev->gmc.real_vram_size, 2ULL << 20);
-       res = devm_request_free_mem_region(adev->dev, &iomem_resource, size);
-       if (IS_ERR(res))
-               return -ENOMEM;
+       if (adev->gmc.xgmi.connected_to_cpu) {
+               pgmap->range.start = adev->gmc.aper_base;
+               pgmap->range.end = adev->gmc.aper_base + adev->gmc.aper_size - 1;
+               pgmap->type = MEMORY_DEVICE_COHERENT;
+       } else {
+               res = devm_request_free_mem_region(adev->dev, &iomem_resource, size);
+               if (IS_ERR(res))
+                       return -ENOMEM;
+               pgmap->range.start = res->start;
+               pgmap->range.end = res->end;
+               pgmap->type = MEMORY_DEVICE_PRIVATE;
+       }
 
-       pgmap->type = MEMORY_DEVICE_PRIVATE;
        pgmap->nr_range = 1;
-       pgmap->range.start = res->start;
-       pgmap->range.end = res->end;
        pgmap->ops = &svm_migrate_pgmap_ops;
        pgmap->owner = SVM_ADEV_PGMAP_OWNER(adev);
-       pgmap->flags = MIGRATE_VMA_SELECT_DEVICE_PRIVATE;
-
+       pgmap->flags = 0;
        /* Device manager releases device-specific resources, memory region and
         * pgmap when driver disconnects from device.
         */
        r = devm_memremap_pages(adev->dev, pgmap);
        if (IS_ERR(r)) {
                pr_err("failed to register HMM device memory\n");
-
                /* Disable SVM support capability */
                pgmap->type = 0;
-               devm_release_mem_region(adev->dev, res->start, resource_size(res));
+               if (pgmap->type == MEMORY_DEVICE_PRIVATE)
+                       devm_release_mem_region(adev->dev, res->start,
+                                               res->end - res->start + 1);
                return PTR_ERR(r);
        }
 
index 1030053..8dc5c88 100644 (file)
@@ -426,7 +426,8 @@ void i915_gem_driver_register__shrinker(struct drm_i915_private *i915)
        i915->mm.shrinker.count_objects = i915_gem_shrinker_count;
        i915->mm.shrinker.seeks = DEFAULT_SEEKS;
        i915->mm.shrinker.batch = 4096;
-       drm_WARN_ON(&i915->drm, register_shrinker(&i915->mm.shrinker));
+       drm_WARN_ON(&i915->drm, register_shrinker(&i915->mm.shrinker,
+                                                 "drm-i915_gem"));
 
        i915->mm.oom_notifier.notifier_call = i915_gem_shrinker_oom;
        drm_WARN_ON(&i915->drm, register_oom_notifier(&i915->mm.oom_notifier));
index 6e39d95..0317055 100644 (file)
@@ -221,7 +221,7 @@ void msm_gem_shrinker_init(struct drm_device *dev)
        priv->shrinker.count_objects = msm_gem_shrinker_count;
        priv->shrinker.scan_objects = msm_gem_shrinker_scan;
        priv->shrinker.seeks = DEFAULT_SEEKS;
-       WARN_ON(register_shrinker(&priv->shrinker));
+       WARN_ON(register_shrinker(&priv->shrinker, "drm-msm_gem"));
 
        priv->vmap_notifier.notifier_call = msm_gem_shrinker_vmap;
        WARN_ON(register_vmap_purge_notifier(&priv->vmap_notifier));
index 77e7cb6..bf01707 100644 (file)
@@ -103,7 +103,7 @@ void panfrost_gem_shrinker_init(struct drm_device *dev)
        pfdev->shrinker.count_objects = panfrost_gem_shrinker_count;
        pfdev->shrinker.scan_objects = panfrost_gem_shrinker_scan;
        pfdev->shrinker.seeks = DEFAULT_SEEKS;
-       WARN_ON(register_shrinker(&pfdev->shrinker));
+       WARN_ON(register_shrinker(&pfdev->shrinker, "drm-panfrost"));
 }
 
 /**
index 1bba0a0..21b6163 100644 (file)
@@ -722,7 +722,7 @@ int ttm_pool_mgr_init(unsigned long num_pages)
        mm_shrinker.count_objects = ttm_pool_shrinker_count;
        mm_shrinker.scan_objects = ttm_pool_shrinker_scan;
        mm_shrinker.seeks = 1;
-       return register_shrinker(&mm_shrinker);
+       return register_shrinker(&mm_shrinker, "drm-ttm_pool");
 }
 
 /**
index e136d6e..147c493 100644 (file)
@@ -812,7 +812,7 @@ int bch_btree_cache_alloc(struct cache_set *c)
        c->shrink.seeks = 4;
        c->shrink.batch = c->btree_pages * 2;
 
-       if (register_shrinker(&c->shrink))
+       if (register_shrinker(&c->shrink, "md-bcache:%pU", c->set_uuid))
                pr_warn("bcache: %s: could not register shrinker\n",
                                __func__);
 
index dc01ce3..514a802 100644 (file)
@@ -1804,7 +1804,8 @@ struct dm_bufio_client *dm_bufio_client_create(struct block_device *bdev, unsign
        c->shrinker.scan_objects = dm_bufio_shrink_scan;
        c->shrinker.seeks = 1;
        c->shrinker.batch = 0;
-       r = register_shrinker(&c->shrinker);
+       r = register_shrinker(&c->shrinker, "md-%s:(%u:%u)", slab_name,
+                             MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev));
        if (r)
                goto bad;
 
index 34db364..0278482 100644 (file)
@@ -2945,7 +2945,9 @@ int dmz_ctr_metadata(struct dmz_dev *dev, int num_dev,
        zmd->mblk_shrinker.seeks = DEFAULT_SEEKS;
 
        /* Metadata cache shrinker */
-       ret = register_shrinker(&zmd->mblk_shrinker);
+       ret = register_shrinker(&zmd->mblk_shrinker, "md-meta:(%u:%u)",
+                               MAJOR(dev->bdev->bd_dev),
+                               MINOR(dev->bdev->bd_dev));
        if (ret) {
                dmz_zmd_err(zmd, "Register metadata cache shrinker failed");
                goto err;
index 28bd4a3..60549b6 100644 (file)
@@ -752,7 +752,7 @@ static int open_table_device(struct table_device *td, dev_t dev,
        }
 
        td->dm_dev.bdev = bdev;
-       td->dm_dev.dax_dev = fs_dax_get_by_bdev(bdev, &part_off);
+       td->dm_dev.dax_dev = fs_dax_get_by_bdev(bdev, &part_off, NULL, NULL);
        return 0;
 }
 
index 860c45c..31a0cbf 100644 (file)
@@ -7644,7 +7644,7 @@ static struct r5conf *setup_conf(struct mddev *mddev)
        conf->shrinker.count_objects = raid5_cache_count;
        conf->shrinker.batch = 128;
        conf->shrinker.flags = 0;
-       ret = register_shrinker(&conf->shrinker);
+       ret = register_shrinker(&conf->shrinker, "md-raid5:%s", mdname(mddev));
        if (ret) {
                pr_warn("md/raid:%s: couldn't register shrinker.\n",
                        mdname(mddev));
index 85dd6aa..61a2be7 100644 (file)
@@ -1585,7 +1585,7 @@ static int vmballoon_register_shrinker(struct vmballoon *b)
        b->shrinker.count_objects = vmballoon_shrinker_count;
        b->shrinker.seeks = DEFAULT_SEEKS;
 
-       r = register_shrinker(&b->shrinker);
+       r = register_shrinker(&b->shrinker, "vmw-balloon");
 
        if (r == 0)
                b->shrinker_registered = true;
index f36efcc..7e88cd2 100644 (file)
@@ -453,6 +453,21 @@ static void pmem_release_disk(void *__pmem)
        put_disk(pmem->disk);
 }
 
+static int pmem_pagemap_memory_failure(struct dev_pagemap *pgmap,
+               unsigned long pfn, unsigned long nr_pages, int mf_flags)
+{
+       struct pmem_device *pmem =
+                       container_of(pgmap, struct pmem_device, pgmap);
+       u64 offset = PFN_PHYS(pfn) - pmem->phys_addr - pmem->data_offset;
+       u64 len = nr_pages << PAGE_SHIFT;
+
+       return dax_holder_notify_failure(pmem->dax_dev, offset, len, mf_flags);
+}
+
+static const struct dev_pagemap_ops fsdax_pagemap_ops = {
+       .memory_failure         = pmem_pagemap_memory_failure,
+};
+
 static int pmem_attach_disk(struct device *dev,
                struct nd_namespace_common *ndns)
 {
@@ -514,6 +529,7 @@ static int pmem_attach_disk(struct device *dev,
        pmem->pfn_flags = PFN_DEV;
        if (is_nd_pfn(dev)) {
                pmem->pgmap.type = MEMORY_DEVICE_FS_DAX;
+               pmem->pgmap.ops = &fsdax_pagemap_ops;
                addr = devm_memremap_pages(dev, &pmem->pgmap);
                pfn_sb = nd_pfn->pfn_sb;
                pmem->data_offset = le64_to_cpu(pfn_sb->dataoff);
@@ -527,6 +543,7 @@ static int pmem_attach_disk(struct device *dev,
                pmem->pgmap.range.end = res->end;
                pmem->pgmap.nr_range = 1;
                pmem->pgmap.type = MEMORY_DEVICE_FS_DAX;
+               pmem->pgmap.ops = &fsdax_pagemap_ops;
                addr = devm_memremap_pages(dev, &pmem->pgmap);
                pmem->pfn_flags |= PFN_MAP;
                bb_range = pmem->pgmap.range;
index e02a30c..d170c88 100644 (file)
@@ -529,7 +529,7 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
                        pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %lu MiB\n",
                                uname, &base, (unsigned long)(size / SZ_1M));
                        if (!nomap)
-                               kmemleak_alloc_phys(base, size, 0, 0);
+                               kmemleak_alloc_phys(base, size, 0);
                }
                else
                        pr_err("Reserved memory: failed to reserve memory for node '%s': base %pa, size %lu MiB\n",
index bd360b9..3f78a3a 100644 (file)
@@ -856,7 +856,7 @@ static int virtio_balloon_register_shrinker(struct virtio_balloon *vb)
        vb->shrinker.count_objects = virtio_balloon_shrinker_count;
        vb->shrinker.seeks = DEFAULT_SEEKS;
 
-       return register_shrinker(&vb->shrinker);
+       return register_shrinker(&vb->shrinker, "virtio-balloon");
 }
 
 static int virtballoon_probe(struct virtio_device *vdev)
index e07486f..0c2892e 100644 (file)
@@ -862,8 +862,7 @@ static void virtio_mem_sbm_notify_online(struct virtio_mem *vm,
                                         unsigned long mb_id,
                                         unsigned long start_pfn)
 {
-       const bool is_movable = page_zonenum(pfn_to_page(start_pfn)) ==
-                               ZONE_MOVABLE;
+       const bool is_movable = is_zone_movable_page(pfn_to_page(start_pfn));
        int new_state;
 
        switch (virtio_mem_sbm_get_mb_state(vm, mb_id)) {
@@ -1158,8 +1157,7 @@ static void virtio_mem_fake_online(unsigned long pfn, unsigned long nr_pages)
  */
 static int virtio_mem_fake_offline(unsigned long pfn, unsigned long nr_pages)
 {
-       const bool is_movable = page_zonenum(pfn_to_page(pfn)) ==
-                               ZONE_MOVABLE;
+       const bool is_movable = is_zone_movable_page(pfn_to_page(pfn));
        int rc, retry_count;
 
        /*
index 5abded9..9c09f89 100644 (file)
@@ -305,7 +305,7 @@ static int __init xenbus_probe_backend_init(void)
 
        register_xenstore_notifier(&xenstore_notifier);
 
-       if (register_shrinker(&backend_memory_shrinker))
+       if (register_shrinker(&backend_memory_shrinker, "xen-backend"))
                pr_warn("shrinker registration failed\n");
 
        return 0;
index 4c7089b..f89beac 100644 (file)
@@ -1816,6 +1816,8 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
                        error = -EBUSY;
        } else {
                snprintf(s->s_id, sizeof(s->s_id), "%pg", bdev);
+               shrinker_debugfs_rename(&s->s_shrink, "sb-%s:%s", fs_type->name,
+                                       s->s_id);
                btrfs_sb(s)->bdev_holder = fs_type;
                if (!strstr(crc32c_impl(), "generic"))
                        set_bit(BTRFS_FS_CSUM_IMPL_FAST, &fs_info->flags);
index 649ff51..c440dce 100644 (file)
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -334,13 +334,35 @@ static unsigned long dax_end_pfn(void *entry)
        for (pfn = dax_to_pfn(entry); \
                        pfn < dax_end_pfn(entry); pfn++)
 
+static inline bool dax_mapping_is_cow(struct address_space *mapping)
+{
+       return (unsigned long)mapping == PAGE_MAPPING_DAX_COW;
+}
+
 /*
- * TODO: for reflink+dax we need a way to associate a single page with
- * multiple address_space instances at different linear_page_index()
- * offsets.
+ * Set the page->mapping with FS_DAX_MAPPING_COW flag, increase the refcount.
+ */
+static inline void dax_mapping_set_cow(struct page *page)
+{
+       if ((uintptr_t)page->mapping != PAGE_MAPPING_DAX_COW) {
+               /*
+                * Reset the index if the page was already mapped
+                * regularly before.
+                */
+               if (page->mapping)
+                       page->index = 1;
+               page->mapping = (void *)PAGE_MAPPING_DAX_COW;
+       }
+       page->index++;
+}
+
+/*
+ * When it is called in dax_insert_entry(), the cow flag will indicate that
+ * whether this entry is shared by multiple files.  If so, set the page->mapping
+ * FS_DAX_MAPPING_COW, and use page->index as refcount.
  */
 static void dax_associate_entry(void *entry, struct address_space *mapping,
-               struct vm_area_struct *vma, unsigned long address)
+               struct vm_area_struct *vma, unsigned long address, bool cow)
 {
        unsigned long size = dax_entry_size(entry), pfn, index;
        int i = 0;
@@ -352,9 +374,13 @@ static void dax_associate_entry(void *entry, struct address_space *mapping,
        for_each_mapped_pfn(entry, pfn) {
                struct page *page = pfn_to_page(pfn);
 
-               WARN_ON_ONCE(page->mapping);
-               page->mapping = mapping;
-               page->index = index + i++;
+               if (cow) {
+                       dax_mapping_set_cow(page);
+               } else {
+                       WARN_ON_ONCE(page->mapping);
+                       page->mapping = mapping;
+                       page->index = index + i++;
+               }
        }
 }
 
@@ -370,7 +396,12 @@ static void dax_disassociate_entry(void *entry, struct address_space *mapping,
                struct page *page = pfn_to_page(pfn);
 
                WARN_ON_ONCE(trunc && page_ref_count(page) > 1);
-               WARN_ON_ONCE(page->mapping && page->mapping != mapping);
+               if (dax_mapping_is_cow(page->mapping)) {
+                       /* keep the CoW flag if this page is still shared */
+                       if (page->index-- > 0)
+                               continue;
+               } else
+                       WARN_ON_ONCE(page->mapping && page->mapping != mapping);
                page->mapping = NULL;
                page->index = 0;
        }
@@ -455,6 +486,69 @@ void dax_unlock_page(struct page *page, dax_entry_t cookie)
        dax_unlock_entry(&xas, (void *)cookie);
 }
 
+/*
+ * dax_lock_mapping_entry - Lock the DAX entry corresponding to a mapping
+ * @mapping: the file's mapping whose entry we want to lock
+ * @index: the offset within this file
+ * @page: output the dax page corresponding to this dax entry
+ *
+ * Return: A cookie to pass to dax_unlock_mapping_entry() or 0 if the entry
+ * could not be locked.
+ */
+dax_entry_t dax_lock_mapping_entry(struct address_space *mapping, pgoff_t index,
+               struct page **page)
+{
+       XA_STATE(xas, NULL, 0);
+       void *entry;
+
+       rcu_read_lock();
+       for (;;) {
+               entry = NULL;
+               if (!dax_mapping(mapping))
+                       break;
+
+               xas.xa = &mapping->i_pages;
+               xas_lock_irq(&xas);
+               xas_set(&xas, index);
+               entry = xas_load(&xas);
+               if (dax_is_locked(entry)) {
+                       rcu_read_unlock();
+                       wait_entry_unlocked(&xas, entry);
+                       rcu_read_lock();
+                       continue;
+               }
+               if (!entry ||
+                   dax_is_zero_entry(entry) || dax_is_empty_entry(entry)) {
+                       /*
+                        * Because we are looking for entry from file's mapping
+                        * and index, so the entry may not be inserted for now,
+                        * or even a zero/empty entry.  We don't think this is
+                        * an error case.  So, return a special value and do
+                        * not output @page.
+                        */
+                       entry = (void *)~0UL;
+               } else {
+                       *page = pfn_to_page(dax_to_pfn(entry));
+                       dax_lock_entry(&xas, entry);
+               }
+               xas_unlock_irq(&xas);
+               break;
+       }
+       rcu_read_unlock();
+       return (dax_entry_t)entry;
+}
+
+void dax_unlock_mapping_entry(struct address_space *mapping, pgoff_t index,
+               dax_entry_t cookie)
+{
+       XA_STATE(xas, &mapping->i_pages, index);
+
+       if (cookie == ~0UL)
+               return;
+
+       dax_unlock_entry(&xas, (void *)cookie);
+}
+
 /*
  * Find page cache entry at given index. If it is a DAX entry, return it
  * with the entry locked. If the page cache doesn't contain an entry at
@@ -735,6 +829,23 @@ static int copy_cow_page_dax(struct vm_fault *vmf, const struct iomap_iter *iter
        return 0;
 }
 
+/*
+ * MAP_SYNC on a dax mapping guarantees dirty metadata is
+ * flushed on write-faults (non-cow), but not read-faults.
+ */
+static bool dax_fault_is_synchronous(const struct iomap_iter *iter,
+               struct vm_area_struct *vma)
+{
+       return (iter->flags & IOMAP_WRITE) && (vma->vm_flags & VM_SYNC) &&
+               (iter->iomap.flags & IOMAP_F_DIRTY);
+}
+
+static bool dax_fault_is_cow(const struct iomap_iter *iter)
+{
+       return (iter->flags & IOMAP_WRITE) &&
+               (iter->iomap.flags & IOMAP_F_SHARED);
+}
+
 /*
  * By this point grab_mapping_entry() has ensured that we have a locked entry
  * of the appropriate size so we don't have to worry about downgrading PMDs to
@@ -742,16 +853,19 @@ static int copy_cow_page_dax(struct vm_fault *vmf, const struct iomap_iter *iter
  * already in the tree, we will skip the insertion and just dirty the PMD as
  * appropriate.
  */
-static void *dax_insert_entry(struct xa_state *xas,
-               struct address_space *mapping, struct vm_fault *vmf,
-               void *entry, pfn_t pfn, unsigned long flags, bool dirty)
+static void *dax_insert_entry(struct xa_state *xas, struct vm_fault *vmf,
+               const struct iomap_iter *iter, void *entry, pfn_t pfn,
+               unsigned long flags)
 {
+       struct address_space *mapping = vmf->vma->vm_file->f_mapping;
        void *new_entry = dax_make_entry(pfn, flags);
+       bool dirty = !dax_fault_is_synchronous(iter, vmf->vma);
+       bool cow = dax_fault_is_cow(iter);
 
        if (dirty)
                __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
 
-       if (dax_is_zero_entry(entry) && !(flags & DAX_ZERO_PAGE)) {
+       if (cow || (dax_is_zero_entry(entry) && !(flags & DAX_ZERO_PAGE))) {
                unsigned long index = xas->xa_index;
                /* we are replacing a zero page with block mapping */
                if (dax_is_pmd_entry(entry))
@@ -763,11 +877,12 @@ static void *dax_insert_entry(struct xa_state *xas,
 
        xas_reset(xas);
        xas_lock_irq(xas);
-       if (dax_is_zero_entry(entry) || dax_is_empty_entry(entry)) {
+       if (cow || dax_is_zero_entry(entry) || dax_is_empty_entry(entry)) {
                void *old;
 
                dax_disassociate_entry(entry, mapping, false);
-               dax_associate_entry(new_entry, mapping, vmf->vma, vmf->address);
+               dax_associate_entry(new_entry, mapping, vmf->vma, vmf->address,
+                               cow);
                /*
                 * Only swap our new entry into the page cache if the current
                 * entry is a zero page or an empty entry.  If a normal PTE or
@@ -787,6 +902,9 @@ static void *dax_insert_entry(struct xa_state *xas,
        if (dirty)
                xas_set_mark(xas, PAGECACHE_TAG_DIRTY);
 
+       if (cow)
+               xas_set_mark(xas, PAGECACHE_TAG_TOWRITE);
+
        xas_unlock_irq(xas);
        return entry;
 }
@@ -931,20 +1049,22 @@ int dax_writeback_mapping_range(struct address_space *mapping,
 }
 EXPORT_SYMBOL_GPL(dax_writeback_mapping_range);
 
-static int dax_iomap_pfn(const struct iomap *iomap, loff_t pos, size_t size,
-                        pfn_t *pfnp)
+static int dax_iomap_direct_access(const struct iomap *iomap, loff_t pos,
+               size_t size, void **kaddr, pfn_t *pfnp)
 {
        pgoff_t pgoff = dax_iomap_pgoff(iomap, pos);
-       int id, rc;
+       int id, rc = 0;
        long length;
 
        id = dax_read_lock();
        length = dax_direct_access(iomap->dax_dev, pgoff, PHYS_PFN(size),
-                                  DAX_ACCESS, NULL, pfnp);
+                                  DAX_ACCESS, kaddr, pfnp);
        if (length < 0) {
                rc = length;
                goto out;
        }
+       if (!pfnp)
+               goto out_check_addr;
        rc = -EINVAL;
        if (PFN_PHYS(length) < size)
                goto out;
@@ -954,11 +1074,71 @@ static int dax_iomap_pfn(const struct iomap *iomap, loff_t pos, size_t size,
        if (length > 1 && !pfn_t_devmap(*pfnp))
                goto out;
        rc = 0;
+
+out_check_addr:
+       if (!kaddr)
+               goto out;
+       if (!*kaddr)
+               rc = -EFAULT;
 out:
        dax_read_unlock(id);
        return rc;
 }
 
+/**
+ * dax_iomap_cow_copy - Copy the data from source to destination before write
+ * @pos:       address to do copy from.
+ * @length:    size of copy operation.
+ * @align_size:        aligned w.r.t align_size (either PMD_SIZE or PAGE_SIZE)
+ * @srcmap:    iomap srcmap
+ * @daddr:     destination address to copy to.
+ *
+ * This can be called from two places. Either during DAX write fault (page
+ * aligned), to copy the length size data to daddr. Or, while doing normal DAX
+ * write operation, dax_iomap_actor() might call this to do the copy of either
+ * start or end unaligned address. In the latter case the rest of the copy of
+ * aligned ranges is taken care by dax_iomap_actor() itself.
+ */
+static int dax_iomap_cow_copy(loff_t pos, uint64_t length, size_t align_size,
+               const struct iomap *srcmap, void *daddr)
+{
+       loff_t head_off = pos & (align_size - 1);
+       size_t size = ALIGN(head_off + length, align_size);
+       loff_t end = pos + length;
+       loff_t pg_end = round_up(end, align_size);
+       bool copy_all = head_off == 0 && end == pg_end;
+       void *saddr = 0;
+       int ret = 0;
+
+       ret = dax_iomap_direct_access(srcmap, pos, size, &saddr, NULL);
+       if (ret)
+               return ret;
+
+       if (copy_all) {
+               ret = copy_mc_to_kernel(daddr, saddr, length);
+               return ret ? -EIO : 0;
+       }
+
+       /* Copy the head part of the range */
+       if (head_off) {
+               ret = copy_mc_to_kernel(daddr, saddr, head_off);
+               if (ret)
+                       return -EIO;
+       }
+
+       /* Copy the tail part of the range */
+       if (end < pg_end) {
+               loff_t tail_off = head_off + length;
+               loff_t tail_len = pg_end - end;
+
+               ret = copy_mc_to_kernel(daddr + tail_off, saddr + tail_off,
+                                       tail_len);
+               if (ret)
+                       return -EIO;
+       }
+       return 0;
+}
+
 /*
  * The user has performed a load from a hole in the file.  Allocating a new
  * page in the file would cause excessive storage usage for workloads with
@@ -966,17 +1146,15 @@ out:
  * If this page is ever written to we will re-fault and change the mapping to
  * point to real DAX storage instead.
  */
-static vm_fault_t dax_load_hole(struct xa_state *xas,
-               struct address_space *mapping, void **entry,
-               struct vm_fault *vmf)
+static vm_fault_t dax_load_hole(struct xa_state *xas, struct vm_fault *vmf,
+               const struct iomap_iter *iter, void **entry)
 {
-       struct inode *inode = mapping->host;
+       struct inode *inode = iter->inode;
        unsigned long vaddr = vmf->address;
        pfn_t pfn = pfn_to_pfn_t(my_zero_pfn(vaddr));
        vm_fault_t ret;
 
-       *entry = dax_insert_entry(xas, mapping, vmf, *entry, pfn,
-                       DAX_ZERO_PAGE, false);
+       *entry = dax_insert_entry(xas, vmf, iter, *entry, pfn, DAX_ZERO_PAGE);
 
        ret = vmf_insert_mixed(vmf->vma, vaddr, pfn);
        trace_dax_load_hole(inode, vmf, ret);
@@ -985,7 +1163,7 @@ static vm_fault_t dax_load_hole(struct xa_state *xas,
 
 #ifdef CONFIG_FS_DAX_PMD
 static vm_fault_t dax_pmd_load_hole(struct xa_state *xas, struct vm_fault *vmf,
-               const struct iomap *iomap, void **entry)
+               const struct iomap_iter *iter, void **entry)
 {
        struct address_space *mapping = vmf->vma->vm_file->f_mapping;
        unsigned long pmd_addr = vmf->address & PMD_MASK;
@@ -1003,8 +1181,8 @@ static vm_fault_t dax_pmd_load_hole(struct xa_state *xas, struct vm_fault *vmf,
                goto fallback;
 
        pfn = page_to_pfn_t(zero_page);
-       *entry = dax_insert_entry(xas, mapping, vmf, *entry, pfn,
-                       DAX_PMD | DAX_ZERO_PAGE, false);
+       *entry = dax_insert_entry(xas, vmf, iter, *entry, pfn,
+                                 DAX_PMD | DAX_ZERO_PAGE);
 
        if (arch_needs_pgtable_deposit()) {
                pgtable = pte_alloc_one(vma->vm_mm);
@@ -1037,23 +1215,34 @@ fallback:
 }
 #else
 static vm_fault_t dax_pmd_load_hole(struct xa_state *xas, struct vm_fault *vmf,
-               const struct iomap *iomap, void **entry)
+               const struct iomap_iter *iter, void **entry)
 {
        return VM_FAULT_FALLBACK;
 }
 #endif /* CONFIG_FS_DAX_PMD */
 
-static int dax_memzero(struct dax_device *dax_dev, pgoff_t pgoff,
-               unsigned int offset, size_t size)
+static int dax_memzero(struct iomap_iter *iter, loff_t pos, size_t size)
 {
+       const struct iomap *iomap = &iter->iomap;
+       const struct iomap *srcmap = iomap_iter_srcmap(iter);
+       unsigned offset = offset_in_page(pos);
+       pgoff_t pgoff = dax_iomap_pgoff(iomap, pos);
        void *kaddr;
        long ret;
 
-       ret = dax_direct_access(dax_dev, pgoff, 1, DAX_ACCESS, &kaddr, NULL);
-       if (ret > 0) {
-               memset(kaddr + offset, 0, size);
-               dax_flush(dax_dev, kaddr + offset, size);
-       }
+       ret = dax_direct_access(iomap->dax_dev, pgoff, 1, DAX_ACCESS, &kaddr,
+                               NULL);
+       if (ret < 0)
+               return ret;
+       memset(kaddr + offset, 0, size);
+       if (srcmap->addr != iomap->addr) {
+               ret = dax_iomap_cow_copy(pos, size, PAGE_SIZE, srcmap,
+                                        kaddr);
+               if (ret < 0)
+                       return ret;
+               dax_flush(iomap->dax_dev, kaddr, PAGE_SIZE);
+       } else
+               dax_flush(iomap->dax_dev, kaddr + offset, size);
        return ret;
 }
 
@@ -1080,7 +1269,7 @@ static s64 dax_zero_iter(struct iomap_iter *iter, bool *did_zero)
                if (IS_ALIGNED(pos, PAGE_SIZE) && size == PAGE_SIZE)
                        rc = dax_zero_page_range(iomap->dax_dev, pgoff, 1);
                else
-                       rc = dax_memzero(iomap->dax_dev, pgoff, offset, size);
+                       rc = dax_memzero(iter, pos, size);
                dax_read_unlock(id);
 
                if (rc < 0)
@@ -1129,15 +1318,17 @@ static loff_t dax_iomap_iter(const struct iomap_iter *iomi,
                struct iov_iter *iter)
 {
        const struct iomap *iomap = &iomi->iomap;
+       const struct iomap *srcmap = &iomi->srcmap;
        loff_t length = iomap_length(iomi);
        loff_t pos = iomi->pos;
        struct dax_device *dax_dev = iomap->dax_dev;
        loff_t end = pos + length, done = 0;
+       bool write = iov_iter_rw(iter) == WRITE;
        ssize_t ret = 0;
        size_t xfer;
        int id;
 
-       if (iov_iter_rw(iter) == READ) {
+       if (!write) {
                end = min(end, i_size_read(iomi->inode));
                if (pos >= end)
                        return 0;
@@ -1146,7 +1337,12 @@ static loff_t dax_iomap_iter(const struct iomap_iter *iomi,
                        return iov_iter_zero(min(length, end - pos), iter);
        }
 
-       if (WARN_ON_ONCE(iomap->type != IOMAP_MAPPED))
+       /*
+        * In DAX mode, enforce either pure overwrites of written extents, or
+        * writes to unwritten extents as part of a copy-on-write operation.
+        */
+       if (WARN_ON_ONCE(iomap->type != IOMAP_MAPPED &&
+                       !(iomap->flags & IOMAP_F_SHARED)))
                return -EIO;
 
        /*
@@ -1188,6 +1384,14 @@ static loff_t dax_iomap_iter(const struct iomap_iter *iomi,
                        break;
                }
 
+               if (write &&
+                   srcmap->type != IOMAP_HOLE && srcmap->addr != iomap->addr) {
+                       ret = dax_iomap_cow_copy(pos, length, PAGE_SIZE, srcmap,
+                                                kaddr);
+                       if (ret)
+                               break;
+               }
+
                map_len = PFN_PHYS(map_len);
                kaddr += offset;
                map_len -= offset;
@@ -1197,7 +1401,7 @@ static loff_t dax_iomap_iter(const struct iomap_iter *iomi,
                if (recovery)
                        xfer = dax_recovery_write(dax_dev, pgoff, kaddr,
                                        map_len, iter);
-               else if (iov_iter_rw(iter) == WRITE)
+               else if (write)
                        xfer = dax_copy_from_iter(dax_dev, pgoff, kaddr,
                                        map_len, iter);
                else
@@ -1267,17 +1471,6 @@ static vm_fault_t dax_fault_return(int error)
        return vmf_error(error);
 }
 
-/*
- * MAP_SYNC on a dax mapping guarantees dirty metadata is
- * flushed on write-faults (non-cow), but not read-faults.
- */
-static bool dax_fault_is_synchronous(unsigned long flags,
-               struct vm_area_struct *vma, const struct iomap *iomap)
-{
-       return (flags & IOMAP_WRITE) && (vma->vm_flags & VM_SYNC)
-               && (iomap->flags & IOMAP_F_DIRTY);
-}
-
 /*
  * When handling a synchronous page fault and the inode need a fsync, we can
  * insert the PTE/PMD into page tables only after that fsync happened. Skip
@@ -1335,15 +1528,15 @@ static vm_fault_t dax_fault_iter(struct vm_fault *vmf,
                const struct iomap_iter *iter, pfn_t *pfnp,
                struct xa_state *xas, void **entry, bool pmd)
 {
-       struct address_space *mapping = vmf->vma->vm_file->f_mapping;
        const struct iomap *iomap = &iter->iomap;
+       const struct iomap *srcmap = &iter->srcmap;
        size_t size = pmd ? PMD_SIZE : PAGE_SIZE;
        loff_t pos = (loff_t)xas->xa_index << PAGE_SHIFT;
-       bool write = vmf->flags & FAULT_FLAG_WRITE;
-       bool sync = dax_fault_is_synchronous(iter->flags, vmf->vma, iomap);
+       bool write = iter->flags & IOMAP_WRITE;
        unsigned long entry_flags = pmd ? DAX_PMD : 0;
        int err = 0;
        pfn_t pfn;
+       void *kaddr;
 
        if (!pmd && vmf->cow_page)
                return dax_fault_cow_page(vmf, iter);
@@ -1352,23 +1545,29 @@ static vm_fault_t dax_fault_iter(struct vm_fault *vmf,
        if (!write &&
            (iomap->type == IOMAP_UNWRITTEN || iomap->type == IOMAP_HOLE)) {
                if (!pmd)
-                       return dax_load_hole(xas, mapping, entry, vmf);
-               return dax_pmd_load_hole(xas, vmf, iomap, entry);
+                       return dax_load_hole(xas, vmf, iter, entry);
+               return dax_pmd_load_hole(xas, vmf, iter, entry);
        }
 
-       if (iomap->type != IOMAP_MAPPED) {
+       if (iomap->type != IOMAP_MAPPED && !(iomap->flags & IOMAP_F_SHARED)) {
                WARN_ON_ONCE(1);
                return pmd ? VM_FAULT_FALLBACK : VM_FAULT_SIGBUS;
        }
 
-       err = dax_iomap_pfn(&iter->iomap, pos, size, &pfn);
+       err = dax_iomap_direct_access(iomap, pos, size, &kaddr, &pfn);
        if (err)
                return pmd ? VM_FAULT_FALLBACK : dax_fault_return(err);
 
-       *entry = dax_insert_entry(xas, mapping, vmf, *entry, pfn, entry_flags,
-                                 write && !sync);
+       *entry = dax_insert_entry(xas, vmf, iter, *entry, pfn, entry_flags);
+
+       if (write &&
+           srcmap->type != IOMAP_HOLE && srcmap->addr != iomap->addr) {
+               err = dax_iomap_cow_copy(pos, size, size, srcmap, kaddr);
+               if (err)
+                       return dax_fault_return(err);
+       }
 
-       if (sync)
+       if (dax_fault_is_synchronous(iter, vmf->vma))
                return dax_fault_synchronous_pfnp(pfnp, pfn);
 
        /* insert PMD pfn */
@@ -1674,3 +1873,85 @@ vm_fault_t dax_finish_sync_fault(struct vm_fault *vmf,
        return dax_insert_pfn_mkwrite(vmf, pfn, order);
 }
 EXPORT_SYMBOL_GPL(dax_finish_sync_fault);
+
+static loff_t dax_range_compare_iter(struct iomap_iter *it_src,
+               struct iomap_iter *it_dest, u64 len, bool *same)
+{
+       const struct iomap *smap = &it_src->iomap;
+       const struct iomap *dmap = &it_dest->iomap;
+       loff_t pos1 = it_src->pos, pos2 = it_dest->pos;
+       void *saddr, *daddr;
+       int id, ret;
+
+       len = min(len, min(smap->length, dmap->length));
+
+       if (smap->type == IOMAP_HOLE && dmap->type == IOMAP_HOLE) {
+               *same = true;
+               return len;
+       }
+
+       if (smap->type == IOMAP_HOLE || dmap->type == IOMAP_HOLE) {
+               *same = false;
+               return 0;
+       }
+
+       id = dax_read_lock();
+       ret = dax_iomap_direct_access(smap, pos1, ALIGN(pos1 + len, PAGE_SIZE),
+                                     &saddr, NULL);
+       if (ret < 0)
+               goto out_unlock;
+
+       ret = dax_iomap_direct_access(dmap, pos2, ALIGN(pos2 + len, PAGE_SIZE),
+                                     &daddr, NULL);
+       if (ret < 0)
+               goto out_unlock;
+
+       *same = !memcmp(saddr, daddr, len);
+       if (!*same)
+               len = 0;
+       dax_read_unlock(id);
+       return len;
+
+out_unlock:
+       dax_read_unlock(id);
+       return -EIO;
+}
+
+int dax_dedupe_file_range_compare(struct inode *src, loff_t srcoff,
+               struct inode *dst, loff_t dstoff, loff_t len, bool *same,
+               const struct iomap_ops *ops)
+{
+       struct iomap_iter src_iter = {
+               .inode          = src,
+               .pos            = srcoff,
+               .len            = len,
+               .flags          = IOMAP_DAX,
+       };
+       struct iomap_iter dst_iter = {
+               .inode          = dst,
+               .pos            = dstoff,
+               .len            = len,
+               .flags          = IOMAP_DAX,
+       };
+       int ret;
+
+       while ((ret = iomap_iter(&src_iter, ops)) > 0) {
+               while ((ret = iomap_iter(&dst_iter, ops)) > 0) {
+                       dst_iter.processed = dax_range_compare_iter(&src_iter,
+                                               &dst_iter, len, same);
+               }
+               if (ret <= 0)
+                       src_iter.processed = ret;
+       }
+       return ret;
+}
+
+int dax_remap_file_range_prep(struct file *file_in, loff_t pos_in,
+                             struct file *file_out, loff_t pos_out,
+                             loff_t *len, unsigned int remap_flags,
+                             const struct iomap_ops *ops)
+{
+       return __generic_remap_file_range_prep(file_in, pos_in, file_out,
+                                              pos_out, len, remap_flags, ops);
+}
+EXPORT_SYMBOL_GPL(dax_remap_file_range_prep);
index 95addc5..3173deb 100644 (file)
@@ -255,7 +255,8 @@ static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb,
                if (IS_ERR(bdev))
                        return PTR_ERR(bdev);
                dif->bdev = bdev;
-               dif->dax_dev = fs_dax_get_by_bdev(bdev, &dif->dax_part_off);
+               dif->dax_dev = fs_dax_get_by_bdev(bdev, &dif->dax_part_off,
+                                                 NULL, NULL);
        }
 
        dif->blocks = le32_to_cpu(dis->blocks);
@@ -720,7 +721,8 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
                }
 
                sbi->dax_dev = fs_dax_get_by_bdev(sb->s_bdev,
-                                                 &sbi->dax_part_off);
+                                                 &sbi->dax_part_off,
+                                                 NULL, NULL);
        }
 
        err = erofs_read_superblock(sb);
@@ -812,7 +814,7 @@ static int erofs_release_device_info(int id, void *ptr, void *data)
 {
        struct erofs_device_info *dif = ptr;
 
-       fs_put_dax(dif->dax_dev);
+       fs_put_dax(dif->dax_dev, NULL);
        if (dif->bdev)
                blkdev_put(dif->bdev, FMODE_READ | FMODE_EXCL);
        erofs_fscache_unregister_cookie(&dif->fscache);
@@ -886,7 +888,7 @@ static void erofs_kill_sb(struct super_block *sb)
                return;
 
        erofs_free_dev_context(sbi->devs);
-       fs_put_dax(sbi->dax_dev);
+       fs_put_dax(sbi->dax_dev, NULL);
        erofs_fscache_unregister_cookie(&sbi->s_fscache);
        erofs_fscache_unregister_fs(sb);
        kfree(sbi->opt.fsid);
index ec9a1d7..46627cb 100644 (file)
@@ -282,7 +282,7 @@ static struct shrinker erofs_shrinker_info = {
 
 int __init erofs_init_shrinker(void)
 {
-       return register_shrinker(&erofs_shrinker_info);
+       return register_shrinker(&erofs_shrinker_info, "erofs-shrinker");
 }
 
 void erofs_exit_shrinker(void)
index 27a0a8c..252c742 100644 (file)
@@ -171,7 +171,7 @@ static void ext2_put_super (struct super_block * sb)
        brelse (sbi->s_sbh);
        sb->s_fs_info = NULL;
        kfree(sbi->s_blockgroup_lock);
-       fs_put_dax(sbi->s_daxdev);
+       fs_put_dax(sbi->s_daxdev, NULL);
        kfree(sbi);
 }
 
@@ -833,7 +833,8 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
        }
        sb->s_fs_info = sbi;
        sbi->s_sb_block = sb_block;
-       sbi->s_daxdev = fs_dax_get_by_bdev(sb->s_bdev, &sbi->s_dax_part_off);
+       sbi->s_daxdev = fs_dax_get_by_bdev(sb->s_bdev, &sbi->s_dax_part_off,
+                                          NULL, NULL);
 
        spin_lock_init(&sbi->s_lock);
        ret = -EINVAL;
@@ -1210,7 +1211,7 @@ failed_mount_group_desc:
 failed_mount:
        brelse(bh);
 failed_sbi:
-       fs_put_dax(sbi->s_daxdev);
+       fs_put_dax(sbi->s_daxdev, NULL);
        sb->s_fs_info = NULL;
        kfree(sbi->s_blockgroup_lock);
        kfree(sbi);
index 9a3a899..23167ef 100644 (file)
@@ -1654,7 +1654,8 @@ int ext4_es_register_shrinker(struct ext4_sb_info *sbi)
        sbi->s_es_shrinker.scan_objects = ext4_es_scan;
        sbi->s_es_shrinker.count_objects = ext4_es_count;
        sbi->s_es_shrinker.seeks = DEFAULT_SEEKS;
-       err = register_shrinker(&sbi->s_es_shrinker);
+       err = register_shrinker(&sbi->s_es_shrinker, "ext4-es:%s",
+                               sbi->s_sb->s_id);
        if (err)
                goto err4;
 
index 8f907e9..9a66abc 100644 (file)
@@ -1307,7 +1307,7 @@ static void ext4_put_super(struct super_block *sb)
        if (sbi->s_chksum_driver)
                crypto_free_shash(sbi->s_chksum_driver);
        kfree(sbi->s_blockgroup_lock);
-       fs_put_dax(sbi->s_daxdev);
+       fs_put_dax(sbi->s_daxdev, NULL);
        fscrypt_free_dummy_policy(&sbi->s_dummy_enc_policy);
 #if IS_ENABLED(CONFIG_UNICODE)
        utf8_unload(sb->s_encoding);
@@ -4281,7 +4281,7 @@ static void ext4_free_sbi(struct ext4_sb_info *sbi)
                return;
 
        kfree(sbi->s_blockgroup_lock);
-       fs_put_dax(sbi->s_daxdev);
+       fs_put_dax(sbi->s_daxdev, NULL);
        kfree(sbi);
 }
 
@@ -4293,7 +4293,8 @@ static struct ext4_sb_info *ext4_alloc_sbi(struct super_block *sb)
        if (!sbi)
                return NULL;
 
-       sbi->s_daxdev = fs_dax_get_by_bdev(sb->s_bdev, &sbi->s_dax_part_off);
+       sbi->s_daxdev = fs_dax_get_by_bdev(sb->s_bdev, &sbi->s_dax_part_off,
+                                          NULL, NULL);
 
        sbi->s_blockgroup_lock =
                kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL);
@@ -4305,7 +4306,7 @@ static struct ext4_sb_info *ext4_alloc_sbi(struct super_block *sb)
        sbi->s_sb = sb;
        return sbi;
 err_out:
-       fs_put_dax(sbi->s_daxdev);
+       fs_put_dax(sbi->s_daxdev, NULL);
        kfree(sbi);
        return NULL;
 }
index 37221e9..bce0230 100644 (file)
@@ -4579,7 +4579,7 @@ static int __init init_f2fs_fs(void)
        err = f2fs_init_sysfs();
        if (err)
                goto free_garbage_collection_cache;
-       err = register_shrinker(&f2fs_shrinker_info);
+       err = register_shrinker(&f2fs_shrinker_info, "f2fs-shrinker");
        if (err)
                goto free_sysfs;
        err = register_filesystem(&f2fs_fs_type);
index c992d53..dca8423 100644 (file)
@@ -2533,7 +2533,7 @@ int __init gfs2_glock_init(void)
                return -ENOMEM;
        }
 
-       ret = register_shrinker(&glock_shrinker);
+       ret = register_shrinker(&glock_shrinker, "gfs2-glock");
        if (ret) {
                destroy_workqueue(gfs2_delete_workqueue);
                destroy_workqueue(glock_workqueue);
index 244187e..b66a3e1 100644 (file)
@@ -148,7 +148,7 @@ static int __init init_gfs2_fs(void)
        if (!gfs2_trans_cachep)
                goto fail_cachep8;
 
-       error = register_shrinker(&gfs2_qd_shrinker);
+       error = register_shrinker(&gfs2_qd_shrinker, "gfs2-qd");
        if (error)
                goto fail_shrinker;
 
index 20336cb..fe0e374 100644 (file)
@@ -11,7 +11,6 @@
 
 #include <linux/thread_info.h>
 #include <asm/current.h>
-#include <linux/sched/signal.h>                /* remove ASAP */
 #include <linux/falloc.h>
 #include <linux/fs.h>
 #include <linux/mount.h>
@@ -40,7 +39,6 @@
 #include <linux/uaccess.h>
 #include <linux/sched/mm.h>
 
-static const struct super_operations hugetlbfs_ops;
 static const struct address_space_operations hugetlbfs_aops;
 const struct file_operations hugetlbfs_file_operations;
 static const struct inode_operations hugetlbfs_dir_inode_operations;
@@ -315,8 +313,7 @@ hugetlbfs_read_actor(struct page *page, unsigned long offset,
 
 /*
  * Support for read() - Find the page attached to f_mapping and copy out the
- * data. Its *very* similar to generic_file_buffered_read(), we can't use that
- * since it has PAGE_SIZE assumptions.
+ * data. This provides functionality similar to filemap_read().
  */
 static ssize_t hugetlbfs_read_iter(struct kiocb *iocb, struct iov_iter *to)
 {
@@ -1082,7 +1079,7 @@ static int hugetlbfs_statfs(struct dentry *dentry, struct kstatfs *buf)
        buf->f_bsize = huge_page_size(h);
        if (sbinfo) {
                spin_lock(&sbinfo->stat_lock);
-               /* If no limits set, just report 0 for max/free/used
+               /* If no limits set, just report 0 or -1 for max/free/used
                 * blocks, like simple_statfs() */
                if (sbinfo->spool) {
                        long free_pages;
@@ -1309,7 +1306,7 @@ static int hugetlbfs_parse_param(struct fs_context *fc, struct fs_parameter *par
                ps = memparse(param->string, &rest);
                ctx->hstate = size_to_hstate(ps);
                if (!ctx->hstate) {
-                       pr_err("Unsupported page size %lu MB\n", ps >> 20);
+                       pr_err("Unsupported page size %lu MB\n", ps / SZ_1M);
                        return -EINVAL;
                }
                return 0;
@@ -1385,7 +1382,7 @@ hugetlbfs_fill_super(struct super_block *sb, struct fs_context *fc)
        /*
         * Allocate and initialize subpool if maximum or minimum size is
         * specified.  Any needed reservations (for minimum size) are taken
-        * taken when the subpool is created.
+        * when the subpool is created.
         */
        if (ctx->max_hpages != -1 || ctx->min_hpages != -1) {
                sbinfo->spool = hugepage_new_subpool(ctx->hstate,
@@ -1555,7 +1552,7 @@ static struct vfsmount *__init mount_one_hugetlbfs(struct hstate *h)
        }
        if (IS_ERR(mnt))
                pr_err("Cannot mount internal hugetlbfs for page size %luK",
-                      huge_page_size(h) >> 10);
+                      huge_page_size(h) / SZ_1K);
        return mnt;
 }
 
index b083961..6350d38 100644 (file)
@@ -1415,7 +1415,8 @@ static journal_t *journal_init_common(struct block_device *bdev,
        if (percpu_counter_init(&journal->j_checkpoint_jh_count, 0, GFP_KERNEL))
                goto err_cleanup;
 
-       if (register_shrinker(&journal->j_shrinker)) {
+       if (register_shrinker(&journal->j_shrinker, "jbd2-journal:(%u:%u)",
+                             MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev))) {
                percpu_counter_destroy(&journal->j_checkpoint_jh_count);
                goto err_cleanup;
        }
index 96f1d49..47ccfcb 100644 (file)
@@ -375,7 +375,7 @@ struct mb_cache *mb_cache_create(int bucket_bits)
        cache->c_shrink.count_objects = mb_cache_count;
        cache->c_shrink.scan_objects = mb_cache_scan;
        cache->c_shrink.seeks = DEFAULT_SEEKS;
-       if (register_shrinker(&cache->c_shrink)) {
+       if (register_shrinker(&cache->c_shrink, "mbcache-shrinker")) {
                kfree(cache->c_hash);
                kfree(cache);
                goto err_out;
index e7b34f7..a9bf09f 100644 (file)
@@ -1017,15 +1017,16 @@ int __init nfs4_xattr_cache_init(void)
        if (ret)
                goto out2;
 
-       ret = register_shrinker(&nfs4_xattr_cache_shrinker);
+       ret = register_shrinker(&nfs4_xattr_cache_shrinker, "nfs-xattr_cache");
        if (ret)
                goto out1;
 
-       ret = register_shrinker(&nfs4_xattr_entry_shrinker);
+       ret = register_shrinker(&nfs4_xattr_entry_shrinker, "nfs-xattr_entry");
        if (ret)
                goto out;
 
-       ret = register_shrinker(&nfs4_xattr_large_entry_shrinker);
+       ret = register_shrinker(&nfs4_xattr_large_entry_shrinker,
+                               "nfs-xattr_large_entry");
        if (!ret)
                return 0;
 
index 6ab5eeb..82944e1 100644 (file)
@@ -149,7 +149,7 @@ int __init register_nfs_fs(void)
        ret = nfs_register_sysctl();
        if (ret < 0)
                goto error_2;
-       ret = register_shrinker(&acl_shrinker);
+       ret = register_shrinker(&acl_shrinker, "nfs-acl");
        if (ret < 0)
                goto error_3;
 #ifdef CONFIG_NFS_V4_2
index 9cb2d59..a605c0e 100644 (file)
@@ -670,7 +670,7 @@ nfsd_file_cache_init(void)
                goto out_err;
        }
 
-       ret = register_shrinker(&nfsd_file_shrinker);
+       ret = register_shrinker(&nfsd_file_shrinker, "nfsd-filecache");
        if (ret) {
                pr_err("nfsd: failed to register nfsd_file_shrinker: %d\n", ret);
                goto out_lru;
index 7da88bd..9b31e11 100644 (file)
@@ -176,7 +176,8 @@ int nfsd_reply_cache_init(struct nfsd_net *nn)
        nn->nfsd_reply_cache_shrinker.scan_objects = nfsd_reply_cache_scan;
        nn->nfsd_reply_cache_shrinker.count_objects = nfsd_reply_cache_count;
        nn->nfsd_reply_cache_shrinker.seeks = 1;
-       status = register_shrinker(&nn->nfsd_reply_cache_shrinker);
+       status = register_shrinker(&nn->nfsd_reply_cache_shrinker,
+                                  "nfsd-reply:%s", nn->nfsd_name);
        if (status)
                goto out_stats_destroy;
 
index 2d04e34..a3398d0 100644 (file)
@@ -406,6 +406,7 @@ struct mem_size_stats {
        u64 pss_anon;
        u64 pss_file;
        u64 pss_shmem;
+       u64 pss_dirty;
        u64 pss_locked;
        u64 swap_pss;
 };
@@ -427,6 +428,7 @@ static void smaps_page_accumulate(struct mem_size_stats *mss,
                mss->pss_locked += pss;
 
        if (dirty || PageDirty(page)) {
+               mss->pss_dirty += pss;
                if (private)
                        mss->private_dirty += size;
                else
@@ -808,6 +810,7 @@ static void __show_smap(struct seq_file *m, const struct mem_size_stats *mss,
 {
        SEQ_PUT_DEC("Rss:            ", mss->resident);
        SEQ_PUT_DEC(" kB\nPss:            ", mss->pss >> PSS_SHIFT);
+       SEQ_PUT_DEC(" kB\nPss_Dirty:      ", mss->pss_dirty >> PSS_SHIFT);
        if (rollup_mode) {
                /*
                 * These are meaningful only for smaps_rollup, otherwise two of
@@ -860,7 +863,7 @@ static int show_smap(struct seq_file *m, void *v)
        __show_smap(m, &mss, false);
 
        seq_printf(m, "THPeligible:    %d\n",
-                  transparent_hugepage_active(vma));
+                  hugepage_vma_check(vma, vma->vm_flags, true, false));
 
        if (arch_pkeys_enabled())
                seq_printf(m, "ProtectionKey:  %8u\n", vma_pkey(vma));
@@ -1792,7 +1795,7 @@ static struct page *can_gather_numa_stats(pte_t pte, struct vm_area_struct *vma,
                return NULL;
 
        page = vm_normal_page(vma, addr, pte);
-       if (!page)
+       if (!page || is_zone_device_page(page))
                return NULL;
 
        if (PageReserved(page))
index 28966da..0427b44 100644 (file)
@@ -3002,7 +3002,7 @@ static int __init dquot_init(void)
        pr_info("VFS: Dquot-cache hash table entries: %ld (order %ld,"
                " %ld bytes)\n", nr_hash, order, (PAGE_SIZE << order));
 
-       if (register_shrinker(&dqcache_shrinker))
+       if (register_shrinker(&dqcache_shrinker, "dquota-cache"))
                panic("Cannot register dquot shrinker");
 
        return 0;
index 046a513..654912d 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/compat.h>
 #include <linux/mount.h>
 #include <linux/fs.h>
+#include <linux/dax.h>
 #include "internal.h"
 
 #include <linux/uaccess.h>
@@ -263,9 +264,11 @@ out_error:
  * If there's an error, then the usual negative error code is returned.
  * Otherwise returns 0 with *len set to the request length.
  */
-int generic_remap_file_range_prep(struct file *file_in, loff_t pos_in,
-                                 struct file *file_out, loff_t pos_out,
-                                 loff_t *len, unsigned int remap_flags)
+int
+__generic_remap_file_range_prep(struct file *file_in, loff_t pos_in,
+                               struct file *file_out, loff_t pos_out,
+                               loff_t *len, unsigned int remap_flags,
+                               const struct iomap_ops *dax_read_ops)
 {
        struct inode *inode_in = file_inode(file_in);
        struct inode *inode_out = file_inode(file_out);
@@ -325,8 +328,18 @@ int generic_remap_file_range_prep(struct file *file_in, loff_t pos_in,
        if (remap_flags & REMAP_FILE_DEDUP) {
                bool            is_same = false;
 
-               ret = vfs_dedupe_file_range_compare(file_in, pos_in,
-                               file_out, pos_out, *len, &is_same);
+               if (*len == 0)
+                       return 0;
+
+               if (!IS_DAX(inode_in))
+                       ret = vfs_dedupe_file_range_compare(file_in, pos_in,
+                                       file_out, pos_out, *len, &is_same);
+               else if (dax_read_ops)
+                       ret = dax_dedupe_file_range_compare(inode_in, pos_in,
+                                       inode_out, pos_out, *len, &is_same,
+                                       dax_read_ops);
+               else
+                       return -EINVAL;
                if (ret)
                        return ret;
                if (!is_same)
@@ -344,6 +357,14 @@ int generic_remap_file_range_prep(struct file *file_in, loff_t pos_in,
 
        return ret;
 }
+
+int generic_remap_file_range_prep(struct file *file_in, loff_t pos_in,
+                                 struct file *file_out, loff_t pos_out,
+                                 loff_t *len, unsigned int remap_flags)
+{
+       return __generic_remap_file_range_prep(file_in, pos_in, file_out,
+                                              pos_out, len, remap_flags, NULL);
+}
 EXPORT_SYMBOL(generic_remap_file_range_prep);
 
 loff_t do_clone_file_range(struct file *file_in, loff_t pos_in,
index 60f57c7..4fca665 100644 (file)
@@ -265,7 +265,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,
        s->s_shrink.count_objects = super_cache_count;
        s->s_shrink.batch = 1024;
        s->s_shrink.flags = SHRINKER_NUMA_AWARE | SHRINKER_MEMCG_AWARE;
-       if (prealloc_shrinker(&s->s_shrink))
+       if (prealloc_shrinker(&s->s_shrink, "sb-%s", type->name))
                goto fail;
        if (list_lru_init_memcg(&s->s_dentry_lru, &s->s_shrink))
                goto fail;
@@ -1288,6 +1288,8 @@ int get_tree_bdev(struct fs_context *fc,
        } else {
                s->s_mode = mode;
                snprintf(s->s_id, sizeof(s->s_id), "%pg", bdev);
+               shrinker_debugfs_rename(&s->s_shrink, "sb-%s:%s",
+                                       fc->fs_type->name, s->s_id);
                sb_set_blocksize(s, block_size(bdev));
                error = fill_super(s, fc);
                if (error) {
@@ -1363,6 +1365,8 @@ struct dentry *mount_bdev(struct file_system_type *fs_type,
        } else {
                s->s_mode = mode;
                snprintf(s->s_id, sizeof(s->s_id), "%pg", bdev);
+               shrinker_debugfs_rename(&s->s_shrink, "sb-%s:%s",
+                                       fs_type->name, s->s_id);
                sb_set_blocksize(s, block_size(bdev));
                error = fill_super(s, data, flags & SB_SILENT ? 1 : 0);
                if (error) {
index 0978d01..d0c9a09 100644 (file)
@@ -2430,7 +2430,7 @@ static int __init ubifs_init(void)
        if (!ubifs_inode_slab)
                return -ENOMEM;
 
-       err = register_shrinker(&ubifs_shrinker_info);
+       err = register_shrinker(&ubifs_shrinker_info, "ubifs-slab");
        if (err)
                goto out_slab;
 
index de86f5b..1c44bf7 100644 (file)
@@ -1925,10 +1925,8 @@ static int userfaultfd_api(struct userfaultfd_ctx *ctx,
        ret = -EFAULT;
        if (copy_from_user(&uffdio_api, buf, sizeof(uffdio_api)))
                goto out;
-       features = uffdio_api.features;
-       ret = -EINVAL;
-       if (uffdio_api.api != UFFD_API || (features & ~UFFD_API_FEATURES))
-               goto err_out;
+       /* Ignore unsupported features (userspace built against newer kernel) */
+       features = uffdio_api.features & UFFD_API_FEATURES;
        ret = -EPERM;
        if ((features & UFFD_FEATURE_EVENT_FORK) && !capable(CAP_SYS_PTRACE))
                goto err_out;
index 1131dd0..03135a1 100644 (file)
@@ -130,6 +130,11 @@ xfs-$(CONFIG_SYSCTL)               += xfs_sysctl.o
 xfs-$(CONFIG_COMPAT)           += xfs_ioctl32.o
 xfs-$(CONFIG_EXPORTFS_BLOCK_OPS)       += xfs_pnfs.o
 
+# notify failure
+ifeq ($(CONFIG_MEMORY_FAILURE),y)
+xfs-$(CONFIG_FS_DAX)           += xfs_notify_failure.o
+endif
+
 # online scrub/repair
 ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y)
 
index 584afe0..dde3464 100644 (file)
@@ -5,6 +5,7 @@
  */
 #include "xfs.h"
 #include <linux/backing-dev.h>
+#include <linux/dax.h>
 
 #include "xfs_shared.h"
 #include "xfs_format.h"
@@ -1944,7 +1945,7 @@ xfs_free_buftarg(
        list_lru_destroy(&btp->bt_lru);
 
        blkdev_issue_flush(btp->bt_bdev);
-       fs_put_dax(btp->bt_daxdev);
+       fs_put_dax(btp->bt_daxdev, btp->bt_mount);
 
        kmem_free(btp);
 }
@@ -1991,13 +1992,18 @@ xfs_alloc_buftarg(
        struct block_device     *bdev)
 {
        xfs_buftarg_t           *btp;
+       const struct dax_holder_operations *ops = NULL;
 
+#if defined(CONFIG_FS_DAX) && defined(CONFIG_MEMORY_FAILURE)
+       ops = &xfs_dax_holder_operations;
+#endif
        btp = kmem_zalloc(sizeof(*btp), KM_NOFS);
 
        btp->bt_mount = mp;
        btp->bt_dev =  bdev->bd_dev;
        btp->bt_bdev = bdev;
-       btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off);
+       btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off,
+                                           mp, ops);
 
        /*
         * Buffer IO error rate limiting. Limit it to no more than 10 messages
@@ -2019,7 +2025,8 @@ xfs_alloc_buftarg(
        btp->bt_shrinker.scan_objects = xfs_buftarg_shrink_scan;
        btp->bt_shrinker.seeks = DEFAULT_SEEKS;
        btp->bt_shrinker.flags = SHRINKER_NUMA_AWARE;
-       if (register_shrinker(&btp->bt_shrinker))
+       if (register_shrinker(&btp->bt_shrinker, "xfs-buf:%s",
+                             mp->m_super->s_id))
                goto error_pcpu;
        return btp;
 
index 8d9b14d..aa7e458 100644 (file)
@@ -25,6 +25,7 @@
 #include "xfs_iomap.h"
 #include "xfs_reflink.h"
 
+#include <linux/dax.h>
 #include <linux/falloc.h>
 #include <linux/backing-dev.h>
 #include <linux/mman.h>
@@ -669,7 +670,7 @@ xfs_file_dax_write(
        pos = iocb->ki_pos;
 
        trace_xfs_file_dax_write(iocb, from);
-       ret = dax_iomap_rw(iocb, from, &xfs_direct_write_iomap_ops);
+       ret = dax_iomap_rw(iocb, from, &xfs_dax_write_iomap_ops);
        if (ret > 0 && iocb->ki_pos > i_size_read(inode)) {
                i_size_write(inode, iocb->ki_pos);
                error = xfs_setfilesize(ip, pos, ret);
@@ -806,7 +807,7 @@ xfs_wait_dax_page(
        xfs_ilock(ip, XFS_MMAPLOCK_EXCL);
 }
 
-static int
+int
 xfs_break_dax_layouts(
        struct inode            *inode,
        bool                    *retry)
@@ -1253,6 +1254,31 @@ xfs_file_llseek(
        return vfs_setpos(file, offset, inode->i_sb->s_maxbytes);
 }
 
+#ifdef CONFIG_FS_DAX
+static int
+xfs_dax_fault(
+       struct vm_fault         *vmf,
+       enum page_entry_size    pe_size,
+       bool                    write_fault,
+       pfn_t                   *pfn)
+{
+       return dax_iomap_fault(vmf, pe_size, pfn, NULL,
+                       (write_fault && !vmf->cow_page) ?
+                               &xfs_dax_write_iomap_ops :
+                               &xfs_read_iomap_ops);
+}
+#else
+static int
+xfs_dax_fault(
+       struct vm_fault         *vmf,
+       enum page_entry_size    pe_size,
+       bool                    write_fault,
+       pfn_t                   *pfn)
+{
+       return 0;
+}
+#endif
+
 /*
  * Locking for serialisation of IO during page faults. This results in a lock
  * ordering of:
@@ -1284,10 +1310,7 @@ __xfs_filemap_fault(
                pfn_t pfn;
 
                xfs_ilock(XFS_I(inode), XFS_MMAPLOCK_SHARED);
-               ret = dax_iomap_fault(vmf, pe_size, &pfn, NULL,
-                               (write_fault && !vmf->cow_page) ?
-                                &xfs_direct_write_iomap_ops :
-                                &xfs_read_iomap_ops);
+               ret = xfs_dax_fault(vmf, pe_size, write_fault, &pfn);
                if (ret & VM_FAULT_NEEDDSYNC)
                        ret = dax_finish_sync_fault(vmf, pe_size, pfn);
                xfs_iunlock(XFS_I(inode), XFS_MMAPLOCK_SHARED);
index 5fe9af2..13851c0 100644 (file)
@@ -531,6 +531,9 @@ xfs_do_force_shutdown(
        } else if (flags & SHUTDOWN_CORRUPT_INCORE) {
                tag = XFS_PTAG_SHUTDOWN_CORRUPT;
                why = "Corruption of in-memory data";
+       } else if (flags & SHUTDOWN_CORRUPT_ONDISK) {
+               tag = XFS_PTAG_SHUTDOWN_CORRUPT;
+               why = "Corruption of on-disk metadata";
        } else {
                tag = XFS_PTAG_SHUTDOWN_IOERROR;
                why = "Metadata I/O Error";
index e3b2304..2bbe791 100644 (file)
@@ -2221,5 +2221,5 @@ xfs_inodegc_register_shrinker(
        shrink->flags = SHRINKER_NONSLAB;
        shrink->batch = XFS_INODEGC_SHRINKER_BATCH;
 
-       return register_shrinker(shrink);
+       return register_shrinker(shrink, "xfs-inodegc:%s", mp->m_super->s_id);
 }
index 3022918..28493c8 100644 (file)
@@ -3447,6 +3447,50 @@ retry:
        return 0;
 }
 
+static int
+xfs_mmaplock_two_inodes_and_break_dax_layout(
+       struct xfs_inode        *ip1,
+       struct xfs_inode        *ip2)
+{
+       int                     error;
+       bool                    retry;
+       struct page             *page;
+
+       if (ip1->i_ino > ip2->i_ino)
+               swap(ip1, ip2);
+
+again:
+       retry = false;
+       /* Lock the first inode */
+       xfs_ilock(ip1, XFS_MMAPLOCK_EXCL);
+       error = xfs_break_dax_layouts(VFS_I(ip1), &retry);
+       if (error || retry) {
+               xfs_iunlock(ip1, XFS_MMAPLOCK_EXCL);
+               if (error == 0 && retry)
+                       goto again;
+               return error;
+       }
+
+       if (ip1 == ip2)
+               return 0;
+
+       /* Nested lock the second inode */
+       xfs_ilock(ip2, xfs_lock_inumorder(XFS_MMAPLOCK_EXCL, 1));
+       /*
+        * We cannot use xfs_break_dax_layouts() directly here because it may
+        * need to unlock & lock the XFS_MMAPLOCK_EXCL which is not suitable
+        * for this nested lock case.
+        */
+       page = dax_layout_busy_page(VFS_I(ip2)->i_mapping);
+       if (page && page_ref_count(page) != 1) {
+               xfs_iunlock(ip2, XFS_MMAPLOCK_EXCL);
+               xfs_iunlock(ip1, XFS_MMAPLOCK_EXCL);
+               goto again;
+       }
+
+       return 0;
+}
+
 /*
  * Lock two inodes so that userspace cannot initiate I/O via file syscalls or
  * mmap activity.
@@ -3461,8 +3505,19 @@ xfs_ilock2_io_mmap(
        ret = xfs_iolock_two_inodes_and_break_layout(VFS_I(ip1), VFS_I(ip2));
        if (ret)
                return ret;
-       filemap_invalidate_lock_two(VFS_I(ip1)->i_mapping,
-                                   VFS_I(ip2)->i_mapping);
+
+       if (IS_DAX(VFS_I(ip1)) && IS_DAX(VFS_I(ip2))) {
+               ret = xfs_mmaplock_two_inodes_and_break_dax_layout(ip1, ip2);
+               if (ret) {
+                       inode_unlock(VFS_I(ip2));
+                       if (ip1 != ip2)
+                               inode_unlock(VFS_I(ip1));
+                       return ret;
+               }
+       } else
+               filemap_invalidate_lock_two(VFS_I(ip1)->i_mapping,
+                                           VFS_I(ip2)->i_mapping);
+
        return 0;
 }
 
@@ -3472,8 +3527,14 @@ xfs_iunlock2_io_mmap(
        struct xfs_inode        *ip1,
        struct xfs_inode        *ip2)
 {
-       filemap_invalidate_unlock_two(VFS_I(ip1)->i_mapping,
-                                     VFS_I(ip2)->i_mapping);
+       if (IS_DAX(VFS_I(ip1)) && IS_DAX(VFS_I(ip2))) {
+               xfs_iunlock(ip2, XFS_MMAPLOCK_EXCL);
+               if (ip1 != ip2)
+                       xfs_iunlock(ip1, XFS_MMAPLOCK_EXCL);
+       } else
+               filemap_invalidate_unlock_two(VFS_I(ip1)->i_mapping,
+                                             VFS_I(ip2)->i_mapping);
+
        inode_unlock(VFS_I(ip2));
        if (ip1 != ip2)
                inode_unlock(VFS_I(ip1));
index 4d626f4..fa780f0 100644 (file)
@@ -531,6 +531,7 @@ xfs_itruncate_extents(
 }
 
 /* from xfs_file.c */
+int    xfs_break_dax_layouts(struct inode *inode, bool *retry);
 int    xfs_break_layouts(struct inode *inode, uint *iolock,
                enum layout_break_reason reason);
 
index 2817d3d..07da039 100644 (file)
@@ -773,7 +773,8 @@ xfs_direct_write_iomap_begin(
 
                /* may drop and re-acquire the ilock */
                error = xfs_reflink_allocate_cow(ip, &imap, &cmap, &shared,
-                               &lockmode, flags & IOMAP_DIRECT);
+                               &lockmode,
+                               (flags & IOMAP_DIRECT) || IS_DAX(inode));
                if (error)
                        goto out_unlock;
                if (shared)
@@ -867,6 +868,33 @@ const struct iomap_ops xfs_direct_write_iomap_ops = {
        .iomap_begin            = xfs_direct_write_iomap_begin,
 };
 
+static int
+xfs_dax_write_iomap_end(
+       struct inode            *inode,
+       loff_t                  pos,
+       loff_t                  length,
+       ssize_t                 written,
+       unsigned                flags,
+       struct iomap            *iomap)
+{
+       struct xfs_inode        *ip = XFS_I(inode);
+
+       if (!xfs_is_cow_inode(ip))
+               return 0;
+
+       if (!written) {
+               xfs_reflink_cancel_cow_range(ip, pos, length, true);
+               return 0;
+       }
+
+       return xfs_reflink_end_cow(ip, pos, written);
+}
+
+const struct iomap_ops xfs_dax_write_iomap_ops = {
+       .iomap_begin    = xfs_direct_write_iomap_begin,
+       .iomap_end      = xfs_dax_write_iomap_end,
+};
+
 static int
 xfs_buffered_write_iomap_begin(
        struct inode            *inode,
index e88dc16..c782e8c 100644 (file)
@@ -51,5 +51,6 @@ extern const struct iomap_ops xfs_direct_write_iomap_ops;
 extern const struct iomap_ops xfs_read_iomap_ops;
 extern const struct iomap_ops xfs_seek_iomap_ops;
 extern const struct iomap_ops xfs_xattr_iomap_ops;
+extern const struct iomap_ops xfs_dax_write_iomap_ops;
 
 #endif /* __XFS_IOMAP_H__*/
index d2eaebd..8aca2cc 100644 (file)
@@ -454,6 +454,7 @@ void xfs_do_force_shutdown(struct xfs_mount *mp, uint32_t flags, char *fname,
 #define SHUTDOWN_LOG_IO_ERROR  (1u << 1) /* write attempt to the log failed */
 #define SHUTDOWN_FORCE_UMOUNT  (1u << 2) /* shutdown from a forced unmount */
 #define SHUTDOWN_CORRUPT_INCORE        (1u << 3) /* corrupt in-memory structures */
+#define SHUTDOWN_CORRUPT_ONDISK        (1u << 4)  /* corrupt metadata on device */
 
 #define XFS_SHUTDOWN_STRINGS \
        { SHUTDOWN_META_IO_ERROR,       "metadata_io" }, \
diff --git a/fs/xfs/xfs_notify_failure.c b/fs/xfs/xfs_notify_failure.c
new file mode 100644 (file)
index 0000000..69d9c83
--- /dev/null
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Fujitsu.  All Rights Reserved.
+ */
+
+#include "xfs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_alloc.h"
+#include "xfs_bit.h"
+#include "xfs_btree.h"
+#include "xfs_inode.h"
+#include "xfs_icache.h"
+#include "xfs_rmap.h"
+#include "xfs_rmap_btree.h"
+#include "xfs_rtalloc.h"
+#include "xfs_trans.h"
+#include "xfs_ag.h"
+
+#include <linux/mm.h>
+#include <linux/dax.h>
+
+struct failure_info {
+       xfs_agblock_t           startblock;
+       xfs_extlen_t            blockcount;
+       int                     mf_flags;
+};
+
+static pgoff_t
+xfs_failure_pgoff(
+       struct xfs_mount                *mp,
+       const struct xfs_rmap_irec      *rec,
+       const struct failure_info       *notify)
+{
+       loff_t                          pos = XFS_FSB_TO_B(mp, rec->rm_offset);
+
+       if (notify->startblock > rec->rm_startblock)
+               pos += XFS_FSB_TO_B(mp,
+                               notify->startblock - rec->rm_startblock);
+       return pos >> PAGE_SHIFT;
+}
+
+static unsigned long
+xfs_failure_pgcnt(
+       struct xfs_mount                *mp,
+       const struct xfs_rmap_irec      *rec,
+       const struct failure_info       *notify)
+{
+       xfs_agblock_t                   end_rec;
+       xfs_agblock_t                   end_notify;
+       xfs_agblock_t                   start_cross;
+       xfs_agblock_t                   end_cross;
+
+       start_cross = max(rec->rm_startblock, notify->startblock);
+
+       end_rec = rec->rm_startblock + rec->rm_blockcount;
+       end_notify = notify->startblock + notify->blockcount;
+       end_cross = min(end_rec, end_notify);
+
+       return XFS_FSB_TO_B(mp, end_cross - start_cross) >> PAGE_SHIFT;
+}
+
+static int
+xfs_dax_failure_fn(
+       struct xfs_btree_cur            *cur,
+       const struct xfs_rmap_irec      *rec,
+       void                            *data)
+{
+       struct xfs_mount                *mp = cur->bc_mp;
+       struct xfs_inode                *ip;
+       struct failure_info             *notify = data;
+       int                             error = 0;
+
+       if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) ||
+           (rec->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))) {
+               xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
+               return -EFSCORRUPTED;
+       }
+
+       /* Get files that incore, filter out others that are not in use. */
+       error = xfs_iget(mp, cur->bc_tp, rec->rm_owner, XFS_IGET_INCORE,
+                        0, &ip);
+       /* Continue the rmap query if the inode isn't incore */
+       if (error == -ENODATA)
+               return 0;
+       if (error)
+               return error;
+
+       error = mf_dax_kill_procs(VFS_I(ip)->i_mapping,
+                                 xfs_failure_pgoff(mp, rec, notify),
+                                 xfs_failure_pgcnt(mp, rec, notify),
+                                 notify->mf_flags);
+       xfs_irele(ip);
+       return error;
+}
+
+static int
+xfs_dax_notify_ddev_failure(
+       struct xfs_mount        *mp,
+       xfs_daddr_t             daddr,
+       xfs_daddr_t             bblen,
+       int                     mf_flags)
+{
+       struct xfs_trans        *tp = NULL;
+       struct xfs_btree_cur    *cur = NULL;
+       struct xfs_buf          *agf_bp = NULL;
+       int                     error = 0;
+       xfs_fsblock_t           fsbno = XFS_DADDR_TO_FSB(mp, daddr);
+       xfs_agnumber_t          agno = XFS_FSB_TO_AGNO(mp, fsbno);
+       xfs_fsblock_t           end_fsbno = XFS_DADDR_TO_FSB(mp, daddr + bblen);
+       xfs_agnumber_t          end_agno = XFS_FSB_TO_AGNO(mp, end_fsbno);
+
+       error = xfs_trans_alloc_empty(mp, &tp);
+       if (error)
+               return error;
+
+       for (; agno <= end_agno; agno++) {
+               struct xfs_rmap_irec    ri_low = { };
+               struct xfs_rmap_irec    ri_high;
+               struct failure_info     notify;
+               struct xfs_agf          *agf;
+               xfs_agblock_t           agend;
+               struct xfs_perag        *pag;
+
+               pag = xfs_perag_get(mp, agno);
+               error = xfs_alloc_read_agf(pag, tp, 0, &agf_bp);
+               if (error) {
+                       xfs_perag_put(pag);
+                       break;
+               }
+
+               cur = xfs_rmapbt_init_cursor(mp, tp, agf_bp, pag);
+
+               /*
+                * Set the rmap range from ri_low to ri_high, which represents
+                * a [start, end] where we looking for the files or metadata.
+                */
+               memset(&ri_high, 0xFF, sizeof(ri_high));
+               ri_low.rm_startblock = XFS_FSB_TO_AGBNO(mp, fsbno);
+               if (agno == end_agno)
+                       ri_high.rm_startblock = XFS_FSB_TO_AGBNO(mp, end_fsbno);
+
+               agf = agf_bp->b_addr;
+               agend = min(be32_to_cpu(agf->agf_length),
+                               ri_high.rm_startblock);
+               notify.startblock = ri_low.rm_startblock;
+               notify.blockcount = agend - ri_low.rm_startblock;
+
+               error = xfs_rmap_query_range(cur, &ri_low, &ri_high,
+                               xfs_dax_failure_fn, &notify);
+               xfs_btree_del_cursor(cur, error);
+               xfs_trans_brelse(tp, agf_bp);
+               xfs_perag_put(pag);
+               if (error)
+                       break;
+
+               fsbno = XFS_AGB_TO_FSB(mp, agno + 1, 0);
+       }
+
+       xfs_trans_cancel(tp);
+       return error;
+}
+
+static int
+xfs_dax_notify_failure(
+       struct dax_device       *dax_dev,
+       u64                     offset,
+       u64                     len,
+       int                     mf_flags)
+{
+       struct xfs_mount        *mp = dax_holder(dax_dev);
+       u64                     ddev_start;
+       u64                     ddev_end;
+
+       if (!(mp->m_sb.sb_flags & SB_BORN)) {
+               xfs_warn(mp, "filesystem is not ready for notify_failure()!");
+               return -EIO;
+       }
+
+       if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_daxdev == dax_dev) {
+               xfs_warn(mp,
+                        "notify_failure() not supported on realtime device!");
+               return -EOPNOTSUPP;
+       }
+
+       if (mp->m_logdev_targp && mp->m_logdev_targp->bt_daxdev == dax_dev &&
+           mp->m_logdev_targp != mp->m_ddev_targp) {
+               xfs_err(mp, "ondisk log corrupt, shutting down fs!");
+               xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
+               return -EFSCORRUPTED;
+       }
+
+       if (!xfs_has_rmapbt(mp)) {
+               xfs_warn(mp, "notify_failure() needs rmapbt enabled!");
+               return -EOPNOTSUPP;
+       }
+
+       ddev_start = mp->m_ddev_targp->bt_dax_part_off;
+       ddev_end = ddev_start + bdev_nr_bytes(mp->m_ddev_targp->bt_bdev) - 1;
+
+       /* Ignore the range out of filesystem area */
+       if (offset + len < ddev_start)
+               return -ENXIO;
+       if (offset > ddev_end)
+               return -ENXIO;
+
+       /* Calculate the real range when it touches the boundary */
+       if (offset > ddev_start)
+               offset -= ddev_start;
+       else {
+               len -= ddev_start - offset;
+               offset = 0;
+       }
+       if (offset + len > ddev_end)
+               len -= ddev_end - offset;
+
+       return xfs_dax_notify_ddev_failure(mp, BTOBB(offset), BTOBB(len),
+                       mf_flags);
+}
+
+const struct dax_holder_operations xfs_dax_holder_operations = {
+       .notify_failure         = xfs_dax_notify_failure,
+};
index 57dd3b7..fbff792 100644 (file)
@@ -677,7 +677,8 @@ xfs_qm_init_quotainfo(
        qinf->qi_shrinker.seeks = DEFAULT_SEEKS;
        qinf->qi_shrinker.flags = SHRINKER_NUMA_AWARE;
 
-       error = register_shrinker(&qinf->qi_shrinker);
+       error = register_shrinker(&qinf->qi_shrinker, "xfs-qm:%s",
+                                 mp->m_super->s_id);
        if (error)
                goto out_free_inos;
 
index 724806c..e17a84e 100644 (file)
@@ -1364,12 +1364,16 @@ xfs_reflink_remap_prep(
        if (XFS_IS_REALTIME_INODE(src) || XFS_IS_REALTIME_INODE(dest))
                goto out_unlock;
 
-       /* Don't share DAX file data for now. */
-       if (IS_DAX(inode_in) || IS_DAX(inode_out))
+       /* Don't share DAX file data with non-DAX file. */
+       if (IS_DAX(inode_in) != IS_DAX(inode_out))
                goto out_unlock;
 
-       ret = generic_remap_file_range_prep(file_in, pos_in, file_out, pos_out,
-                       len, remap_flags);
+       if (!IS_DAX(inode_in))
+               ret = generic_remap_file_range_prep(file_in, pos_in, file_out,
+                               pos_out, len, remap_flags);
+       else
+               ret = dax_remap_file_range_prep(file_in, pos_in, file_out,
+                               pos_out, len, remap_flags, &xfs_read_iomap_ops);
        if (ret || *len == 0)
                goto out_unlock;
 
index 3d27ba1..9ac5981 100644 (file)
@@ -351,8 +351,10 @@ xfs_setup_dax_always(
                goto disable_dax;
        }
 
-       if (xfs_has_reflink(mp)) {
-               xfs_alert(mp, "DAX and reflink cannot be used together!");
+       if (xfs_has_reflink(mp) &&
+           bdev_is_partition(mp->m_ddev_targp->bt_bdev)) {
+               xfs_alert(mp,
+                       "DAX and reflink cannot work with multi-partitions!");
                return -EINVAL;
        }
 
index 3cd5a51..364e2c2 100644 (file)
@@ -92,6 +92,7 @@ extern xfs_agnumber_t xfs_set_inode_alloc(struct xfs_mount *,
 
 extern const struct export_operations xfs_export_operations;
 extern const struct quotactl_ops xfs_quotactl_operations;
+extern const struct dax_holder_operations xfs_dax_holder_operations;
 
 extern void xfs_reinit_percpu_counters(struct xfs_mount *mp);
 
index d452071..439815c 100644 (file)
@@ -140,12 +140,6 @@ static inline bool mapping_can_writeback(struct address_space *mapping)
        return inode_to_bdi(mapping->host)->capabilities & BDI_CAP_WRITEBACK;
 }
 
-static inline int bdi_sched_wait(void *word)
-{
-       schedule();
-       return 0;
-}
-
 #ifdef CONFIG_CGROUP_WRITEBACK
 
 struct bdi_writeback *wb_get_lookup(struct backing_dev_info *bdi,
@@ -235,18 +229,6 @@ wb_get_create_current(struct backing_dev_info *bdi, gfp_t gfp)
        return wb;
 }
 
-/**
- * inode_to_wb_is_valid - test whether an inode has a wb associated
- * @inode: inode of interest
- *
- * Returns %true if @inode has a wb associated.  May be called without any
- * locking.
- */
-static inline bool inode_to_wb_is_valid(struct inode *inode)
-{
-       return inode->i_wb;
-}
-
 /**
  * inode_to_wb - determine the wb of an inode
  * @inode: inode of interest
@@ -345,11 +327,6 @@ wb_get_create_current(struct backing_dev_info *bdi, gfp_t gfp)
        return &bdi->wb;
 }
 
-static inline bool inode_to_wb_is_valid(struct inode *inode)
-{
-       return true;
-}
-
 static inline struct bdi_writeback *inode_to_wb(struct inode *inode)
 {
        return &inode_to_bdi(inode)->wb;
index 7c62da3..7b1f4a4 100644 (file)
@@ -86,6 +86,8 @@ struct damon_target {
  * @DAMOS_PAGEOUT:     Call ``madvise()`` for the region with MADV_PAGEOUT.
  * @DAMOS_HUGEPAGE:    Call ``madvise()`` for the region with MADV_HUGEPAGE.
  * @DAMOS_NOHUGEPAGE:  Call ``madvise()`` for the region with MADV_NOHUGEPAGE.
+ * @DAMOS_LRU_PRIO:    Prioritize the region on its LRU lists.
+ * @DAMOS_LRU_DEPRIO:  Deprioritize the region on its LRU lists.
  * @DAMOS_STAT:                Do nothing but count the stat.
  * @NR_DAMOS_ACTIONS:  Total number of DAMOS actions
  */
@@ -95,6 +97,8 @@ enum damos_action {
        DAMOS_PAGEOUT,
        DAMOS_HUGEPAGE,
        DAMOS_NOHUGEPAGE,
+       DAMOS_LRU_PRIO,
+       DAMOS_LRU_DEPRIO,
        DAMOS_STAT,             /* Do nothing but only record the stat */
        NR_DAMOS_ACTIONS,
 };
@@ -397,7 +401,6 @@ struct damon_callback {
  * detail.
  *
  * @kdamond:           Kernel thread who does the monitoring.
- * @kdamond_stop:      Notifies whether kdamond should stop.
  * @kdamond_lock:      Mutex for the synchronizations with @kdamond.
  *
  * For each monitoring context, one kernel thread for the monitoring is
@@ -406,14 +409,14 @@ struct damon_callback {
  * Once started, the monitoring thread runs until explicitly required to be
  * terminated or every monitoring target is invalid.  The validity of the
  * targets is checked via the &damon_operations.target_valid of @ops.  The
- * termination can also be explicitly requested by writing non-zero to
- * @kdamond_stop.  The thread sets @kdamond to NULL when it terminates.
- * Therefore, users can know whether the monitoring is ongoing or terminated by
- * reading @kdamond.  Reads and writes to @kdamond and @kdamond_stop from
- * outside of the monitoring thread must be protected by @kdamond_lock.
+ * termination can also be explicitly requested by calling damon_stop().
+ * The thread sets @kdamond to NULL when it terminates. Therefore, users can
+ * know whether the monitoring is ongoing or terminated by reading @kdamond.
+ * Reads and writes to @kdamond from outside of the monitoring thread must
+ * be protected by @kdamond_lock.
  *
- * Note that the monitoring thread protects only @kdamond and @kdamond_stop via
- * @kdamond_lock.  Accesses to other fields must be protected by themselves.
+ * Note that the monitoring thread protects only @kdamond via @kdamond_lock.
+ * Accesses to other fields must be protected by themselves.
  *
  * @ops:       Set of monitoring operations for given use cases.
  * @callback:  Set of callbacks for monitoring events notifications.
@@ -526,6 +529,12 @@ bool damon_is_registered_ops(enum damon_ops_id id);
 int damon_register_ops(struct damon_operations *ops);
 int damon_select_ops(struct damon_ctx *ctx, enum damon_ops_id id);
 
+static inline bool damon_target_has_pid(const struct damon_ctx *ctx)
+{
+       return ctx->ops.id == DAMON_OPS_VADDR || ctx->ops.id == DAMON_OPS_FVADDR;
+}
+
+
 int damon_start(struct damon_ctx **ctxs, int nr_ctxs, bool exclusive);
 int damon_stop(struct damon_ctx **ctxs, int nr_ctxs);
 
index e7b8163..ba98533 100644 (file)
@@ -43,8 +43,21 @@ struct dax_operations {
                        void *addr, size_t bytes, struct iov_iter *iter);
 };
 
+struct dax_holder_operations {
+       /*
+        * notify_failure - notify memory failure into inner holder device
+        * @dax_dev: the dax device which contains the holder
+        * @offset: offset on this dax device where memory failure occurs
+        * @len: length of this memory failure event
+        * @flags: action flags for memory failure handler
+        */
+       int (*notify_failure)(struct dax_device *dax_dev, u64 offset,
+                       u64 len, int mf_flags);
+};
+
 #if IS_ENABLED(CONFIG_DAX)
 struct dax_device *alloc_dax(void *private, const struct dax_operations *ops);
+void *dax_holder(struct dax_device *dax_dev);
 void put_dax(struct dax_device *dax_dev);
 void kill_dax(struct dax_device *dax_dev);
 void dax_write_cache(struct dax_device *dax_dev, bool wc);
@@ -66,6 +79,10 @@ static inline bool daxdev_mapping_supported(struct vm_area_struct *vma,
        return dax_synchronous(dax_dev);
 }
 #else
+static inline void *dax_holder(struct dax_device *dax_dev)
+{
+       return NULL;
+}
 static inline struct dax_device *alloc_dax(void *private,
                const struct dax_operations *ops)
 {
@@ -114,12 +131,9 @@ struct writeback_control;
 #if defined(CONFIG_BLOCK) && defined(CONFIG_FS_DAX)
 int dax_add_host(struct dax_device *dax_dev, struct gendisk *disk);
 void dax_remove_host(struct gendisk *disk);
-struct dax_device *fs_dax_get_by_bdev(struct block_device *bdev,
-               u64 *start_off);
-static inline void fs_put_dax(struct dax_device *dax_dev)
-{
-       put_dax(dax_dev);
-}
+struct dax_device *fs_dax_get_by_bdev(struct block_device *bdev, u64 *start_off,
+               void *holder, const struct dax_holder_operations *ops);
+void fs_put_dax(struct dax_device *dax_dev, void *holder);
 #else
 static inline int dax_add_host(struct dax_device *dax_dev, struct gendisk *disk)
 {
@@ -129,11 +143,12 @@ static inline void dax_remove_host(struct gendisk *disk)
 {
 }
 static inline struct dax_device *fs_dax_get_by_bdev(struct block_device *bdev,
-               u64 *start_off)
+               u64 *start_off, void *holder,
+               const struct dax_holder_operations *ops)
 {
        return NULL;
 }
-static inline void fs_put_dax(struct dax_device *dax_dev)
+static inline void fs_put_dax(struct dax_device *dax_dev, void *holder)
 {
 }
 #endif /* CONFIG_BLOCK && CONFIG_FS_DAX */
@@ -146,6 +161,10 @@ struct page *dax_layout_busy_page(struct address_space *mapping);
 struct page *dax_layout_busy_page_range(struct address_space *mapping, loff_t start, loff_t end);
 dax_entry_t dax_lock_page(struct page *page);
 void dax_unlock_page(struct page *page, dax_entry_t cookie);
+dax_entry_t dax_lock_mapping_entry(struct address_space *mapping,
+               unsigned long index, struct page **page);
+void dax_unlock_mapping_entry(struct address_space *mapping,
+               unsigned long index, dax_entry_t cookie);
 #else
 static inline struct page *dax_layout_busy_page(struct address_space *mapping)
 {
@@ -173,6 +192,17 @@ static inline dax_entry_t dax_lock_page(struct page *page)
 static inline void dax_unlock_page(struct page *page, dax_entry_t cookie)
 {
 }
+
+static inline dax_entry_t dax_lock_mapping_entry(struct address_space *mapping,
+               unsigned long index, struct page **page)
+{
+       return 0;
+}
+
+static inline void dax_unlock_mapping_entry(struct address_space *mapping,
+               unsigned long index, dax_entry_t cookie)
+{
+}
 #endif
 
 int dax_zero_range(struct inode *inode, loff_t pos, loff_t len, bool *did_zero,
@@ -203,6 +233,8 @@ size_t dax_copy_to_iter(struct dax_device *dax_dev, pgoff_t pgoff, void *addr,
                size_t bytes, struct iov_iter *i);
 int dax_zero_page_range(struct dax_device *dax_dev, pgoff_t pgoff,
                        size_t nr_pages);
+int dax_holder_notify_failure(struct dax_device *dax_dev, u64 off, u64 len,
+               int mf_flags);
 void dax_flush(struct dax_device *dax_dev, void *addr, size_t size);
 
 ssize_t dax_iomap_rw(struct kiocb *iocb, struct iov_iter *iter,
@@ -214,6 +246,14 @@ vm_fault_t dax_finish_sync_fault(struct vm_fault *vmf,
 int dax_delete_mapping_entry(struct address_space *mapping, pgoff_t index);
 int dax_invalidate_mapping_entry_sync(struct address_space *mapping,
                                      pgoff_t index);
+int dax_dedupe_file_range_compare(struct inode *src, loff_t srcoff,
+                                 struct inode *dest, loff_t destoff,
+                                 loff_t len, bool *is_same,
+                                 const struct iomap_ops *ops);
+int dax_remap_file_range_prep(struct file *file_in, loff_t pos_in,
+                             struct file *file_out, loff_t pos_out,
+                             loff_t *len, unsigned int remap_flags,
+                             const struct iomap_ops *ops);
 static inline bool dax_mapping(struct address_space *mapping)
 {
        return mapping->host && IS_DAX(mapping->host);
index cc64873..a3522bd 100644 (file)
@@ -74,6 +74,7 @@ struct fsverity_operations;
 struct fs_context;
 struct fs_parameter_spec;
 struct fileattr;
+struct iomap_ops;
 
 extern void __init inode_init(void);
 extern void __init inode_init_early(void);
@@ -2200,10 +2201,13 @@ extern ssize_t vfs_copy_file_range(struct file *, loff_t , struct file *,
 extern ssize_t generic_copy_file_range(struct file *file_in, loff_t pos_in,
                                       struct file *file_out, loff_t pos_out,
                                       size_t len, unsigned int flags);
-extern int generic_remap_file_range_prep(struct file *file_in, loff_t pos_in,
-                                        struct file *file_out, loff_t pos_out,
-                                        loff_t *count,
-                                        unsigned int remap_flags);
+int __generic_remap_file_range_prep(struct file *file_in, loff_t pos_in,
+                                   struct file *file_out, loff_t pos_out,
+                                   loff_t *len, unsigned int remap_flags,
+                                   const struct iomap_ops *dax_read_ops);
+int generic_remap_file_range_prep(struct file *file_in, loff_t pos_in,
+                                 struct file *file_out, loff_t pos_out,
+                                 loff_t *count, unsigned int remap_flags);
 extern loff_t do_clone_file_range(struct file *file_in, loff_t pos_in,
                                  struct file *file_out, loff_t pos_out,
                                  loff_t len, unsigned int remap_flags);
index 56d6a01..177b079 100644 (file)
@@ -243,6 +243,16 @@ static inline void clear_highpage(struct page *page)
        kunmap_local(kaddr);
 }
 
+static inline void clear_highpage_kasan_tagged(struct page *page)
+{
+       u8 tag;
+
+       tag = page_kasan_tag(page);
+       page_kasan_tag_reset(page);
+       clear_highpage(page);
+       page_kasan_tag_set(page, tag);
+}
+
 #ifndef __HAVE_ARCH_TAG_CLEAR_HIGHPAGE
 
 static inline void tag_clear_highpage(struct page *page)
@@ -336,19 +346,6 @@ static inline void memcpy_page(struct page *dst_page, size_t dst_off,
        kunmap_local(dst);
 }
 
-static inline void memmove_page(struct page *dst_page, size_t dst_off,
-                              struct page *src_page, size_t src_off,
-                              size_t len)
-{
-       char *dst = kmap_local_page(dst_page);
-       char *src = kmap_local_page(src_page);
-
-       VM_BUG_ON(dst_off + len > PAGE_SIZE || src_off + len > PAGE_SIZE);
-       memmove(dst + dst_off, src + src_off, len);
-       kunmap_local(src);
-       kunmap_local(dst);
-}
-
 static inline void memset_page(struct page *page, size_t offset, int val,
                               size_t len)
 {
index d5a6f10..126a365 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Authors: Jérôme Glisse <jglisse@redhat.com>
  *
- * See Documentation/vm/hmm.rst for reasons and overview of what HMM is.
+ * See Documentation/mm/hmm.rst for reasons and overview of what HMM is.
  */
 #ifndef LINUX_HMM_H
 #define LINUX_HMM_H
@@ -100,7 +100,7 @@ struct hmm_range {
 };
 
 /*
- * Please see Documentation/vm/hmm.rst for how to use the range API.
+ * Please see Documentation/mm/hmm.rst for how to use the range API.
  */
 int hmm_range_fault(struct hmm_range *range);
 
index 4ddaf6a..768e526 100644 (file)
@@ -116,9 +116,30 @@ extern struct kobj_attribute shmem_enabled_attr;
 
 extern unsigned long transparent_hugepage_flags;
 
+#define hugepage_flags_enabled()                                              \
+       (transparent_hugepage_flags &                                  \
+        ((1<<TRANSPARENT_HUGEPAGE_FLAG) |                     \
+         (1<<TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG)))
+#define hugepage_flags_always()                                \
+       (transparent_hugepage_flags &                   \
+        (1<<TRANSPARENT_HUGEPAGE_FLAG))
+
+/*
+ * Do the below checks:
+ *   - For file vma, check if the linear page offset of vma is
+ *     HPAGE_PMD_NR aligned within the file.  The hugepage is
+ *     guaranteed to be hugepage-aligned within the file, but we must
+ *     check that the PMD-aligned addresses in the VMA map to
+ *     PMD-aligned offsets within the file, else the hugepage will
+ *     not be PMD-mappable.
+ *   - For all vmas, check if the haddr is in an aligned HPAGE_PMD_SIZE
+ *     area.
+ */
 static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
-               unsigned long haddr)
+               unsigned long addr)
 {
+       unsigned long haddr;
+
        /* Don't have to check pgoff for anonymous vma */
        if (!vma_is_anonymous(vma)) {
                if (!IS_ALIGNED((vma->vm_start >> PAGE_SHIFT) - vma->vm_pgoff,
@@ -126,53 +147,13 @@ static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
                        return false;
        }
 
-       if (haddr < vma->vm_start || haddr + HPAGE_PMD_SIZE > vma->vm_end)
-               return false;
-       return true;
-}
+       haddr = addr & HPAGE_PMD_MASK;
 
-static inline bool transhuge_vma_enabled(struct vm_area_struct *vma,
-                                         unsigned long vm_flags)
-{
-       /* Explicitly disabled through madvise. */
-       if ((vm_flags & VM_NOHUGEPAGE) ||
-           test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags))
+       if (haddr < vma->vm_start || haddr + HPAGE_PMD_SIZE > vma->vm_end)
                return false;
        return true;
 }
 
-/*
- * to be used on vmas which are known to support THP.
- * Use transparent_hugepage_active otherwise
- */
-static inline bool __transparent_hugepage_enabled(struct vm_area_struct *vma)
-{
-
-       /*
-        * If the hardware/firmware marked hugepage support disabled.
-        */
-       if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_NEVER_DAX))
-               return false;
-
-       if (!transhuge_vma_enabled(vma, vma->vm_flags))
-               return false;
-
-       if (vma_is_temporary_stack(vma))
-               return false;
-
-       if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_FLAG))
-               return true;
-
-       if (vma_is_dax(vma))
-               return true;
-
-       if (transparent_hugepage_flags &
-                               (1 << TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG))
-               return !!(vma->vm_flags & VM_HUGEPAGE);
-
-       return false;
-}
-
 static inline bool file_thp_enabled(struct vm_area_struct *vma)
 {
        struct inode *inode;
@@ -187,7 +168,9 @@ static inline bool file_thp_enabled(struct vm_area_struct *vma)
               !inode_is_open_for_write(inode) && S_ISREG(inode->i_mode);
 }
 
-bool transparent_hugepage_active(struct vm_area_struct *vma);
+bool hugepage_vma_check(struct vm_area_struct *vma,
+                       unsigned long vm_flags,
+                       bool smaps, bool in_pf);
 
 #define transparent_hugepage_use_zero_page()                           \
        (transparent_hugepage_flags &                                   \
@@ -290,7 +273,7 @@ static inline bool is_huge_zero_page(struct page *page)
 
 static inline bool is_huge_zero_pmd(pmd_t pmd)
 {
-       return READ_ONCE(huge_zero_pfn) == pmd_pfn(pmd) && pmd_present(pmd);
+       return pmd_present(pmd) && READ_ONCE(huge_zero_pfn) == pmd_pfn(pmd);
 }
 
 static inline bool is_huge_zero_pud(pud_t pud)
@@ -311,8 +294,8 @@ static inline bool thp_migration_supported(void)
 static inline struct list_head *page_deferred_list(struct page *page)
 {
        /*
-        * Global or memcg deferred list in the second tail pages is
-        * occupied by compound_head.
+        * See organization of tail pages of compound page in
+        * "struct page" definition.
         */
        return &page[2].deferred_list;
 }
@@ -331,24 +314,15 @@ static inline bool folio_test_pmd_mappable(struct folio *folio)
        return false;
 }
 
-static inline bool __transparent_hugepage_enabled(struct vm_area_struct *vma)
-{
-       return false;
-}
-
-static inline bool transparent_hugepage_active(struct vm_area_struct *vma)
-{
-       return false;
-}
-
 static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
-               unsigned long haddr)
+               unsigned long addr)
 {
        return false;
 }
 
-static inline bool transhuge_vma_enabled(struct vm_area_struct *vma,
-                                         unsigned long vm_flags)
+static inline bool hugepage_vma_check(struct vm_area_struct *vma,
+                                      unsigned long vm_flags,
+                                      bool smaps, bool in_pf)
 {
        return false;
 }
index e4cff27..4cdfce9 100644 (file)
@@ -152,7 +152,7 @@ void __unmap_hugepage_range_final(struct mmu_gather *tlb,
                          struct page *ref_page, zap_flags_t zap_flags);
 void hugetlb_report_meminfo(struct seq_file *);
 int hugetlb_report_node_meminfo(char *buf, int len, int nid);
-void hugetlb_show_meminfo(void);
+void hugetlb_show_meminfo_node(int nid);
 unsigned long hugetlb_total_pages(void);
 vm_fault_t hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,
                        unsigned long address, unsigned int flags);
@@ -170,7 +170,7 @@ bool hugetlb_reserve_pages(struct inode *inode, long from, long to,
                                                vm_flags_t vm_flags);
 long hugetlb_unreserve_pages(struct inode *inode, long start, long end,
                                                long freed);
-bool isolate_huge_page(struct page *page, struct list_head *list);
+int isolate_hugetlb(struct page *page, struct list_head *list);
 int get_hwpoison_huge_page(struct page *page, bool *hugetlb);
 int get_huge_page_for_hwpoison(unsigned long pfn, int flags);
 void putback_active_hugepage(struct page *page);
@@ -194,8 +194,9 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,
                        unsigned long addr, unsigned long sz);
 pte_t *huge_pte_offset(struct mm_struct *mm,
                       unsigned long addr, unsigned long sz);
+unsigned long hugetlb_mask_last_page(struct hstate *h);
 int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma,
-                               unsigned long *addr, pte_t *ptep);
+                               unsigned long addr, pte_t *ptep);
 void adjust_range_if_pmd_sharing_possible(struct vm_area_struct *vma,
                                unsigned long *start, unsigned long *end);
 struct page *follow_huge_addr(struct mm_struct *mm, unsigned long address,
@@ -242,7 +243,7 @@ static inline struct address_space *hugetlb_page_mapping_lock_write(
 
 static inline int huge_pmd_unshare(struct mm_struct *mm,
                                        struct vm_area_struct *vma,
-                                       unsigned long *addr, pte_t *ptep)
+                                       unsigned long addr, pte_t *ptep)
 {
        return 0;
 }
@@ -297,7 +298,7 @@ static inline int hugetlb_report_node_meminfo(char *buf, int len, int nid)
        return 0;
 }
 
-static inline void hugetlb_show_meminfo(void)
+static inline void hugetlb_show_meminfo_node(int nid)
 {
 }
 
@@ -376,9 +377,9 @@ static inline pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr,
        return NULL;
 }
 
-static inline bool isolate_huge_page(struct page *page, struct list_head *list)
+static inline int isolate_hugetlb(struct page *page, struct list_head *list)
 {
-       return false;
+       return -EBUSY;
 }
 
 static inline int get_hwpoison_huge_page(struct page *page, bool *hugetlb)
@@ -903,14 +904,6 @@ static inline void hugetlb_count_sub(long l, struct mm_struct *mm)
        atomic_long_sub(l, &mm->hugetlb_usage);
 }
 
-#ifndef set_huge_swap_pte_at
-static inline void set_huge_swap_pte_at(struct mm_struct *mm, unsigned long addr,
-                                       pte_t *ptep, pte_t pte, unsigned long sz)
-{
-       set_huge_pte_at(mm, addr, ptep, pte);
-}
-#endif
-
 #ifndef huge_ptep_modify_prot_start
 #define huge_ptep_modify_prot_start huge_ptep_modify_prot_start
 static inline pte_t huge_ptep_modify_prot_start(struct vm_area_struct *vma,
@@ -1094,11 +1087,6 @@ static inline void hugetlb_count_sub(long l, struct mm_struct *mm)
 {
 }
 
-static inline void set_huge_swap_pte_at(struct mm_struct *mm, unsigned long addr,
-                                       pte_t *ptep, pte_t pte, unsigned long sz)
-{
-}
-
 static inline pte_t huge_ptep_clear_flush(struct vm_area_struct *vma,
                                          unsigned long addr, pte_t *ptep)
 {
index 392d34c..384f034 100644 (file)
@@ -10,8 +10,6 @@ extern struct attribute_group khugepaged_attr_group;
 extern int khugepaged_init(void);
 extern void khugepaged_destroy(void);
 extern int start_stop_khugepaged(void);
-extern bool hugepage_vma_check(struct vm_area_struct *vma,
-                              unsigned long vm_flags);
 extern void __khugepaged_enter(struct mm_struct *mm);
 extern void __khugepaged_exit(struct mm_struct *mm);
 extern void khugepaged_enter_vma(struct vm_area_struct *vma,
@@ -26,20 +24,6 @@ static inline void collapse_pte_mapped_thp(struct mm_struct *mm,
 }
 #endif
 
-#define khugepaged_enabled()                                          \
-       (transparent_hugepage_flags &                                  \
-        ((1<<TRANSPARENT_HUGEPAGE_FLAG) |                     \
-         (1<<TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG)))
-#define khugepaged_always()                            \
-       (transparent_hugepage_flags &                   \
-        (1<<TRANSPARENT_HUGEPAGE_FLAG))
-#define khugepaged_req_madv()                                  \
-       (transparent_hugepage_flags &                           \
-        (1<<TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG))
-#define khugepaged_defrag()                                    \
-       (transparent_hugepage_flags &                           \
-        (1<<TRANSPARENT_HUGEPAGE_DEFRAG_KHUGEPAGED_FLAG))
-
 static inline void khugepaged_fork(struct mm_struct *mm, struct mm_struct *oldmm)
 {
        if (test_bit(MMF_VM_HUGEPAGE, &oldmm->flags))
@@ -51,16 +35,6 @@ static inline void khugepaged_exit(struct mm_struct *mm)
        if (test_bit(MMF_VM_HUGEPAGE, &mm->flags))
                __khugepaged_exit(mm);
 }
-
-static inline void khugepaged_enter(struct vm_area_struct *vma,
-                                  unsigned long vm_flags)
-{
-       if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
-           khugepaged_enabled()) {
-               if (hugepage_vma_check(vma, vm_flags))
-                       __khugepaged_enter(vma->vm_mm);
-       }
-}
 #else /* CONFIG_TRANSPARENT_HUGEPAGE */
 static inline void khugepaged_fork(struct mm_struct *mm, struct mm_struct *oldmm)
 {
@@ -68,10 +42,6 @@ static inline void khugepaged_fork(struct mm_struct *mm, struct mm_struct *oldmm
 static inline void khugepaged_exit(struct mm_struct *mm)
 {
 }
-static inline void khugepaged_enter(struct vm_area_struct *vma,
-                                   unsigned long vm_flags)
-{
-}
 static inline void khugepaged_enter_vma(struct vm_area_struct *vma,
                                        unsigned long vm_flags)
 {
index 34684b2..6a3cd1b 100644 (file)
@@ -29,10 +29,9 @@ extern void kmemleak_not_leak(const void *ptr) __ref;
 extern void kmemleak_ignore(const void *ptr) __ref;
 extern void kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp) __ref;
 extern void kmemleak_no_scan(const void *ptr) __ref;
-extern void kmemleak_alloc_phys(phys_addr_t phys, size_t size, int min_count,
+extern void kmemleak_alloc_phys(phys_addr_t phys, size_t size,
                                gfp_t gfp) __ref;
 extern void kmemleak_free_part_phys(phys_addr_t phys, size_t size) __ref;
-extern void kmemleak_not_leak_phys(phys_addr_t phys) __ref;
 extern void kmemleak_ignore_phys(phys_addr_t phys) __ref;
 
 static inline void kmemleak_alloc_recursive(const void *ptr, size_t size,
@@ -107,15 +106,12 @@ static inline void kmemleak_no_scan(const void *ptr)
 {
 }
 static inline void kmemleak_alloc_phys(phys_addr_t phys, size_t size,
-                                      int min_count, gfp_t gfp)
+                                      gfp_t gfp)
 {
 }
 static inline void kmemleak_free_part_phys(phys_addr_t phys, size_t size)
 {
 }
-static inline void kmemleak_not_leak_phys(phys_addr_t phys)
-{
-}
 static inline void kmemleak_ignore_phys(phys_addr_t phys)
 {
 }
index 9ecead1..4d31ce5 100644 (file)
@@ -837,6 +837,15 @@ static inline unsigned short mem_cgroup_id(struct mem_cgroup *memcg)
 }
 struct mem_cgroup *mem_cgroup_from_id(unsigned short id);
 
+#ifdef CONFIG_SHRINKER_DEBUG
+static inline unsigned long mem_cgroup_ino(struct mem_cgroup *memcg)
+{
+       return memcg ? cgroup_ino(memcg->css.cgroup) : 0;
+}
+
+struct mem_cgroup *mem_cgroup_get_from_ino(unsigned long ino);
+#endif
+
 static inline struct mem_cgroup *mem_cgroup_from_seq(struct seq_file *m)
 {
        return mem_cgroup_from_css(seq_css(m));
@@ -1343,6 +1352,18 @@ static inline struct mem_cgroup *mem_cgroup_from_id(unsigned short id)
        return NULL;
 }
 
+#ifdef CONFIG_SHRINKER_DEBUG
+static inline unsigned long mem_cgroup_ino(struct mem_cgroup *memcg)
+{
+       return 0;
+}
+
+static inline struct mem_cgroup *mem_cgroup_get_from_ino(unsigned long ino)
+{
+       return NULL;
+}
+#endif
+
 static inline struct mem_cgroup *mem_cgroup_from_seq(struct seq_file *m)
 {
        return NULL;
@@ -1740,6 +1761,7 @@ static inline int memcg_kmem_id(struct mem_cgroup *memcg)
 }
 
 struct mem_cgroup *mem_cgroup_from_obj(void *p);
+struct mem_cgroup *mem_cgroup_from_slab_obj(void *p);
 
 static inline void count_objcg_event(struct obj_cgroup *objcg,
                                     enum vm_event_item idx)
@@ -1755,6 +1777,42 @@ static inline void count_objcg_event(struct obj_cgroup *objcg,
        rcu_read_unlock();
 }
 
+/**
+ * get_mem_cgroup_from_obj - get a memcg associated with passed kernel object.
+ * @p: pointer to object from which memcg should be extracted. It can be NULL.
+ *
+ * Retrieves the memory group into which the memory of the pointed kernel
+ * object is accounted. If memcg is found, its reference is taken.
+ * If a passed kernel object is uncharged, or if proper memcg cannot be found,
+ * as well as if mem_cgroup is disabled, NULL is returned.
+ *
+ * Return: valid memcg pointer with taken reference or NULL.
+ */
+static inline struct mem_cgroup *get_mem_cgroup_from_obj(void *p)
+{
+       struct mem_cgroup *memcg;
+
+       rcu_read_lock();
+       do {
+               memcg = mem_cgroup_from_obj(p);
+       } while (memcg && !css_tryget(&memcg->css));
+       rcu_read_unlock();
+       return memcg;
+}
+
+/**
+ * mem_cgroup_or_root - always returns a pointer to a valid memory cgroup.
+ * @memcg: pointer to a valid memory cgroup or NULL.
+ *
+ * If passed argument is not NULL, returns it without any additional checks
+ * and changes. Otherwise, root_mem_cgroup is returned.
+ *
+ * NOTE: root_mem_cgroup can be NULL during early boot.
+ */
+static inline struct mem_cgroup *mem_cgroup_or_root(struct mem_cgroup *memcg)
+{
+       return memcg ? memcg : root_mem_cgroup;
+}
 #else
 static inline bool mem_cgroup_kmem_disabled(void)
 {
@@ -1798,7 +1856,12 @@ static inline int memcg_kmem_id(struct mem_cgroup *memcg)
 
 static inline struct mem_cgroup *mem_cgroup_from_obj(void *p)
 {
-       return NULL;
+       return NULL;
+}
+
+static inline struct mem_cgroup *mem_cgroup_from_slab_obj(void *p)
+{
+       return NULL;
 }
 
 static inline void count_objcg_event(struct obj_cgroup *objcg,
@@ -1806,6 +1869,15 @@ static inline void count_objcg_event(struct obj_cgroup *objcg,
 {
 }
 
+static inline struct mem_cgroup *get_mem_cgroup_from_obj(void *p)
+{
+       return NULL;
+}
+
+static inline struct mem_cgroup *mem_cgroup_or_root(struct mem_cgroup *memcg)
+{
+       return NULL;
+}
 #endif /* CONFIG_MEMCG_KMEM */
 
 #if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_ZSWAP)
index 20d7edf..e0b2209 100644 (file)
@@ -351,13 +351,4 @@ void arch_remove_linear_mapping(u64 start, u64 size);
 extern bool mhp_supports_memmap_on_memory(unsigned long size);
 #endif /* CONFIG_MEMORY_HOTPLUG */
 
-#ifdef CONFIG_MHP_MEMMAP_ON_MEMORY
-bool mhp_memmap_on_memory(void);
-#else
-static inline bool mhp_memmap_on_memory(void)
-{
-       return false;
-}
-#endif
-
 #endif /* __LINUX_MEMORY_HOTPLUG_H */
index 8af304f..1901049 100644 (file)
@@ -2,7 +2,7 @@
 #ifndef _LINUX_MEMREMAP_H_
 #define _LINUX_MEMREMAP_H_
 
-#include <linux/mm.h>
+#include <linux/mmzone.h>
 #include <linux/range.h>
 #include <linux/ioport.h>
 #include <linux/percpu-refcount.h>
@@ -39,7 +39,14 @@ struct vmem_altmap {
  * must be treated as an opaque object, rather than a "normal" struct page.
  *
  * A more complete discussion of unaddressable memory may be found in
- * include/linux/hmm.h and Documentation/vm/hmm.rst.
+ * include/linux/hmm.h and Documentation/mm/hmm.rst.
+ *
+ * MEMORY_DEVICE_COHERENT:
+ * Device memory that is cache coherent from device and CPU point of view. This
+ * is used on platforms that have an advanced system bus (like CAPI or CXL). A
+ * driver can hotplug the device memory using ZONE_DEVICE and with that memory
+ * type. Any page of a process can be migrated to such memory. However no one
+ * should be allowed to pin such memory so that it can always be evicted.
  *
  * MEMORY_DEVICE_FS_DAX:
  * Host memory that has similar access semantics as System RAM i.e. DMA
@@ -61,6 +68,7 @@ struct vmem_altmap {
 enum memory_type {
        /* 0 is reserved to catch uninitialized type fields */
        MEMORY_DEVICE_PRIVATE = 1,
+       MEMORY_DEVICE_COHERENT,
        MEMORY_DEVICE_FS_DAX,
        MEMORY_DEVICE_GENERIC,
        MEMORY_DEVICE_PCI_P2PDMA,
@@ -79,6 +87,18 @@ struct dev_pagemap_ops {
         * the page back to a CPU accessible page.
         */
        vm_fault_t (*migrate_to_ram)(struct vm_fault *vmf);
+
+       /*
+        * Handle the memory failure happens on a range of pfns.  Notify the
+        * processes who are using these pfns, and try to recover the data on
+        * them if necessary.  The mf_flags is finally passed to the recover
+        * function through the whole notify routine.
+        *
+        * When this is not implemented, or it returns -EOPNOTSUPP, the caller
+        * will fall back to a common handler called mf_generic_kill_procs().
+        */
+       int (*memory_failure)(struct dev_pagemap *pgmap, unsigned long pfn,
+                             unsigned long nr_pages, int mf_flags);
 };
 
 #define PGMAP_ALTMAP_VALID     (1 << 0)
@@ -150,6 +170,17 @@ static inline bool is_pci_p2pdma_page(const struct page *page)
                page->pgmap->type == MEMORY_DEVICE_PCI_P2PDMA;
 }
 
+static inline bool is_device_coherent_page(const struct page *page)
+{
+       return is_zone_device_page(page) &&
+               page->pgmap->type == MEMORY_DEVICE_COHERENT;
+}
+
+static inline bool folio_is_device_coherent(const struct folio *folio)
+{
+       return is_device_coherent_page(&folio->page);
+}
+
 #ifdef CONFIG_ZONE_DEVICE
 void *memremap_pages(struct dev_pagemap *pgmap, int nid);
 void memunmap_pages(struct dev_pagemap *pgmap);
index ae5bb67..22c0a0c 100644 (file)
@@ -182,6 +182,7 @@ static inline unsigned long migrate_pfn(unsigned long pfn)
 enum migrate_vma_direction {
        MIGRATE_VMA_SELECT_SYSTEM = 1 << 0,
        MIGRATE_VMA_SELECT_DEVICE_PRIVATE = 1 << 1,
+       MIGRATE_VMA_SELECT_DEVICE_COHERENT = 1 << 2,
 };
 
 struct migrate_vma {
index a37b8c0..18e0147 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/sched.h>
 #include <linux/pgtable.h>
 #include <linux/kasan.h>
+#include <linux/memremap.h>
 
 struct mempolicy;
 struct anon_vma;
@@ -427,7 +428,6 @@ extern unsigned int kobjsize(const void *objp);
  * mapping from the currently active vm_flags protection bits (the
  * low four bits) to a page protection mask..
  */
-extern pgprot_t protection_map[16];
 
 /*
  * The default fault flags that should be used by most of the
@@ -858,7 +858,7 @@ static inline struct folio *virt_to_folio(const void *x)
        return page_folio(page);
 }
 
-void __put_page(struct page *page);
+void __folio_put(struct folio *folio);
 
 void put_pages_list(struct list_head *pages);
 
@@ -895,11 +895,7 @@ static inline void set_compound_page_dtor(struct page *page,
        page[1].compound_dtor = compound_dtor;
 }
 
-static inline void destroy_compound_page(struct page *page)
-{
-       VM_BUG_ON_PAGE(page[1].compound_dtor >= NR_COMPOUND_DTORS, page);
-       compound_page_dtors[page[1].compound_dtor](page);
-}
+void destroy_large_folio(struct folio *folio);
 
 static inline int head_compound_pincount(struct page *head)
 {
@@ -1052,84 +1048,6 @@ vm_fault_t finish_mkwrite_fault(struct vm_fault *vmf);
  *   back into memory.
  */
 
-/*
- * The zone field is never updated after free_area_init_core()
- * sets it, so none of the operations on it need to be atomic.
- */
-
-/* Page flags: | [SECTION] | [NODE] | ZONE | [LAST_CPUPID] | ... | FLAGS | */
-#define SECTIONS_PGOFF         ((sizeof(unsigned long)*8) - SECTIONS_WIDTH)
-#define NODES_PGOFF            (SECTIONS_PGOFF - NODES_WIDTH)
-#define ZONES_PGOFF            (NODES_PGOFF - ZONES_WIDTH)
-#define LAST_CPUPID_PGOFF      (ZONES_PGOFF - LAST_CPUPID_WIDTH)
-#define KASAN_TAG_PGOFF                (LAST_CPUPID_PGOFF - KASAN_TAG_WIDTH)
-
-/*
- * Define the bit shifts to access each section.  For non-existent
- * sections we define the shift as 0; that plus a 0 mask ensures
- * the compiler will optimise away reference to them.
- */
-#define SECTIONS_PGSHIFT       (SECTIONS_PGOFF * (SECTIONS_WIDTH != 0))
-#define NODES_PGSHIFT          (NODES_PGOFF * (NODES_WIDTH != 0))
-#define ZONES_PGSHIFT          (ZONES_PGOFF * (ZONES_WIDTH != 0))
-#define LAST_CPUPID_PGSHIFT    (LAST_CPUPID_PGOFF * (LAST_CPUPID_WIDTH != 0))
-#define KASAN_TAG_PGSHIFT      (KASAN_TAG_PGOFF * (KASAN_TAG_WIDTH != 0))
-
-/* NODE:ZONE or SECTION:ZONE is used to ID a zone for the buddy allocator */
-#ifdef NODE_NOT_IN_PAGE_FLAGS
-#define ZONEID_SHIFT           (SECTIONS_SHIFT + ZONES_SHIFT)
-#define ZONEID_PGOFF           ((SECTIONS_PGOFF < ZONES_PGOFF)? \
-                                               SECTIONS_PGOFF : ZONES_PGOFF)
-#else
-#define ZONEID_SHIFT           (NODES_SHIFT + ZONES_SHIFT)
-#define ZONEID_PGOFF           ((NODES_PGOFF < ZONES_PGOFF)? \
-                                               NODES_PGOFF : ZONES_PGOFF)
-#endif
-
-#define ZONEID_PGSHIFT         (ZONEID_PGOFF * (ZONEID_SHIFT != 0))
-
-#define ZONES_MASK             ((1UL << ZONES_WIDTH) - 1)
-#define NODES_MASK             ((1UL << NODES_WIDTH) - 1)
-#define SECTIONS_MASK          ((1UL << SECTIONS_WIDTH) - 1)
-#define LAST_CPUPID_MASK       ((1UL << LAST_CPUPID_SHIFT) - 1)
-#define KASAN_TAG_MASK         ((1UL << KASAN_TAG_WIDTH) - 1)
-#define ZONEID_MASK            ((1UL << ZONEID_SHIFT) - 1)
-
-static inline enum zone_type page_zonenum(const struct page *page)
-{
-       ASSERT_EXCLUSIVE_BITS(page->flags, ZONES_MASK << ZONES_PGSHIFT);
-       return (page->flags >> ZONES_PGSHIFT) & ZONES_MASK;
-}
-
-static inline enum zone_type folio_zonenum(const struct folio *folio)
-{
-       return page_zonenum(&folio->page);
-}
-
-#ifdef CONFIG_ZONE_DEVICE
-static inline bool is_zone_device_page(const struct page *page)
-{
-       return page_zonenum(page) == ZONE_DEVICE;
-}
-extern void memmap_init_zone_device(struct zone *, unsigned long,
-                                   unsigned long, struct dev_pagemap *);
-#else
-static inline bool is_zone_device_page(const struct page *page)
-{
-       return false;
-}
-#endif
-
-static inline bool folio_is_zone_device(const struct folio *folio)
-{
-       return is_zone_device_page(&folio->page);
-}
-
-static inline bool is_zone_movable_page(const struct page *page)
-{
-       return page_zonenum(page) == ZONE_MOVABLE;
-}
-
 #if defined(CONFIG_ZONE_DEVICE) && defined(CONFIG_FS_DAX)
 DECLARE_STATIC_KEY_FALSE(devmap_managed_key);
 
@@ -1204,7 +1122,7 @@ static inline __must_check bool try_get_page(struct page *page)
 static inline void folio_put(struct folio *folio)
 {
        if (folio_put_testzero(folio))
-               __put_page(&folio->page);
+               __folio_put(folio);
 }
 
 /**
@@ -1224,7 +1142,26 @@ static inline void folio_put(struct folio *folio)
 static inline void folio_put_refs(struct folio *folio, int refs)
 {
        if (folio_ref_sub_and_test(folio, refs))
-               __put_page(&folio->page);
+               __folio_put(folio);
+}
+
+void release_pages(struct page **pages, int nr);
+
+/**
+ * folios_put - Decrement the reference count on an array of folios.
+ * @folios: The folios.
+ * @nr: How many folios there are.
+ *
+ * Like folio_put(), but for an array of folios.  This is more efficient
+ * than writing the loop yourself as it will optimise the locks which
+ * need to be taken if the folios are freed.
+ *
+ * Context: May be called in process or interrupt context, but not in NMI
+ * context.  May be called while holding a spinlock.
+ */
+static inline void folios_put(struct folio **folios, unsigned int nr)
+{
+       release_pages((struct page **)folios, nr);
 }
 
 static inline void put_page(struct page *page)
@@ -1599,7 +1536,7 @@ static inline bool page_needs_cow_for_dma(struct vm_area_struct *vma,
 
 /* MIGRATE_CMA and ZONE_MOVABLE do not allow pin pages */
 #ifdef CONFIG_MIGRATION
-static inline bool is_pinnable_page(struct page *page)
+static inline bool is_longterm_pinnable_page(struct page *page)
 {
 #ifdef CONFIG_CMA
        int mt = get_pageblock_migratetype(page);
@@ -1607,18 +1544,20 @@ static inline bool is_pinnable_page(struct page *page)
        if (mt == MIGRATE_CMA || mt == MIGRATE_ISOLATE)
                return false;
 #endif
-       return !is_zone_movable_page(page) || is_zero_pfn(page_to_pfn(page));
+       return !(is_device_coherent_page(page) ||
+                is_zone_movable_page(page) ||
+                is_zero_pfn(page_to_pfn(page)));
 }
 #else
-static inline bool is_pinnable_page(struct page *page)
+static inline bool is_longterm_pinnable_page(struct page *page)
 {
        return true;
 }
 #endif
 
-static inline bool folio_is_pinnable(struct folio *folio)
+static inline bool folio_is_longterm_pinnable(struct folio *folio)
 {
-       return is_pinnable_page(&folio->page);
+       return is_longterm_pinnable_page(&folio->page);
 }
 
 static inline void set_page_zone(struct page *page, enum zone_type zone)
@@ -1969,8 +1908,12 @@ extern unsigned long move_page_tables(struct vm_area_struct *vma,
  * for now all the callers are only use one of the flags at the same
  * time.
  */
-/* Whether we should allow dirty bit accounting */
-#define  MM_CP_DIRTY_ACCT                  (1UL << 0)
+/*
+ * Whether we should manually check if we can map individual PTEs writable,
+ * because something (e.g., COW, uffd-wp) blocks that from happening for all
+ * PTEs automatically in a writable mapping.
+ */
+#define  MM_CP_TRY_CHANGE_WRITABLE        (1UL << 0)
 /* Whether this protection change is for NUMA hints */
 #define  MM_CP_PROT_NUMA                   (1UL << 1)
 /* Whether this change is for write protecting */
@@ -3241,6 +3184,8 @@ enum mf_flags {
        MF_UNPOISON = 1 << 4,
        MF_SW_SIMULATED = 1 << 5,
 };
+int mf_dax_kill_procs(struct address_space *mapping, pgoff_t index,
+                     unsigned long count, int mf_flags);
 extern int memory_failure(unsigned long pfn, int flags);
 extern void memory_failure_queue(unsigned long pfn, int flags);
 extern void memory_failure_queue_kick(int cpu);
index c29ab4c..cf97f38 100644 (file)
@@ -87,6 +87,7 @@ struct page {
                         */
                        union {
                                struct list_head lru;
+
                                /* Or, for the Unevictable "LRU list" slot */
                                struct {
                                        /* Always even, to negate PageTail */
@@ -94,6 +95,10 @@ struct page {
                                        /* Count page's or folio's mlocks */
                                        unsigned int mlock_count;
                                };
+
+                               /* Or, free page */
+                               struct list_head buddy_list;
+                               struct list_head pcp_list;
                        };
                        /* See page-flags.h for PAGE_MAPPING_FLAGS */
                        struct address_space *mapping;
@@ -729,6 +734,7 @@ typedef __bitwise unsigned int vm_fault_t;
  * @VM_FAULT_NEEDDSYNC:                ->fault did not modify page tables and needs
  *                             fsync() to complete (for synchronous page faults
  *                             in DAX)
+ * @VM_FAULT_COMPLETED:                ->fault completed, meanwhile mmap lock released
  * @VM_FAULT_HINDEX_MASK:      mask HINDEX value
  *
  */
@@ -746,6 +752,7 @@ enum vm_fault_reason {
        VM_FAULT_FALLBACK       = (__force vm_fault_t)0x000800,
        VM_FAULT_DONE_COW       = (__force vm_fault_t)0x001000,
        VM_FAULT_NEEDDSYNC      = (__force vm_fault_t)0x002000,
+       VM_FAULT_COMPLETED      = (__force vm_fault_t)0x004000,
        VM_FAULT_HINDEX_MASK    = (__force vm_fault_t)0x0f0000,
 };
 
index 45fc2c8..d6c06e1 100644 (file)
@@ -198,7 +198,7 @@ struct mmu_notifier_ops {
         * invalidate_range_start()/end() notifiers, as
         * invalidate_range() already catches the points in time when an
         * external TLB range needs to be flushed. For more in depth
-        * discussion on this see Documentation/vm/mmu_notifier.rst
+        * discussion on this see Documentation/mm/mmu_notifier.rst
         *
         * Note that this function might be called with just a sub-range
         * of what was passed to invalidate_range_start()/end(), if
index aab7035..e24b40c 100644 (file)
@@ -355,15 +355,18 @@ enum zone_watermarks {
 };
 
 /*
- * One per migratetype for each PAGE_ALLOC_COSTLY_ORDER plus one additional
- * for pageblock size for THP if configured.
+ * One per migratetype for each PAGE_ALLOC_COSTLY_ORDER. One additional list
+ * for THP which will usually be GFP_MOVABLE. Even if it is another type,
+ * it should not contribute to serious fragmentation causing THP allocation
+ * failures.
  */
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
 #define NR_PCP_THP 1
 #else
 #define NR_PCP_THP 0
 #endif
-#define NR_PCP_LISTS (MIGRATE_PCPTYPES * (PAGE_ALLOC_COSTLY_ORDER + 1 + NR_PCP_THP))
+#define NR_LOWORDER_PCP_LISTS (MIGRATE_PCPTYPES * (PAGE_ALLOC_COSTLY_ORDER + 1))
+#define NR_PCP_LISTS (NR_LOWORDER_PCP_LISTS + NR_PCP_THP)
 
 /*
  * Shift to encode migratetype and order in the same integer, with order
@@ -379,6 +382,7 @@ enum zone_watermarks {
 
 /* Fields and list protected by pagesets local_lock in page_alloc.c */
 struct per_cpu_pages {
+       spinlock_t lock;        /* Protects lists field */
        int count;              /* number of pages in the list */
        int high;               /* high watermark, emptying needed */
        int batch;              /* chunk size for buddy add/remove */
@@ -389,7 +393,7 @@ struct per_cpu_pages {
 
        /* Lists of pages, one per migrate type stored on the pcp-lists */
        struct list_head lists[NR_PCP_LISTS];
-};
+} ____cacheline_aligned_in_smp;
 
 struct per_cpu_zonestat {
 #ifdef CONFIG_SMP
@@ -591,8 +595,8 @@ struct zone {
         * give them a chance of being in the same cacheline.
         *
         * Write access to present_pages at runtime should be protected by
-        * mem_hotplug_begin/end(). Any reader who can't tolerant drift of
-        * present_pages should get_online_mems() to get a stable value.
+        * mem_hotplug_begin/done(). Any reader who can't tolerant drift of
+        * present_pages should use get_online_mems() to get a stable value.
         */
        atomic_long_t           managed_pages;
        unsigned long           spanned_pages;
@@ -730,6 +734,86 @@ static inline bool zone_is_empty(struct zone *zone)
        return zone->spanned_pages == 0;
 }
 
+#ifndef BUILD_VDSO32_64
+/*
+ * The zone field is never updated after free_area_init_core()
+ * sets it, so none of the operations on it need to be atomic.
+ */
+
+/* Page flags: | [SECTION] | [NODE] | ZONE | [LAST_CPUPID] | ... | FLAGS | */
+#define SECTIONS_PGOFF         ((sizeof(unsigned long)*8) - SECTIONS_WIDTH)
+#define NODES_PGOFF            (SECTIONS_PGOFF - NODES_WIDTH)
+#define ZONES_PGOFF            (NODES_PGOFF - ZONES_WIDTH)
+#define LAST_CPUPID_PGOFF      (ZONES_PGOFF - LAST_CPUPID_WIDTH)
+#define KASAN_TAG_PGOFF                (LAST_CPUPID_PGOFF - KASAN_TAG_WIDTH)
+
+/*
+ * Define the bit shifts to access each section.  For non-existent
+ * sections we define the shift as 0; that plus a 0 mask ensures
+ * the compiler will optimise away reference to them.
+ */
+#define SECTIONS_PGSHIFT       (SECTIONS_PGOFF * (SECTIONS_WIDTH != 0))
+#define NODES_PGSHIFT          (NODES_PGOFF * (NODES_WIDTH != 0))
+#define ZONES_PGSHIFT          (ZONES_PGOFF * (ZONES_WIDTH != 0))
+#define LAST_CPUPID_PGSHIFT    (LAST_CPUPID_PGOFF * (LAST_CPUPID_WIDTH != 0))
+#define KASAN_TAG_PGSHIFT      (KASAN_TAG_PGOFF * (KASAN_TAG_WIDTH != 0))
+
+/* NODE:ZONE or SECTION:ZONE is used to ID a zone for the buddy allocator */
+#ifdef NODE_NOT_IN_PAGE_FLAGS
+#define ZONEID_SHIFT           (SECTIONS_SHIFT + ZONES_SHIFT)
+#define ZONEID_PGOFF           ((SECTIONS_PGOFF < ZONES_PGOFF) ? \
+                                               SECTIONS_PGOFF : ZONES_PGOFF)
+#else
+#define ZONEID_SHIFT           (NODES_SHIFT + ZONES_SHIFT)
+#define ZONEID_PGOFF           ((NODES_PGOFF < ZONES_PGOFF) ? \
+                                               NODES_PGOFF : ZONES_PGOFF)
+#endif
+
+#define ZONEID_PGSHIFT         (ZONEID_PGOFF * (ZONEID_SHIFT != 0))
+
+#define ZONES_MASK             ((1UL << ZONES_WIDTH) - 1)
+#define NODES_MASK             ((1UL << NODES_WIDTH) - 1)
+#define SECTIONS_MASK          ((1UL << SECTIONS_WIDTH) - 1)
+#define LAST_CPUPID_MASK       ((1UL << LAST_CPUPID_SHIFT) - 1)
+#define KASAN_TAG_MASK         ((1UL << KASAN_TAG_WIDTH) - 1)
+#define ZONEID_MASK            ((1UL << ZONEID_SHIFT) - 1)
+
+static inline enum zone_type page_zonenum(const struct page *page)
+{
+       ASSERT_EXCLUSIVE_BITS(page->flags, ZONES_MASK << ZONES_PGSHIFT);
+       return (page->flags >> ZONES_PGSHIFT) & ZONES_MASK;
+}
+
+static inline enum zone_type folio_zonenum(const struct folio *folio)
+{
+       return page_zonenum(&folio->page);
+}
+
+#ifdef CONFIG_ZONE_DEVICE
+static inline bool is_zone_device_page(const struct page *page)
+{
+       return page_zonenum(page) == ZONE_DEVICE;
+}
+extern void memmap_init_zone_device(struct zone *, unsigned long,
+                                   unsigned long, struct dev_pagemap *);
+#else
+static inline bool is_zone_device_page(const struct page *page)
+{
+       return false;
+}
+#endif
+
+static inline bool folio_is_zone_device(const struct folio *folio)
+{
+       return is_zone_device_page(&folio->page);
+}
+
+static inline bool is_zone_movable_page(const struct page *page)
+{
+       return page_zonenum(page) == ZONE_MOVABLE;
+}
+#endif
+
 /*
  * Return true if [start_pfn, start_pfn + nr_pages) range has a non-empty
  * intersection with the given zone
@@ -870,7 +954,7 @@ typedef struct pglist_data {
        unsigned long nr_reclaim_start; /* nr pages written while throttled
                                         * when throttling started. */
        struct task_struct *kswapd;     /* Protected by
-                                          mem_hotplug_begin/end() */
+                                          mem_hotplug_begin/done() */
        int kswapd_order;
        enum zone_type kswapd_highest_zoneidx;
 
@@ -1053,15 +1137,6 @@ static inline int is_highmem_idx(enum zone_type idx)
 #endif
 }
 
-#ifdef CONFIG_ZONE_DMA
-bool has_managed_dma(void);
-#else
-static inline bool has_managed_dma(void)
-{
-       return false;
-}
-#endif
-
 /**
  * is_highmem - helper function to quickly check if a struct zone is a
  *              highmem zone or not.  This is an attempt to keep references
@@ -1071,12 +1146,17 @@ static inline bool has_managed_dma(void)
  */
 static inline int is_highmem(struct zone *zone)
 {
-#ifdef CONFIG_HIGHMEM
        return is_highmem_idx(zone_idx(zone));
+}
+
+#ifdef CONFIG_ZONE_DMA
+bool has_managed_dma(void);
 #else
-       return 0;
-#endif
+static inline bool has_managed_dma(void)
+{
+       return false;
 }
+#endif
 
 /* These two functions are used to setup the per zone pages min values */
 struct ctl_table;
@@ -1418,16 +1498,32 @@ extern size_t mem_section_usage_size(void);
  *      (equal SECTION_SIZE_BITS - PAGE_SHIFT), and the
  *      worst combination is powerpc with 256k pages,
  *      which results in PFN_SECTION_SHIFT equal 6.
- * To sum it up, at least 6 bits are available.
+ * To sum it up, at least 6 bits are available on all architectures.
+ * However, we can exceed 6 bits on some other architectures except
+ * powerpc (e.g. 15 bits are available on x86_64, 13 bits are available
+ * with the worst case of 64K pages on arm64) if we make sure the
+ * exceeded bit is not applicable to powerpc.
  */
-#define SECTION_MARKED_PRESENT         (1UL<<0)
-#define SECTION_HAS_MEM_MAP            (1UL<<1)
-#define SECTION_IS_ONLINE              (1UL<<2)
-#define SECTION_IS_EARLY               (1UL<<3)
-#define SECTION_TAINT_ZONE_DEVICE      (1UL<<4)
-#define SECTION_MAP_LAST_BIT           (1UL<<5)
-#define SECTION_MAP_MASK               (~(SECTION_MAP_LAST_BIT-1))
-#define SECTION_NID_SHIFT              6
+enum {
+       SECTION_MARKED_PRESENT_BIT,
+       SECTION_HAS_MEM_MAP_BIT,
+       SECTION_IS_ONLINE_BIT,
+       SECTION_IS_EARLY_BIT,
+#ifdef CONFIG_ZONE_DEVICE
+       SECTION_TAINT_ZONE_DEVICE_BIT,
+#endif
+       SECTION_MAP_LAST_BIT,
+};
+
+#define SECTION_MARKED_PRESENT         BIT(SECTION_MARKED_PRESENT_BIT)
+#define SECTION_HAS_MEM_MAP            BIT(SECTION_HAS_MEM_MAP_BIT)
+#define SECTION_IS_ONLINE              BIT(SECTION_IS_ONLINE_BIT)
+#define SECTION_IS_EARLY               BIT(SECTION_IS_EARLY_BIT)
+#ifdef CONFIG_ZONE_DEVICE
+#define SECTION_TAINT_ZONE_DEVICE      BIT(SECTION_TAINT_ZONE_DEVICE_BIT)
+#endif
+#define SECTION_MAP_MASK               (~(BIT(SECTION_MAP_LAST_BIT) - 1))
+#define SECTION_NID_SHIFT              SECTION_MAP_LAST_BIT
 
 static inline struct page *__section_mem_map_addr(struct mem_section *section)
 {
@@ -1466,12 +1562,19 @@ static inline int online_section(struct mem_section *section)
        return (section && (section->section_mem_map & SECTION_IS_ONLINE));
 }
 
+#ifdef CONFIG_ZONE_DEVICE
 static inline int online_device_section(struct mem_section *section)
 {
        unsigned long flags = SECTION_IS_ONLINE | SECTION_TAINT_ZONE_DEVICE;
 
        return section && ((section->section_mem_map & flags) == flags);
 }
+#else
+static inline int online_device_section(struct mem_section *section)
+{
+       return 0;
+}
+#endif
 
 static inline int online_section_nr(unsigned long nr)
 {
index 3f5490f..ea19528 100644 (file)
@@ -193,6 +193,11 @@ enum pageflags {
 
        /* Only valid for buddy pages. Used to track pages that are reported */
        PG_reported = PG_uptodate,
+
+#ifdef CONFIG_MEMORY_HOTPLUG
+       /* For self-hosted memmap pages */
+       PG_vmemmap_self_hosted = PG_owner_priv_1,
+#endif
 };
 
 #define PAGEFLAGS_MASK         ((1UL << NR_PAGEFLAGS) - 1)
@@ -628,6 +633,12 @@ PAGEFLAG_FALSE(SkipKASanPoison, skip_kasan_poison)
  */
 __PAGEFLAG(Reported, reported, PF_NO_COMPOUND)
 
+#ifdef CONFIG_MEMORY_HOTPLUG
+PAGEFLAG(VmemmapSelfHosted, vmemmap_self_hosted, PF_ANY)
+#else
+PAGEFLAG_FALSE(VmemmapSelfHosted, vmemmap_self_hosted)
+#endif
+
 /*
  * On an anonymous page mapped into a user virtual memory area,
  * page->mapping points to its anon_vma, not to a struct address_space;
@@ -650,6 +661,12 @@ __PAGEFLAG(Reported, reported, PF_NO_COMPOUND)
 #define PAGE_MAPPING_KSM       (PAGE_MAPPING_ANON | PAGE_MAPPING_MOVABLE)
 #define PAGE_MAPPING_FLAGS     (PAGE_MAPPING_ANON | PAGE_MAPPING_MOVABLE)
 
+/*
+ * Different with flags above, this flag is used only for fsdax mode.  It
+ * indicates that this page->mapping is now under reflink case.
+ */
+#define PAGE_MAPPING_DAX_COW   0x1
+
 static __always_inline bool folio_mapping_flags(struct folio *folio)
 {
        return ((unsigned long)folio->mapping & PAGE_MAPPING_FLAGS) != 0;
@@ -670,6 +687,12 @@ static __always_inline bool PageAnon(struct page *page)
        return folio_test_anon(page_folio(page));
 }
 
+static __always_inline bool __folio_test_movable(const struct folio *folio)
+{
+       return ((unsigned long)folio->mapping & PAGE_MAPPING_FLAGS) ==
+                       PAGE_MAPPING_MOVABLE;
+}
+
 static __always_inline int __PageMovable(struct page *page)
 {
        return ((unsigned long)page->mapping & PAGE_MAPPING_FLAGS) ==
index cc9adba..0178b20 100644 (file)
@@ -345,8 +345,6 @@ static inline void filemap_nr_thps_dec(struct address_space *mapping)
 #endif
 }
 
-void release_pages(struct page **pages, int nr);
-
 struct address_space *page_mapping(struct page *);
 struct address_space *folio_mapping(struct folio *);
 struct address_space *swapcache_mapping(struct folio *);
index 6649154..215eb6c 100644 (file)
@@ -26,7 +26,6 @@ struct pagevec {
 };
 
 void __pagevec_release(struct pagevec *pvec);
-void __pagevec_lru_add(struct pagevec *pvec);
 unsigned pagevec_lookup_range_tag(struct pagevec *pvec,
                struct address_space *mapping, pgoff_t *index, pgoff_t end,
                xa_mark_t tag);
index 3cdc16c..014ee8f 100644 (file)
@@ -1689,4 +1689,32 @@ typedef unsigned int pgtbl_mod_mask;
 #define MAX_PTRS_PER_P4D PTRS_PER_P4D
 #endif
 
+/* description of effects of mapping type and prot in current implementation.
+ * this is due to the limited x86 page protection hardware.  The expected
+ * behavior is in parens:
+ *
+ * map_type    prot
+ *             PROT_NONE       PROT_READ       PROT_WRITE      PROT_EXEC
+ * MAP_SHARED  r: (no) no      r: (yes) yes    r: (no) yes     r: (no) yes
+ *             w: (no) no      w: (no) no      w: (yes) yes    w: (no) no
+ *             x: (no) no      x: (no) yes     x: (no) yes     x: (yes) yes
+ *
+ * MAP_PRIVATE r: (no) no      r: (yes) yes    r: (no) yes     r: (no) yes
+ *             w: (no) no      w: (no) no      w: (copy) copy  w: (no) no
+ *             x: (no) no      x: (no) yes     x: (no) yes     x: (yes) yes
+ *
+ * On arm64, PROT_EXEC has the following behaviour for both MAP_SHARED and
+ * MAP_PRIVATE (with Enhanced PAN supported):
+ *                                                             r: (no) no
+ *                                                             w: (no) no
+ *                                                             x: (yes) yes
+ */
+#define DECLARE_VM_GET_PAGE_PROT                                       \
+pgprot_t vm_get_page_prot(unsigned long vm_flags)                      \
+{                                                                      \
+               return protection_map[vm_flags &                        \
+                       (VM_READ | VM_WRITE | VM_EXEC | VM_SHARED)];    \
+}                                                                      \
+EXPORT_SYMBOL(vm_get_page_prot);
+
 #endif /* _LINUX_PGTABLE_H */
index 9ec2313..bf80adc 100644 (file)
@@ -325,8 +325,8 @@ struct page_vma_mapped_walk {
 #define DEFINE_PAGE_VMA_WALK(name, _page, _vma, _address, _flags)      \
        struct page_vma_mapped_walk name = {                            \
                .pfn = page_to_pfn(_page),                              \
-               .nr_pages = compound_nr(page),                          \
-               .pgoff = page_to_pgoff(page),                           \
+               .nr_pages = compound_nr(_page),                         \
+               .pgoff = page_to_pgoff(_page),                          \
                .vma = _vma,                                            \
                .address = _address,                                    \
                .flags = _flags,                                        \
index 8cd975a..2a24361 100644 (file)
@@ -29,7 +29,7 @@ extern struct mm_struct *mm_alloc(void);
  *
  * Use mmdrop() to release the reference acquired by mmgrab().
  *
- * See also <Documentation/vm/active_mm.rst> for an in-depth explanation
+ * See also <Documentation/mm/active_mm.rst> for an in-depth explanation
  * of &mm_struct.mm_count vs &mm_struct.mm_users.
  */
 static inline void mmgrab(struct mm_struct *mm)
@@ -92,7 +92,7 @@ static inline void mmdrop_sched(struct mm_struct *mm)
  *
  * Use mmput() to release the reference acquired by mmget().
  *
- * See also <Documentation/vm/active_mm.rst> for an in-depth explanation
+ * See also <Documentation/mm/active_mm.rst> for an in-depth explanation
  * of &mm_struct.mm_count vs &mm_struct.mm_users.
  */
 static inline void mmget(struct mm_struct *mm)
index a68f982..1b6c401 100644 (file)
@@ -25,9 +25,20 @@ struct shmem_inode_info {
        struct simple_xattrs    xattrs;         /* list of xattrs */
        atomic_t                stop_eviction;  /* hold when working on inode */
        struct timespec64       i_crtime;       /* file creation time */
+       unsigned int            fsflags;        /* flags for FS_IOC_[SG]ETFLAGS */
        struct inode            vfs_inode;
 };
 
+#define SHMEM_FL_USER_VISIBLE FS_FL_USER_VISIBLE
+#define SHMEM_FL_USER_MODIFIABLE FS_FL_USER_MODIFIABLE
+#define SHMEM_FL_INHERITED FS_FL_USER_MODIFIABLE
+
+/* Flags that are appropriate for regular files (all but dir-specific ones). */
+#define SHMEM_REG_FLMASK (~(FS_DIRSYNC_FL | FS_TOPDIR_FL))
+
+/* Flags that are appropriate for non-directories/regular files. */
+#define SHMEM_OTHER_FLMASK (FS_NODUMP_FL | FS_NOATIME_FL)
+
 struct shmem_sb_info {
        unsigned long max_blocks;   /* How many blocks are allowed */
        struct percpu_counter used_blocks;  /* How many are allocated */
index 76fbf92..08e6054 100644 (file)
@@ -72,6 +72,11 @@ struct shrinker {
 #ifdef CONFIG_MEMCG
        /* ID in shrinker_idr */
        int id;
+#endif
+#ifdef CONFIG_SHRINKER_DEBUG
+       int debugfs_id;
+       const char *name;
+       struct dentry *debugfs_entry;
 #endif
        /* objs pending delete, per node */
        atomic_long_t *nr_deferred;
@@ -88,10 +93,32 @@ struct shrinker {
  */
 #define SHRINKER_NONSLAB       (1 << 3)
 
-extern int prealloc_shrinker(struct shrinker *shrinker);
+extern int __printf(2, 3) prealloc_shrinker(struct shrinker *shrinker,
+                                           const char *fmt, ...);
 extern void register_shrinker_prepared(struct shrinker *shrinker);
-extern int register_shrinker(struct shrinker *shrinker);
+extern int __printf(2, 3) register_shrinker(struct shrinker *shrinker,
+                                           const char *fmt, ...);
 extern void unregister_shrinker(struct shrinker *shrinker);
 extern void free_prealloced_shrinker(struct shrinker *shrinker);
 extern void synchronize_shrinkers(void);
-#endif
+
+#ifdef CONFIG_SHRINKER_DEBUG
+extern int shrinker_debugfs_add(struct shrinker *shrinker);
+extern void shrinker_debugfs_remove(struct shrinker *shrinker);
+extern int __printf(2, 3) shrinker_debugfs_rename(struct shrinker *shrinker,
+                                                 const char *fmt, ...);
+#else /* CONFIG_SHRINKER_DEBUG */
+static inline int shrinker_debugfs_add(struct shrinker *shrinker)
+{
+       return 0;
+}
+static inline void shrinker_debugfs_remove(struct shrinker *shrinker)
+{
+}
+static inline __printf(2, 3)
+int shrinker_debugfs_rename(struct shrinker *shrinker, const char *fmt, ...)
+{
+       return 0;
+}
+#endif /* CONFIG_SHRINKER_DEBUG */
+#endif /* _LINUX_SHRINKER_H */
index 8672a71..43150b9 100644 (file)
@@ -74,7 +74,7 @@ static inline int current_is_kswapd(void)
 
 /*
  * Unaddressable device memory support. See include/linux/hmm.h and
- * Documentation/vm/hmm.rst. Short description is we need struct pages for
+ * Documentation/mm/hmm.rst. Short description is we need struct pages for
  * device memory that is unaddressable (inaccessible) by CPU, so that we can
  * migrate part of a process memory to device memory.
  *
@@ -411,10 +411,13 @@ extern void lru_cache_add_inactive_or_unevictable(struct page *page,
 extern unsigned long zone_reclaimable_pages(struct zone *zone);
 extern unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
                                        gfp_t gfp_mask, nodemask_t *mask);
+
+#define MEMCG_RECLAIM_MAY_SWAP (1 << 1)
+#define MEMCG_RECLAIM_PROACTIVE (1 << 2)
 extern unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
                                                  unsigned long nr_pages,
                                                  gfp_t gfp_mask,
-                                                 bool may_swap);
+                                                 unsigned int reclaim_options);
 extern unsigned long mem_cgroup_shrink_node(struct mem_cgroup *mem,
                                                gfp_t gfp_mask, bool noswap,
                                                pg_data_t *pgdat,
@@ -456,6 +459,7 @@ static inline unsigned long total_swapcache_pages(void)
        return global_node_page_state(NR_SWAPCACHE);
 }
 
+extern void free_swap_cache(struct page *page);
 extern void free_page_and_swap_cache(struct page *);
 extern void free_pages_and_swap_cache(struct page **, int);
 /* linux/mm/swapfile.c */
@@ -540,6 +544,10 @@ static inline void put_swap_device(struct swap_info_struct *si)
 /* used to sanity check ptes in zap_pte_range when CONFIG_SWAP=0 */
 #define free_swap_and_cache(e) is_pfn_swap_entry(e)
 
+static inline void free_swap_cache(struct page *page)
+{
+}
+
 static inline int add_swap_count_continuation(swp_entry_t swp, gfp_t gfp_mask)
 {
        return 0;
index f24775b..bb7afd0 100644 (file)
@@ -244,8 +244,10 @@ extern void __migration_entry_wait(struct mm_struct *mm, pte_t *ptep,
                                        spinlock_t *ptl);
 extern void migration_entry_wait(struct mm_struct *mm, pmd_t *pmd,
                                        unsigned long address);
-extern void migration_entry_wait_huge(struct vm_area_struct *vma,
-               struct mm_struct *mm, pte_t *pte);
+#ifdef CONFIG_HUGETLB_PAGE
+extern void __migration_entry_wait_huge(pte_t *ptep, spinlock_t *ptl);
+extern void migration_entry_wait_huge(struct vm_area_struct *vma, pte_t *pte);
+#endif
 #else
 static inline swp_entry_t make_readable_migration_entry(pgoff_t offset)
 {
@@ -271,8 +273,10 @@ static inline void __migration_entry_wait(struct mm_struct *mm, pte_t *ptep,
                                        spinlock_t *ptl) { }
 static inline void migration_entry_wait(struct mm_struct *mm, pmd_t *pmd,
                                         unsigned long address) { }
-static inline void migration_entry_wait_huge(struct vm_area_struct *vma,
-               struct mm_struct *mm, pte_t *pte) { }
+#ifdef CONFIG_HUGETLB_PAGE
+static inline void __migration_entry_wait_huge(pte_t *ptep, spinlock_t *ptl) { }
+static inline void migration_entry_wait_huge(struct vm_area_struct *vma, pte_t *pte) { }
+#endif
 static inline int is_writable_migration_entry(swp_entry_t entry)
 {
        return 0;
index 3dc9680..79aea7d 100644 (file)
@@ -4574,7 +4574,7 @@ static void __init kfree_rcu_batch_init(void)
                INIT_DELAYED_WORK(&krcp->page_cache_work, fill_page_cache_func);
                krcp->initialized = true;
        }
-       if (register_shrinker(&kfree_rcu_shrinker))
+       if (register_shrinker(&kfree_rcu_shrinker, "rcu-kfree"))
                pr_err("Failed to register kfree_rcu() shrinker!\n");
 }
 
index 35cd828..403071f 100644 (file)
@@ -699,6 +699,14 @@ config DEBUG_OBJECTS_ENABLE_DEFAULT
        help
          Debug objects boot parameter default value
 
+config SHRINKER_DEBUG
+       bool "Enable shrinker debugging support"
+       depends on DEBUG_FS
+       help
+         Say Y to enable the shrinker debugfs interface which provides
+         visibility into the kernel memory shrinkers subsystem.
+         Disable it to avoid an extra memory footprint.
+
 config HAVE_DEBUG_KMEMLEAK
        bool
 
index 25ae1ac..9ebf6f5 100644 (file)
@@ -17,7 +17,7 @@ static void test_free_pages(gfp_t gfp)
 
        for (i = 0; i < 1000 * 1000; i++) {
                unsigned long addr = __get_free_pages(gfp, 3);
-               struct page *page = virt_to_page(addr);
+               struct page *page = virt_to_page((void *)addr);
 
                /* Simulate page cache getting a speculative reference */
                get_page(page);
index cfe6320..e3965ca 100644 (file)
 
 #include "test_hmm_uapi.h"
 
-#define DMIRROR_NDEVICES               2
+#define DMIRROR_NDEVICES               4
 #define DMIRROR_RANGE_FAULT_TIMEOUT    1000
 #define DEVMEM_CHUNK_SIZE              (256 * 1024 * 1024U)
 #define DEVMEM_CHUNKS_RESERVE          16
 
+/*
+ * For device_private pages, dpage is just a dummy struct page
+ * representing a piece of device memory. dmirror_devmem_alloc_page
+ * allocates a real system memory page as backing storage to fake a
+ * real device. zone_device_data points to that backing page. But
+ * for device_coherent memory, the struct page represents real
+ * physical CPU-accessible memory that we can use directly.
+ */
+#define BACKING_PAGE(page) (is_device_private_page((page)) ? \
+                          (page)->zone_device_data : (page))
+
+static unsigned long spm_addr_dev0;
+module_param(spm_addr_dev0, long, 0644);
+MODULE_PARM_DESC(spm_addr_dev0,
+               "Specify start address for SPM (special purpose memory) used for device 0. By setting this Coherent device type will be used. Make sure spm_addr_dev1 is set too. Minimum SPM size should be DEVMEM_CHUNK_SIZE.");
+
+static unsigned long spm_addr_dev1;
+module_param(spm_addr_dev1, long, 0644);
+MODULE_PARM_DESC(spm_addr_dev1,
+               "Specify start address for SPM (special purpose memory) used for device 1. By setting this Coherent device type will be used. Make sure spm_addr_dev0 is set too. Minimum SPM size should be DEVMEM_CHUNK_SIZE.");
+
 static const struct dev_pagemap_ops dmirror_devmem_ops;
 static const struct mmu_interval_notifier_ops dmirror_min_ops;
 static dev_t dmirror_dev;
@@ -87,6 +108,7 @@ struct dmirror_chunk {
 struct dmirror_device {
        struct cdev             cdevice;
        struct hmm_devmem       *devmem;
+       unsigned int            zone_device_type;
 
        unsigned int            devmem_capacity;
        unsigned int            devmem_count;
@@ -114,6 +136,21 @@ static int dmirror_bounce_init(struct dmirror_bounce *bounce,
        return 0;
 }
 
+static bool dmirror_is_private_zone(struct dmirror_device *mdevice)
+{
+       return (mdevice->zone_device_type ==
+               HMM_DMIRROR_MEMORY_DEVICE_PRIVATE) ? true : false;
+}
+
+static enum migrate_vma_direction
+dmirror_select_device(struct dmirror *dmirror)
+{
+       return (dmirror->mdevice->zone_device_type ==
+               HMM_DMIRROR_MEMORY_DEVICE_PRIVATE) ?
+               MIGRATE_VMA_SELECT_DEVICE_PRIVATE :
+               MIGRATE_VMA_SELECT_DEVICE_COHERENT;
+}
+
 static void dmirror_bounce_fini(struct dmirror_bounce *bounce)
 {
        vfree(bounce->ptr);
@@ -454,28 +491,44 @@ fini:
        return ret;
 }
 
-static bool dmirror_allocate_chunk(struct dmirror_device *mdevice,
+static int dmirror_allocate_chunk(struct dmirror_device *mdevice,
                                   struct page **ppage)
 {
        struct dmirror_chunk *devmem;
-       struct resource *res;
+       struct resource *res = NULL;
        unsigned long pfn;
        unsigned long pfn_first;
        unsigned long pfn_last;
        void *ptr;
+       int ret = -ENOMEM;
 
        devmem = kzalloc(sizeof(*devmem), GFP_KERNEL);
        if (!devmem)
-               return false;
+               return ret;
 
-       res = request_free_mem_region(&iomem_resource, DEVMEM_CHUNK_SIZE,
-                                     "hmm_dmirror");
-       if (IS_ERR(res))
+       switch (mdevice->zone_device_type) {
+       case HMM_DMIRROR_MEMORY_DEVICE_PRIVATE:
+               res = request_free_mem_region(&iomem_resource, DEVMEM_CHUNK_SIZE,
+                                             "hmm_dmirror");
+               if (IS_ERR_OR_NULL(res))
+                       goto err_devmem;
+               devmem->pagemap.range.start = res->start;
+               devmem->pagemap.range.end = res->end;
+               devmem->pagemap.type = MEMORY_DEVICE_PRIVATE;
+               break;
+       case HMM_DMIRROR_MEMORY_DEVICE_COHERENT:
+               devmem->pagemap.range.start = (MINOR(mdevice->cdevice.dev) - 2) ?
+                                                       spm_addr_dev0 :
+                                                       spm_addr_dev1;
+               devmem->pagemap.range.end = devmem->pagemap.range.start +
+                                           DEVMEM_CHUNK_SIZE - 1;
+               devmem->pagemap.type = MEMORY_DEVICE_COHERENT;
+               break;
+       default:
+               ret = -EINVAL;
                goto err_devmem;
+       }
 
-       devmem->pagemap.type = MEMORY_DEVICE_PRIVATE;
-       devmem->pagemap.range.start = res->start;
-       devmem->pagemap.range.end = res->end;
        devmem->pagemap.nr_range = 1;
        devmem->pagemap.ops = &dmirror_devmem_ops;
        devmem->pagemap.owner = mdevice;
@@ -496,10 +549,14 @@ static bool dmirror_allocate_chunk(struct dmirror_device *mdevice,
                mdevice->devmem_capacity = new_capacity;
                mdevice->devmem_chunks = new_chunks;
        }
-
        ptr = memremap_pages(&devmem->pagemap, numa_node_id());
-       if (IS_ERR(ptr))
+       if (IS_ERR_OR_NULL(ptr)) {
+               if (ptr)
+                       ret = PTR_ERR(ptr);
+               else
+                       ret = -EFAULT;
                goto err_release;
+       }
 
        devmem->mdevice = mdevice;
        pfn_first = devmem->pagemap.range.start >> PAGE_SHIFT;
@@ -528,30 +585,35 @@ static bool dmirror_allocate_chunk(struct dmirror_device *mdevice,
        }
        spin_unlock(&mdevice->lock);
 
-       return true;
+       return 0;
 
 err_release:
        mutex_unlock(&mdevice->devmem_lock);
-       release_mem_region(devmem->pagemap.range.start, range_len(&devmem->pagemap.range));
+       if (res && devmem->pagemap.type == MEMORY_DEVICE_PRIVATE)
+               release_mem_region(devmem->pagemap.range.start,
+                                  range_len(&devmem->pagemap.range));
 err_devmem:
        kfree(devmem);
 
-       return false;
+       return ret;
 }
 
 static struct page *dmirror_devmem_alloc_page(struct dmirror_device *mdevice)
 {
        struct page *dpage = NULL;
-       struct page *rpage;
+       struct page *rpage = NULL;
 
        /*
-        * This is a fake device so we alloc real system memory to store
-        * our device memory.
+        * For ZONE_DEVICE private type, this is a fake device so we allocate
+        * real system memory to store our device memory.
+        * For ZONE_DEVICE coherent type we use the actual dpage to store the
+        * data and ignore rpage.
         */
-       rpage = alloc_page(GFP_HIGHUSER);
-       if (!rpage)
-               return NULL;
-
+       if (dmirror_is_private_zone(mdevice)) {
+               rpage = alloc_page(GFP_HIGHUSER);
+               if (!rpage)
+                       return NULL;
+       }
        spin_lock(&mdevice->lock);
 
        if (mdevice->free_pages) {
@@ -561,7 +623,7 @@ static struct page *dmirror_devmem_alloc_page(struct dmirror_device *mdevice)
                spin_unlock(&mdevice->lock);
        } else {
                spin_unlock(&mdevice->lock);
-               if (!dmirror_allocate_chunk(mdevice, &dpage))
+               if (dmirror_allocate_chunk(mdevice, &dpage))
                        goto error;
        }
 
@@ -570,7 +632,8 @@ static struct page *dmirror_devmem_alloc_page(struct dmirror_device *mdevice)
        return dpage;
 
 error:
-       __free_page(rpage);
+       if (rpage)
+               __free_page(rpage);
        return NULL;
 }
 
@@ -596,12 +659,16 @@ static void dmirror_migrate_alloc_and_copy(struct migrate_vma *args,
                 * unallocated pte_none() or read-only zero page.
                 */
                spage = migrate_pfn_to_page(*src);
+               if (WARN(spage && is_zone_device_page(spage),
+                    "page already in device spage pfn: 0x%lx\n",
+                    page_to_pfn(spage)))
+                       continue;
 
                dpage = dmirror_devmem_alloc_page(mdevice);
                if (!dpage)
                        continue;
 
-               rpage = dpage->zone_device_data;
+               rpage = BACKING_PAGE(dpage);
                if (spage)
                        copy_highpage(rpage, spage);
                else
@@ -615,6 +682,8 @@ static void dmirror_migrate_alloc_and_copy(struct migrate_vma *args,
                 */
                rpage->zone_device_data = dmirror;
 
+               pr_debug("migrating from sys to dev pfn src: 0x%lx pfn dst: 0x%lx\n",
+                        page_to_pfn(spage), page_to_pfn(dpage));
                *dst = migrate_pfn(page_to_pfn(dpage));
                if ((*src & MIGRATE_PFN_WRITE) ||
                    (!spage && args->vma->vm_flags & VM_WRITE))
@@ -692,11 +761,7 @@ static int dmirror_migrate_finalize_and_map(struct migrate_vma *args,
                if (!dpage)
                        continue;
 
-               /*
-                * Store the page that holds the data so the page table
-                * doesn't have to deal with ZONE_DEVICE private pages.
-                */
-               entry = dpage->zone_device_data;
+               entry = BACKING_PAGE(dpage);
                if (*dst & MIGRATE_PFN_WRITE)
                        entry = xa_tag_pointer(entry, DPT_XA_TAG_WRITE);
                entry = xa_store(&dmirror->pt, pfn, entry, GFP_ATOMIC);
@@ -732,7 +797,7 @@ static int dmirror_exclusive(struct dmirror *dmirror,
 
        mmap_read_lock(mm);
        for (addr = start; addr < end; addr = next) {
-               unsigned long mapped;
+               unsigned long mapped = 0;
                int i;
 
                if (end < addr + (ARRAY_SIZE(pages) << PAGE_SHIFT))
@@ -741,7 +806,13 @@ static int dmirror_exclusive(struct dmirror *dmirror,
                        next = addr + (ARRAY_SIZE(pages) << PAGE_SHIFT);
 
                ret = make_device_exclusive_range(mm, addr, next, pages, NULL);
-               mapped = dmirror_atomic_map(addr, next, pages, dmirror);
+               /*
+                * Do dmirror_atomic_map() iff all pages are marked for
+                * exclusive access to avoid accessing uninitialized
+                * fields of pages.
+                */
+               if (ret == (next - addr) >> PAGE_SHIFT)
+                       mapped = dmirror_atomic_map(addr, next, pages, dmirror);
                for (i = 0; i < ret; i++) {
                        if (pages[i]) {
                                unlock_page(pages[i]);
@@ -776,15 +847,126 @@ static int dmirror_exclusive(struct dmirror *dmirror,
        return ret;
 }
 
-static int dmirror_migrate(struct dmirror *dmirror,
-                          struct hmm_dmirror_cmd *cmd)
+static vm_fault_t dmirror_devmem_fault_alloc_and_copy(struct migrate_vma *args,
+                                                     struct dmirror *dmirror)
+{
+       const unsigned long *src = args->src;
+       unsigned long *dst = args->dst;
+       unsigned long start = args->start;
+       unsigned long end = args->end;
+       unsigned long addr;
+
+       for (addr = start; addr < end; addr += PAGE_SIZE,
+                                      src++, dst++) {
+               struct page *dpage, *spage;
+
+               spage = migrate_pfn_to_page(*src);
+               if (!spage || !(*src & MIGRATE_PFN_MIGRATE))
+                       continue;
+
+               if (WARN_ON(!is_device_private_page(spage) &&
+                           !is_device_coherent_page(spage)))
+                       continue;
+               spage = BACKING_PAGE(spage);
+               dpage = alloc_page_vma(GFP_HIGHUSER_MOVABLE, args->vma, addr);
+               if (!dpage)
+                       continue;
+               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);
+               xa_erase(&dmirror->pt, addr >> PAGE_SHIFT);
+               copy_highpage(dpage, spage);
+               *dst = migrate_pfn(page_to_pfn(dpage));
+               if (*src & MIGRATE_PFN_WRITE)
+                       *dst |= MIGRATE_PFN_WRITE;
+       }
+       return 0;
+}
+
+static unsigned long
+dmirror_successful_migrated_pages(struct migrate_vma *migrate)
+{
+       unsigned long cpages = 0;
+       unsigned long i;
+
+       for (i = 0; i < migrate->npages; i++) {
+               if (migrate->src[i] & MIGRATE_PFN_VALID &&
+                   migrate->src[i] & MIGRATE_PFN_MIGRATE)
+                       cpages++;
+       }
+       return cpages;
+}
+
+static int dmirror_migrate_to_system(struct dmirror *dmirror,
+                                    struct hmm_dmirror_cmd *cmd)
 {
        unsigned long start, end, addr;
        unsigned long size = cmd->npages << PAGE_SHIFT;
        struct mm_struct *mm = dmirror->notifier.mm;
        struct vm_area_struct *vma;
-       unsigned long src_pfns[64];
-       unsigned long dst_pfns[64];
+       unsigned long src_pfns[64] = { 0 };
+       unsigned long dst_pfns[64] = { 0 };
+       struct migrate_vma args;
+       unsigned long next;
+       int ret;
+
+       start = cmd->addr;
+       end = start + size;
+       if (end < start)
+               return -EINVAL;
+
+       /* Since the mm is for the mirrored process, get a reference first. */
+       if (!mmget_not_zero(mm))
+               return -EINVAL;
+
+       cmd->cpages = 0;
+       mmap_read_lock(mm);
+       for (addr = start; addr < end; addr = next) {
+               vma = vma_lookup(mm, addr);
+               if (!vma || !(vma->vm_flags & VM_READ)) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+               next = min(end, addr + (ARRAY_SIZE(src_pfns) << PAGE_SHIFT));
+               if (next > vma->vm_end)
+                       next = vma->vm_end;
+
+               args.vma = vma;
+               args.src = src_pfns;
+               args.dst = dst_pfns;
+               args.start = addr;
+               args.end = next;
+               args.pgmap_owner = dmirror->mdevice;
+               args.flags = dmirror_select_device(dmirror);
+
+               ret = migrate_vma_setup(&args);
+               if (ret)
+                       goto out;
+
+               pr_debug("Migrating from device mem to sys mem\n");
+               dmirror_devmem_fault_alloc_and_copy(&args, dmirror);
+
+               migrate_vma_pages(&args);
+               cmd->cpages += dmirror_successful_migrated_pages(&args);
+               migrate_vma_finalize(&args);
+       }
+out:
+       mmap_read_unlock(mm);
+       mmput(mm);
+
+       return ret;
+}
+
+static int dmirror_migrate_to_device(struct dmirror *dmirror,
+                               struct hmm_dmirror_cmd *cmd)
+{
+       unsigned long start, end, addr;
+       unsigned long size = cmd->npages << PAGE_SHIFT;
+       struct mm_struct *mm = dmirror->notifier.mm;
+       struct vm_area_struct *vma;
+       unsigned long src_pfns[64] = { 0 };
+       unsigned long dst_pfns[64] = { 0 };
        struct dmirror_bounce bounce;
        struct migrate_vma args;
        unsigned long next;
@@ -821,6 +1003,7 @@ static int dmirror_migrate(struct dmirror *dmirror,
                if (ret)
                        goto out;
 
+               pr_debug("Migrating from sys mem to device mem\n");
                dmirror_migrate_alloc_and_copy(&args, dmirror);
                migrate_vma_pages(&args);
                dmirror_migrate_finalize_and_map(&args, dmirror);
@@ -829,7 +1012,10 @@ static int dmirror_migrate(struct dmirror *dmirror,
        mmap_read_unlock(mm);
        mmput(mm);
 
-       /* Return the migrated data for verification. */
+       /*
+        * Return the migrated data for verification.
+        * Only for pages in device zone
+        */
        ret = dmirror_bounce_init(&bounce, start, size);
        if (ret)
                return ret;
@@ -872,6 +1058,12 @@ static void dmirror_mkentry(struct dmirror *dmirror, struct hmm_range *range,
                        *perm = HMM_DMIRROR_PROT_DEV_PRIVATE_LOCAL;
                else
                        *perm = HMM_DMIRROR_PROT_DEV_PRIVATE_REMOTE;
+       } else if (is_device_coherent_page(page)) {
+               /* Is the page migrated to this device or some other? */
+               if (dmirror->mdevice == dmirror_page_to_device(page))
+                       *perm = HMM_DMIRROR_PROT_DEV_COHERENT_LOCAL;
+               else
+                       *perm = HMM_DMIRROR_PROT_DEV_COHERENT_REMOTE;
        } else if (is_zero_pfn(page_to_pfn(page)))
                *perm = HMM_DMIRROR_PROT_ZERO;
        else
@@ -1059,8 +1251,12 @@ static long dmirror_fops_unlocked_ioctl(struct file *filp,
                ret = dmirror_write(dmirror, &cmd);
                break;
 
-       case HMM_DMIRROR_MIGRATE:
-               ret = dmirror_migrate(dmirror, &cmd);
+       case HMM_DMIRROR_MIGRATE_TO_DEV:
+               ret = dmirror_migrate_to_device(dmirror, &cmd);
+               break;
+
+       case HMM_DMIRROR_MIGRATE_TO_SYS:
+               ret = dmirror_migrate_to_system(dmirror, &cmd);
                break;
 
        case HMM_DMIRROR_EXCLUSIVE:
@@ -1122,14 +1318,13 @@ static const struct file_operations dmirror_fops = {
 
 static void dmirror_devmem_free(struct page *page)
 {
-       struct page *rpage = page->zone_device_data;
+       struct page *rpage = BACKING_PAGE(page);
        struct dmirror_device *mdevice;
 
-       if (rpage)
+       if (rpage != page)
                __free_page(rpage);
 
        mdevice = dmirror_page_to_device(page);
-
        spin_lock(&mdevice->lock);
        mdevice->cfree++;
        page->zone_device_data = mdevice->free_pages;
@@ -1137,43 +1332,11 @@ static void dmirror_devmem_free(struct page *page)
        spin_unlock(&mdevice->lock);
 }
 
-static vm_fault_t dmirror_devmem_fault_alloc_and_copy(struct migrate_vma *args,
-                                                     struct dmirror *dmirror)
-{
-       const unsigned long *src = args->src;
-       unsigned long *dst = args->dst;
-       unsigned long start = args->start;
-       unsigned long end = args->end;
-       unsigned long addr;
-
-       for (addr = start; addr < end; addr += PAGE_SIZE,
-                                      src++, dst++) {
-               struct page *dpage, *spage;
-
-               spage = migrate_pfn_to_page(*src);
-               if (!spage || !(*src & MIGRATE_PFN_MIGRATE))
-                       continue;
-               spage = spage->zone_device_data;
-
-               dpage = alloc_page_vma(GFP_HIGHUSER_MOVABLE, args->vma, addr);
-               if (!dpage)
-                       continue;
-
-               lock_page(dpage);
-               xa_erase(&dmirror->pt, addr >> PAGE_SHIFT);
-               copy_highpage(dpage, spage);
-               *dst = migrate_pfn(page_to_pfn(dpage));
-               if (*src & MIGRATE_PFN_WRITE)
-                       *dst |= MIGRATE_PFN_WRITE;
-       }
-       return 0;
-}
-
 static vm_fault_t dmirror_devmem_fault(struct vm_fault *vmf)
 {
        struct migrate_vma args;
-       unsigned long src_pfns;
-       unsigned long dst_pfns;
+       unsigned long src_pfns = 0;
+       unsigned long dst_pfns = 0;
        struct page *rpage;
        struct dmirror *dmirror;
        vm_fault_t ret;
@@ -1193,7 +1356,7 @@ static vm_fault_t dmirror_devmem_fault(struct vm_fault *vmf)
        args.src = &src_pfns;
        args.dst = &dst_pfns;
        args.pgmap_owner = dmirror->mdevice;
-       args.flags = MIGRATE_VMA_SELECT_DEVICE_PRIVATE;
+       args.flags = dmirror_select_device(dmirror);
 
        if (migrate_vma_setup(&args))
                return VM_FAULT_SIGBUS;
@@ -1231,10 +1394,8 @@ static int dmirror_device_init(struct dmirror_device *mdevice, int id)
        if (ret)
                return ret;
 
-       /* Build a list of free ZONE_DEVICE private struct pages */
-       dmirror_allocate_chunk(mdevice, NULL);
-
-       return 0;
+       /* Build a list of free ZONE_DEVICE struct pages */
+       return dmirror_allocate_chunk(mdevice, NULL);
 }
 
 static void dmirror_device_remove(struct dmirror_device *mdevice)
@@ -1247,8 +1408,9 @@ static void dmirror_device_remove(struct dmirror_device *mdevice)
                                mdevice->devmem_chunks[i];
 
                        memunmap_pages(&devmem->pagemap);
-                       release_mem_region(devmem->pagemap.range.start,
-                                          range_len(&devmem->pagemap.range));
+                       if (devmem->pagemap.type == MEMORY_DEVICE_PRIVATE)
+                               release_mem_region(devmem->pagemap.range.start,
+                                                  range_len(&devmem->pagemap.range));
                        kfree(devmem);
                }
                kfree(mdevice->devmem_chunks);
@@ -1260,14 +1422,26 @@ static void dmirror_device_remove(struct dmirror_device *mdevice)
 static int __init hmm_dmirror_init(void)
 {
        int ret;
-       int id;
+       int id = 0;
+       int ndevices = 0;
 
        ret = alloc_chrdev_region(&dmirror_dev, 0, DMIRROR_NDEVICES,
                                  "HMM_DMIRROR");
        if (ret)
                goto err_unreg;
 
-       for (id = 0; id < DMIRROR_NDEVICES; id++) {
+       memset(dmirror_devices, 0, DMIRROR_NDEVICES * sizeof(dmirror_devices[0]));
+       dmirror_devices[ndevices++].zone_device_type =
+                               HMM_DMIRROR_MEMORY_DEVICE_PRIVATE;
+       dmirror_devices[ndevices++].zone_device_type =
+                               HMM_DMIRROR_MEMORY_DEVICE_PRIVATE;
+       if (spm_addr_dev0 && spm_addr_dev1) {
+               dmirror_devices[ndevices++].zone_device_type =
+                                       HMM_DMIRROR_MEMORY_DEVICE_COHERENT;
+               dmirror_devices[ndevices++].zone_device_type =
+                                       HMM_DMIRROR_MEMORY_DEVICE_COHERENT;
+       }
+       for (id = 0; id < ndevices; id++) {
                ret = dmirror_device_init(dmirror_devices + id, id);
                if (ret)
                        goto err_chrdev;
@@ -1289,7 +1463,8 @@ static void __exit hmm_dmirror_exit(void)
        int id;
 
        for (id = 0; id < DMIRROR_NDEVICES; id++)
-               dmirror_device_remove(dmirror_devices + id);
+               if (dmirror_devices[id].zone_device_type)
+                       dmirror_device_remove(dmirror_devices + id);
        unregister_chrdev_region(dmirror_dev, DMIRROR_NDEVICES);
 }
 
index f14dea5..e31d58c 100644 (file)
@@ -31,10 +31,11 @@ struct hmm_dmirror_cmd {
 /* Expose the address space of the calling process through hmm device file */
 #define HMM_DMIRROR_READ               _IOWR('H', 0x00, struct hmm_dmirror_cmd)
 #define HMM_DMIRROR_WRITE              _IOWR('H', 0x01, struct hmm_dmirror_cmd)
-#define HMM_DMIRROR_MIGRATE            _IOWR('H', 0x02, struct hmm_dmirror_cmd)
-#define HMM_DMIRROR_SNAPSHOT           _IOWR('H', 0x03, struct hmm_dmirror_cmd)
-#define HMM_DMIRROR_EXCLUSIVE          _IOWR('H', 0x04, struct hmm_dmirror_cmd)
-#define HMM_DMIRROR_CHECK_EXCLUSIVE    _IOWR('H', 0x05, struct hmm_dmirror_cmd)
+#define HMM_DMIRROR_MIGRATE_TO_DEV     _IOWR('H', 0x02, struct hmm_dmirror_cmd)
+#define HMM_DMIRROR_MIGRATE_TO_SYS     _IOWR('H', 0x03, struct hmm_dmirror_cmd)
+#define HMM_DMIRROR_SNAPSHOT           _IOWR('H', 0x04, struct hmm_dmirror_cmd)
+#define HMM_DMIRROR_EXCLUSIVE          _IOWR('H', 0x05, struct hmm_dmirror_cmd)
+#define HMM_DMIRROR_CHECK_EXCLUSIVE    _IOWR('H', 0x06, struct hmm_dmirror_cmd)
 
 /*
  * Values returned in hmm_dmirror_cmd.ptr for HMM_DMIRROR_SNAPSHOT.
@@ -49,6 +50,8 @@ struct hmm_dmirror_cmd {
  *                                     device the ioctl() is made
  * HMM_DMIRROR_PROT_DEV_PRIVATE_REMOTE: Migrated device private page on some
  *                                     other device
+ * HMM_DMIRROR_PROT_DEV_COHERENT: Migrate device coherent page on the device
+ *                               the ioctl() is made
  */
 enum {
        HMM_DMIRROR_PROT_ERROR                  = 0xFF,
@@ -60,6 +63,14 @@ enum {
        HMM_DMIRROR_PROT_ZERO                   = 0x10,
        HMM_DMIRROR_PROT_DEV_PRIVATE_LOCAL      = 0x20,
        HMM_DMIRROR_PROT_DEV_PRIVATE_REMOTE     = 0x30,
+       HMM_DMIRROR_PROT_DEV_COHERENT_LOCAL     = 0x40,
+       HMM_DMIRROR_PROT_DEV_COHERENT_REMOTE    = 0x50,
+};
+
+enum {
+       /* 0 is reserved to catch uninitialized type fields */
+       HMM_DMIRROR_MEMORY_DEVICE_PRIVATE = 1,
+       HMM_DMIRROR_MEMORY_DEVICE_COHERENT,
 };
 
 #endif /* _LIB_TEST_HMM_UAPI_H */
index cf41fd6..4f2f2d1 100644 (file)
@@ -74,12 +74,13 @@ test_report_one_done(void)
 
 static int random_size_align_alloc_test(void)
 {
-       unsigned long size, align, rnd;
+       unsigned long size, align;
+       unsigned int rnd;
        void *ptr;
        int i;
 
        for (i = 0; i < test_loop_count; i++) {
-               get_random_bytes(&rnd, sizeof(rnd));
+               rnd = prandom_u32();
 
                /*
                 * Maximum 1024 pages, if PAGE_SIZE is 4096.
@@ -150,7 +151,7 @@ static int random_size_alloc_test(void)
        int i;
 
        for (i = 0; i < test_loop_count; i++) {
-               get_random_bytes(&n, sizeof(i));
+               n = prandom_u32();
                n = (n % 100) + 1;
 
                p = vmalloc(n * PAGE_SIZE);
@@ -294,14 +295,14 @@ pcpu_alloc_test(void)
        for (i = 0; i < 35000; i++) {
                unsigned int r;
 
-               get_random_bytes(&r, sizeof(i));
+               r = prandom_u32();
                size = (r % (PAGE_SIZE / 4)) + 1;
 
                /*
                 * Maximum PAGE_SIZE
                 */
-               get_random_bytes(&r, sizeof(i));
-               align = 1 << ((i % 11) + 1);
+               r = prandom_u32();
+               align = 1 << ((r % 11) + 1);
 
                pcpu[i] = __alloc_percpu(size, align);
                if (!pcpu[i])
@@ -396,7 +397,7 @@ static void shuffle_array(int *arr, int n)
        int i, j;
 
        for (i = n - 1; i > 0; i--)  {
-               get_random_bytes(&rnd, sizeof(rnd));
+               rnd = prandom_u32();
 
                /* Cut the range. */
                j = rnd % i;
index b7a44b1..e59cf5f 100644 (file)
@@ -33,7 +33,7 @@ config ZSWAP
          pages that are in the process of being swapped out and attempts to
          compress them into a dynamically allocated RAM-based memory pool.
          This can result in a significant I/O reduction on swap device and,
-         in the case where decompressing from RAM is faster that swap device
+         in the case where decompressing from RAM is faster than swap device
          reads, can also improve workload performance.
 
          This is marked experimental because it is a new feature (as of
@@ -655,7 +655,7 @@ config KSM
          the many instances by a single page with that content, so
          saving memory until one or another app needs to modify the content.
          Recommended for use with KVM, or with other duplicative applications.
-         See Documentation/vm/ksm.rst for more information: KSM is inactive
+         See Documentation/mm/ksm.rst for more information: KSM is inactive
          until a program has madvised that an area is MADV_MERGEABLE, and
          root has set /sys/kernel/mm/ksm/run to 1 (if CONFIG_SYSFS is set).
 
@@ -943,9 +943,6 @@ config ARCH_HAS_CURRENT_STACK_POINTER
          register alias named "current_stack_pointer", this config can be
          selected.
 
-config ARCH_HAS_VM_GET_PAGE_PROT
-       bool
-
 config ARCH_HAS_PTE_DEVMAP
        bool
 
index 6f9ffa9..9a564f8 100644 (file)
@@ -133,3 +133,4 @@ obj-$(CONFIG_PAGE_REPORTING) += page_reporting.o
 obj-$(CONFIG_IO_MAPPING) += io-mapping.o
 obj-$(CONFIG_HAVE_BOOTMEM_INFO_NODE) += bootmem_info.o
 obj-$(CONFIG_GENERIC_IOREMAP) += ioremap.o
+obj-$(CONFIG_SHRINKER_DEBUG) += shrinker_debug.o
index 2e77049..c3ffe25 100644 (file)
@@ -163,7 +163,7 @@ DEFINE_DEBUGFS_ATTRIBUTE(cma_alloc_fops, NULL, cma_alloc_write, "%llu\n");
 static void cma_debugfs_add_one(struct cma *cma, struct dentry *root_dentry)
 {
        struct dentry *tmp;
-       char name[16];
+       char name[CMA_MAX_NAME];
 
        scnprintf(name, sizeof(name), "cma-%s", cma->name);
 
index a2c53fc..640fa76 100644 (file)
@@ -613,6 +613,7 @@ static unsigned long isolate_freepages_block(struct compact_control *cc,
                        break;
                set_page_private(page, order);
 
+               nr_scanned += isolated - 1;
                total_isolated += isolated;
                cc->nr_freepages += isolated;
                list_add_tail(&page->lru, freelist);
@@ -1099,6 +1100,7 @@ isolate_success:
 isolate_success_no_list:
                cc->nr_migratepages += compound_nr(page);
                nr_isolated += compound_nr(page);
+               nr_scanned += compound_nr(page) - 1;
 
                /*
                 * Avoid isolating too much unless this block is being
@@ -1502,6 +1504,7 @@ fast_isolate_freepages(struct compact_control *cc)
                        if (__isolate_free_page(page, order)) {
                                set_page_private(page, order);
                                nr_isolated = 1 << order;
+                               nr_scanned += nr_isolated - 1;
                                cc->nr_freepages += nr_isolated;
                                list_add_tail(&page->lru, &cc->freepages);
                                count_compact_events(COMPACTISOLATED, nr_isolated);
@@ -3009,7 +3012,7 @@ void kcompactd_run(int nid)
 
 /*
  * Called by memory hotplug when all memory in a node is offlined. Caller must
- * hold mem_hotplug_begin/end().
+ * be holding mem_hotplug_begin/done().
  */
 void kcompactd_stop(int nid)
 {
index 9b559c7..66265e3 100644 (file)
@@ -92,4 +92,12 @@ config DAMON_RECLAIM
          reclamation under light memory pressure, while the traditional page
          scanning-based reclamation is used for heavy pressure.
 
+config DAMON_LRU_SORT
+       bool "Build DAMON-based LRU-lists sorting (DAMON_LRU_SORT)"
+       depends on DAMON_PADDR
+       help
+         This builds the DAMON-based LRU-lists sorting subsystem.  It tries to
+         protect frequently accessed (hot) pages while rarely accessed (cold)
+         pages reclaimed first under memory pressure.
+
 endmenu
index dbf7190..3e6b8ad 100644 (file)
@@ -6,3 +6,4 @@ obj-$(CONFIG_DAMON_PADDR)       += ops-common.o paddr.o
 obj-$(CONFIG_DAMON_SYSFS)      += sysfs.o
 obj-$(CONFIG_DAMON_DBGFS)      += dbgfs.o
 obj-$(CONFIG_DAMON_RECLAIM)    += reclaim.o
+obj-$(CONFIG_DAMON_LRU_SORT)   += lru_sort.o
index a0dab8b..cb8a7e9 100644 (file)
@@ -97,6 +97,31 @@ out:
        return ret;
 }
 
+/*
+ * Return corresponding dbgfs' scheme action value (int) for the given
+ * damos_action if the given damos_action value is valid and supported by
+ * dbgfs, negative error code otherwise.
+ */
+static int damos_action_to_dbgfs_scheme_action(enum damos_action action)
+{
+       switch (action) {
+       case DAMOS_WILLNEED:
+               return 0;
+       case DAMOS_COLD:
+               return 1;
+       case DAMOS_PAGEOUT:
+               return 2;
+       case DAMOS_HUGEPAGE:
+               return 3;
+       case DAMOS_NOHUGEPAGE:
+               return 4;
+       case DAMOS_STAT:
+               return 5;
+       default:
+               return -EINVAL;
+       }
+}
+
 static ssize_t sprint_schemes(struct damon_ctx *c, char *buf, ssize_t len)
 {
        struct damos *s;
@@ -109,7 +134,7 @@ static ssize_t sprint_schemes(struct damon_ctx *c, char *buf, ssize_t len)
                                s->min_sz_region, s->max_sz_region,
                                s->min_nr_accesses, s->max_nr_accesses,
                                s->min_age_region, s->max_age_region,
-                               s->action,
+                               damos_action_to_dbgfs_scheme_action(s->action),
                                s->quota.ms, s->quota.sz,
                                s->quota.reset_interval,
                                s->quota.weight_sz,
@@ -160,18 +185,27 @@ static void free_schemes_arr(struct damos **schemes, ssize_t nr_schemes)
        kfree(schemes);
 }
 
-static bool damos_action_valid(int action)
+/*
+ * Return corresponding damos_action for the given dbgfs input for a scheme
+ * action if the input is valid, negative error code otherwise.
+ */
+static enum damos_action dbgfs_scheme_action_to_damos_action(int dbgfs_action)
 {
-       switch (action) {
-       case DAMOS_WILLNEED:
-       case DAMOS_COLD:
-       case DAMOS_PAGEOUT:
-       case DAMOS_HUGEPAGE:
-       case DAMOS_NOHUGEPAGE:
-       case DAMOS_STAT:
-               return true;
+       switch (dbgfs_action) {
+       case 0:
+               return DAMOS_WILLNEED;
+       case 1:
+               return DAMOS_COLD;
+       case 2:
+               return DAMOS_PAGEOUT;
+       case 3:
+               return DAMOS_HUGEPAGE;
+       case 4:
+               return DAMOS_NOHUGEPAGE;
+       case 5:
+               return DAMOS_STAT;
        default:
-               return false;
+               return -EINVAL;
        }
 }
 
@@ -189,7 +223,8 @@ static struct damos **str_to_schemes(const char *str, ssize_t len,
        int pos = 0, parsed, ret;
        unsigned long min_sz, max_sz;
        unsigned int min_nr_a, max_nr_a, min_age, max_age;
-       unsigned int action;
+       unsigned int action_input;
+       enum damos_action action;
 
        schemes = kmalloc_array(max_nr_schemes, sizeof(scheme),
                        GFP_KERNEL);
@@ -204,7 +239,7 @@ static struct damos **str_to_schemes(const char *str, ssize_t len,
                ret = sscanf(&str[pos],
                                "%lu %lu %u %u %u %u %u %lu %lu %lu %u %u %u %u %lu %lu %lu %lu%n",
                                &min_sz, &max_sz, &min_nr_a, &max_nr_a,
-                               &min_age, &max_age, &action, &quota.ms,
+                               &min_age, &max_age, &action_input, &quota.ms,
                                &quota.sz, &quota.reset_interval,
                                &quota.weight_sz, &quota.weight_nr_accesses,
                                &quota.weight_age, &wmarks.metric,
@@ -212,7 +247,8 @@ static struct damos **str_to_schemes(const char *str, ssize_t len,
                                &wmarks.low, &parsed);
                if (ret != 18)
                        break;
-               if (!damos_action_valid(action))
+               action = dbgfs_scheme_action_to_damos_action(action_input);
+               if ((int)action < 0)
                        goto fail;
 
                if (min_sz > max_sz || min_nr_a > max_nr_a || min_age > max_age)
@@ -275,11 +311,6 @@ out:
        return ret;
 }
 
-static inline bool target_has_pid(const struct damon_ctx *ctx)
-{
-       return ctx->ops.id == DAMON_OPS_VADDR;
-}
-
 static ssize_t sprint_target_ids(struct damon_ctx *ctx, char *buf, ssize_t len)
 {
        struct damon_target *t;
@@ -288,7 +319,7 @@ static ssize_t sprint_target_ids(struct damon_ctx *ctx, char *buf, ssize_t len)
        int rc;
 
        damon_for_each_target(t, ctx) {
-               if (target_has_pid(ctx))
+               if (damon_target_has_pid(ctx))
                        /* Show pid numbers to debugfs users */
                        id = pid_vnr(t->pid);
                else
@@ -415,7 +446,7 @@ static int dbgfs_set_targets(struct damon_ctx *ctx, ssize_t nr_targets,
        struct damon_target *t, *next;
 
        damon_for_each_target_safe(t, next, ctx) {
-               if (target_has_pid(ctx))
+               if (damon_target_has_pid(ctx))
                        put_pid(t->pid);
                damon_destroy_target(t);
        }
@@ -425,11 +456,11 @@ static int dbgfs_set_targets(struct damon_ctx *ctx, ssize_t nr_targets,
                if (!t) {
                        damon_for_each_target_safe(t, next, ctx)
                                damon_destroy_target(t);
-                       if (target_has_pid(ctx))
+                       if (damon_target_has_pid(ctx))
                                dbgfs_put_pids(pids, nr_targets);
                        return -ENOMEM;
                }
-               if (target_has_pid(ctx))
+               if (damon_target_has_pid(ctx))
                        t->pid = pids[i];
                damon_add_target(ctx, t);
        }
@@ -722,7 +753,7 @@ static void dbgfs_before_terminate(struct damon_ctx *ctx)
 {
        struct damon_target *t, *next;
 
-       if (!target_has_pid(ctx))
+       if (!damon_target_has_pid(ctx))
                return;
 
        mutex_lock(&ctx->kdamond_lock);
diff --git a/mm/damon/lru_sort.c b/mm/damon/lru_sort.c
new file mode 100644 (file)
index 0000000..9de6f00
--- /dev/null
@@ -0,0 +1,548 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DAMON-based LRU-lists Sorting
+ *
+ * Author: SeongJae Park <sj@kernel.org>
+ */
+
+#define pr_fmt(fmt) "damon-lru-sort: " fmt
+
+#include <linux/damon.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+
+#ifdef MODULE_PARAM_PREFIX
+#undef MODULE_PARAM_PREFIX
+#endif
+#define MODULE_PARAM_PREFIX "damon_lru_sort."
+
+/*
+ * Enable or disable DAMON_LRU_SORT.
+ *
+ * You can enable DAMON_LRU_SORT by setting the value of this parameter as
+ * ``Y``.  Setting it as ``N`` disables DAMON_LRU_SORT.  Note that
+ * DAMON_LRU_SORT could do no real monitoring and LRU-lists sorting due to the
+ * watermarks-based activation condition.  Refer to below descriptions for the
+ * watermarks parameter for this.
+ */
+static bool enabled __read_mostly;
+
+/*
+ * Make DAMON_LRU_SORT reads the input parameters again, except ``enabled``.
+ *
+ * Input parameters that updated while DAMON_LRU_SORT is running are not
+ * applied by default.  Once this parameter is set as ``Y``, DAMON_LRU_SORT
+ * reads values of parametrs except ``enabled`` again.  Once the re-reading is
+ * done, this parameter is set as ``N``.  If invalid parameters are found while
+ * the re-reading, DAMON_LRU_SORT will be disabled.
+ */
+static bool commit_inputs __read_mostly;
+module_param(commit_inputs, bool, 0600);
+
+/*
+ * Access frequency threshold for hot memory regions identification in permil.
+ *
+ * If a memory region is accessed in frequency of this or higher,
+ * DAMON_LRU_SORT identifies the region as hot, and mark it as accessed on the
+ * LRU list, so that it could not be reclaimed under memory pressure.  50% by
+ * default.
+ */
+static unsigned long hot_thres_access_freq = 500;
+module_param(hot_thres_access_freq, ulong, 0600);
+
+/*
+ * Time threshold for cold memory regions identification in microseconds.
+ *
+ * If a memory region is not accessed for this or longer time, DAMON_LRU_SORT
+ * identifies the region as cold, and mark it as unaccessed on the LRU list, so
+ * that it could be reclaimed first under memory pressure.  120 seconds by
+ * default.
+ */
+static unsigned long cold_min_age __read_mostly = 120000000;
+module_param(cold_min_age, ulong, 0600);
+
+/*
+ * Limit of time for trying the LRU lists sorting in milliseconds.
+ *
+ * DAMON_LRU_SORT tries to use only up to this time within a time window
+ * (quota_reset_interval_ms) for trying LRU lists sorting.  This can be used
+ * for limiting CPU consumption of DAMON_LRU_SORT.  If the value is zero, the
+ * limit is disabled.
+ *
+ * 10 ms by default.
+ */
+static unsigned long quota_ms __read_mostly = 10;
+module_param(quota_ms, ulong, 0600);
+
+/*
+ * The time quota charge reset interval in milliseconds.
+ *
+ * The charge reset interval for the quota of time (quota_ms).  That is,
+ * DAMON_LRU_SORT does not try LRU-lists sorting for more than quota_ms
+ * milliseconds or quota_sz bytes within quota_reset_interval_ms milliseconds.
+ *
+ * 1 second by default.
+ */
+static unsigned long quota_reset_interval_ms __read_mostly = 1000;
+module_param(quota_reset_interval_ms, ulong, 0600);
+
+/*
+ * The watermarks check time interval in microseconds.
+ *
+ * Minimal time to wait before checking the watermarks, when DAMON_LRU_SORT is
+ * enabled but inactive due to its watermarks rule.  5 seconds by default.
+ */
+static unsigned long wmarks_interval __read_mostly = 5000000;
+module_param(wmarks_interval, ulong, 0600);
+
+/*
+ * Free memory rate (per thousand) for the high watermark.
+ *
+ * If free memory of the system in bytes per thousand bytes is higher than
+ * this, DAMON_LRU_SORT becomes inactive, so it does nothing but periodically
+ * checks the watermarks.  200 (20%) by default.
+ */
+static unsigned long wmarks_high __read_mostly = 200;
+module_param(wmarks_high, ulong, 0600);
+
+/*
+ * Free memory rate (per thousand) for the middle watermark.
+ *
+ * If free memory of the system in bytes per thousand bytes is between this and
+ * the low watermark, DAMON_LRU_SORT becomes active, so starts the monitoring
+ * and the LRU-lists sorting.  150 (15%) by default.
+ */
+static unsigned long wmarks_mid __read_mostly = 150;
+module_param(wmarks_mid, ulong, 0600);
+
+/*
+ * Free memory rate (per thousand) for the low watermark.
+ *
+ * If free memory of the system in bytes per thousand bytes is lower than this,
+ * DAMON_LRU_SORT becomes inactive, so it does nothing but periodically checks
+ * the watermarks.  50 (5%) by default.
+ */
+static unsigned long wmarks_low __read_mostly = 50;
+module_param(wmarks_low, ulong, 0600);
+
+/*
+ * Sampling interval for the monitoring in microseconds.
+ *
+ * The sampling interval of DAMON for the hot/cold memory monitoring.  Please
+ * refer to the DAMON documentation for more detail.  5 ms by default.
+ */
+static unsigned long sample_interval __read_mostly = 5000;
+module_param(sample_interval, ulong, 0600);
+
+/*
+ * Aggregation interval for the monitoring in microseconds.
+ *
+ * The aggregation interval of DAMON for the hot/cold memory monitoring.
+ * Please refer to the DAMON documentation for more detail.  100 ms by default.
+ */
+static unsigned long aggr_interval __read_mostly = 100000;
+module_param(aggr_interval, ulong, 0600);
+
+/*
+ * Minimum number of monitoring regions.
+ *
+ * The minimal number of monitoring regions of DAMON for the hot/cold memory
+ * monitoring.  This can be used to set lower-bound of the monitoring quality.
+ * But, setting this too high could result in increased monitoring overhead.
+ * Please refer to the DAMON documentation for more detail.  10 by default.
+ */
+static unsigned long min_nr_regions __read_mostly = 10;
+module_param(min_nr_regions, ulong, 0600);
+
+/*
+ * Maximum number of monitoring regions.
+ *
+ * The maximum number of monitoring regions of DAMON for the hot/cold memory
+ * monitoring.  This can be used to set upper-bound of the monitoring overhead.
+ * However, setting this too low could result in bad monitoring quality.
+ * Please refer to the DAMON documentation for more detail.  1000 by default.
+ */
+static unsigned long max_nr_regions __read_mostly = 1000;
+module_param(max_nr_regions, ulong, 0600);
+
+/*
+ * Start of the target memory region in physical address.
+ *
+ * The start physical address of memory region that DAMON_LRU_SORT will do work
+ * against.  By default, biggest System RAM is used as the region.
+ */
+static unsigned long monitor_region_start __read_mostly;
+module_param(monitor_region_start, ulong, 0600);
+
+/*
+ * End of the target memory region in physical address.
+ *
+ * The end physical address of memory region that DAMON_LRU_SORT will do work
+ * against.  By default, biggest System RAM is used as the region.
+ */
+static unsigned long monitor_region_end __read_mostly;
+module_param(monitor_region_end, ulong, 0600);
+
+/*
+ * PID of the DAMON thread
+ *
+ * If DAMON_LRU_SORT is enabled, this becomes the PID of the worker thread.
+ * Else, -1.
+ */
+static int kdamond_pid __read_mostly = -1;
+module_param(kdamond_pid, int, 0400);
+
+/*
+ * Number of hot memory regions that tried to be LRU-sorted.
+ */
+static unsigned long nr_lru_sort_tried_hot_regions __read_mostly;
+module_param(nr_lru_sort_tried_hot_regions, ulong, 0400);
+
+/*
+ * Total bytes of hot memory regions that tried to be LRU-sorted.
+ */
+static unsigned long bytes_lru_sort_tried_hot_regions __read_mostly;
+module_param(bytes_lru_sort_tried_hot_regions, ulong, 0400);
+
+/*
+ * Number of hot memory regions that successfully be LRU-sorted.
+ */
+static unsigned long nr_lru_sorted_hot_regions __read_mostly;
+module_param(nr_lru_sorted_hot_regions, ulong, 0400);
+
+/*
+ * Total bytes of hot memory regions that successfully be LRU-sorted.
+ */
+static unsigned long bytes_lru_sorted_hot_regions __read_mostly;
+module_param(bytes_lru_sorted_hot_regions, ulong, 0400);
+
+/*
+ * Number of times that the time quota limit for hot regions have exceeded
+ */
+static unsigned long nr_hot_quota_exceeds __read_mostly;
+module_param(nr_hot_quota_exceeds, ulong, 0400);
+
+/*
+ * Number of cold memory regions that tried to be LRU-sorted.
+ */
+static unsigned long nr_lru_sort_tried_cold_regions __read_mostly;
+module_param(nr_lru_sort_tried_cold_regions, ulong, 0400);
+
+/*
+ * Total bytes of cold memory regions that tried to be LRU-sorted.
+ */
+static unsigned long bytes_lru_sort_tried_cold_regions __read_mostly;
+module_param(bytes_lru_sort_tried_cold_regions, ulong, 0400);
+
+/*
+ * Number of cold memory regions that successfully be LRU-sorted.
+ */
+static unsigned long nr_lru_sorted_cold_regions __read_mostly;
+module_param(nr_lru_sorted_cold_regions, ulong, 0400);
+
+/*
+ * Total bytes of cold memory regions that successfully be LRU-sorted.
+ */
+static unsigned long bytes_lru_sorted_cold_regions __read_mostly;
+module_param(bytes_lru_sorted_cold_regions, ulong, 0400);
+
+/*
+ * Number of times that the time quota limit for cold regions have exceeded
+ */
+static unsigned long nr_cold_quota_exceeds __read_mostly;
+module_param(nr_cold_quota_exceeds, ulong, 0400);
+
+static struct damon_ctx *ctx;
+static struct damon_target *target;
+
+struct damon_lru_sort_ram_walk_arg {
+       unsigned long start;
+       unsigned long end;
+};
+
+static int walk_system_ram(struct resource *res, void *arg)
+{
+       struct damon_lru_sort_ram_walk_arg *a = arg;
+
+       if (a->end - a->start < resource_size(res)) {
+               a->start = res->start;
+               a->end = res->end;
+       }
+       return 0;
+}
+
+/*
+ * Find biggest 'System RAM' resource and store its start and end address in
+ * @start and @end, respectively.  If no System RAM is found, returns false.
+ */
+static bool get_monitoring_region(unsigned long *start, unsigned long *end)
+{
+       struct damon_lru_sort_ram_walk_arg arg = {};
+
+       walk_system_ram_res(0, ULONG_MAX, &arg, walk_system_ram);
+       if (arg.end <= arg.start)
+               return false;
+
+       *start = arg.start;
+       *end = arg.end;
+       return true;
+}
+
+/* Create a DAMON-based operation scheme for hot memory regions */
+static struct damos *damon_lru_sort_new_hot_scheme(unsigned int hot_thres)
+{
+       struct damos_watermarks wmarks = {
+               .metric = DAMOS_WMARK_FREE_MEM_RATE,
+               .interval = wmarks_interval,
+               .high = wmarks_high,
+               .mid = wmarks_mid,
+               .low = wmarks_low,
+       };
+       struct damos_quota quota = {
+               /*
+                * Do not try LRU-lists sorting of hot pages for more than half
+                * of quota_ms milliseconds within quota_reset_interval_ms.
+                */
+               .ms = quota_ms / 2,
+               .sz = 0,
+               .reset_interval = quota_reset_interval_ms,
+               /* Within the quota, mark hotter regions accessed first. */
+               .weight_sz = 0,
+               .weight_nr_accesses = 1,
+               .weight_age = 0,
+       };
+       struct damos *scheme = damon_new_scheme(
+                       /* Find regions having PAGE_SIZE or larger size */
+                       PAGE_SIZE, ULONG_MAX,
+                       /* and accessed for more than the threshold */
+                       hot_thres, UINT_MAX,
+                       /* no matter its age */
+                       0, UINT_MAX,
+                       /* prioritize those on LRU lists, as soon as found */
+                       DAMOS_LRU_PRIO,
+                       /* under the quota. */
+                       &quota,
+                       /* (De)activate this according to the watermarks. */
+                       &wmarks);
+
+       return scheme;
+}
+
+/* Create a DAMON-based operation scheme for cold memory regions */
+static struct damos *damon_lru_sort_new_cold_scheme(unsigned int cold_thres)
+{
+       struct damos_watermarks wmarks = {
+               .metric = DAMOS_WMARK_FREE_MEM_RATE,
+               .interval = wmarks_interval,
+               .high = wmarks_high,
+               .mid = wmarks_mid,
+               .low = wmarks_low,
+       };
+       struct damos_quota quota = {
+               /*
+                * Do not try LRU-lists sorting of cold pages for more than
+                * half of quota_ms milliseconds within
+                * quota_reset_interval_ms.
+                */
+               .ms = quota_ms / 2,
+               .sz = 0,
+               .reset_interval = quota_reset_interval_ms,
+               /* Within the quota, mark colder regions not accessed first. */
+               .weight_sz = 0,
+               .weight_nr_accesses = 0,
+               .weight_age = 1,
+       };
+       struct damos *scheme = damon_new_scheme(
+                       /* Find regions having PAGE_SIZE or larger size */
+                       PAGE_SIZE, ULONG_MAX,
+                       /* and not accessed at all */
+                       0, 0,
+                       /* for cold_thres or more micro-seconds, and */
+                       cold_thres, UINT_MAX,
+                       /* mark those as not accessed, as soon as found */
+                       DAMOS_LRU_DEPRIO,
+                       /* under the quota. */
+                       &quota,
+                       /* (De)activate this according to the watermarks. */
+                       &wmarks);
+
+       return scheme;
+}
+
+static int damon_lru_sort_apply_parameters(void)
+{
+       struct damos *scheme, *next_scheme;
+       struct damon_addr_range addr_range;
+       unsigned int hot_thres, cold_thres;
+       int err = 0;
+
+       err = damon_set_attrs(ctx, sample_interval, aggr_interval, 0,
+                       min_nr_regions, max_nr_regions);
+       if (err)
+               return err;
+
+       /* free previously set schemes */
+       damon_for_each_scheme_safe(scheme, next_scheme, ctx)
+               damon_destroy_scheme(scheme);
+
+       /* aggr_interval / sample_interval is the maximum nr_accesses */
+       hot_thres = aggr_interval / sample_interval * hot_thres_access_freq /
+               1000;
+       scheme = damon_lru_sort_new_hot_scheme(hot_thres);
+       if (!scheme)
+               return -ENOMEM;
+       damon_add_scheme(ctx, scheme);
+
+       cold_thres = cold_min_age / aggr_interval;
+       scheme = damon_lru_sort_new_cold_scheme(cold_thres);
+       if (!scheme)
+               return -ENOMEM;
+       damon_add_scheme(ctx, scheme);
+
+       if (monitor_region_start > monitor_region_end)
+               return -EINVAL;
+       if (!monitor_region_start && !monitor_region_end &&
+                       !get_monitoring_region(&monitor_region_start,
+                               &monitor_region_end))
+               return -EINVAL;
+       addr_range.start = monitor_region_start;
+       addr_range.end = monitor_region_end;
+       return damon_set_regions(target, &addr_range, 1);
+}
+
+static int damon_lru_sort_turn(bool on)
+{
+       int err;
+
+       if (!on) {
+               err = damon_stop(&ctx, 1);
+               if (!err)
+                       kdamond_pid = -1;
+               return err;
+       }
+
+       err = damon_lru_sort_apply_parameters();
+       if (err)
+               return err;
+
+       err = damon_start(&ctx, 1, true);
+       if (err)
+               return err;
+       kdamond_pid = ctx->kdamond->pid;
+       return 0;
+}
+
+static struct delayed_work damon_lru_sort_timer;
+static void damon_lru_sort_timer_fn(struct work_struct *work)
+{
+       static bool last_enabled;
+       bool now_enabled;
+
+       now_enabled = enabled;
+       if (last_enabled != now_enabled) {
+               if (!damon_lru_sort_turn(now_enabled))
+                       last_enabled = now_enabled;
+               else
+                       enabled = last_enabled;
+       }
+}
+static DECLARE_DELAYED_WORK(damon_lru_sort_timer, damon_lru_sort_timer_fn);
+
+static bool damon_lru_sort_initialized;
+
+static int damon_lru_sort_enabled_store(const char *val,
+               const struct kernel_param *kp)
+{
+       int rc = param_set_bool(val, kp);
+
+       if (rc < 0)
+               return rc;
+
+       if (!damon_lru_sort_initialized)
+               return rc;
+
+       schedule_delayed_work(&damon_lru_sort_timer, 0);
+
+       return 0;
+}
+
+static const struct kernel_param_ops enabled_param_ops = {
+       .set = damon_lru_sort_enabled_store,
+       .get = param_get_bool,
+};
+
+module_param_cb(enabled, &enabled_param_ops, &enabled, 0600);
+MODULE_PARM_DESC(enabled,
+       "Enable or disable DAMON_LRU_SORT (default: disabled)");
+
+static int damon_lru_sort_handle_commit_inputs(void)
+{
+       int err;
+
+       if (!commit_inputs)
+               return 0;
+
+       err = damon_lru_sort_apply_parameters();
+       commit_inputs = false;
+       return err;
+}
+
+static int damon_lru_sort_after_aggregation(struct damon_ctx *c)
+{
+       struct damos *s;
+
+       /* update the stats parameter */
+       damon_for_each_scheme(s, c) {
+               if (s->action == DAMOS_LRU_PRIO) {
+                       nr_lru_sort_tried_hot_regions = s->stat.nr_tried;
+                       bytes_lru_sort_tried_hot_regions = s->stat.sz_tried;
+                       nr_lru_sorted_hot_regions = s->stat.nr_applied;
+                       bytes_lru_sorted_hot_regions = s->stat.sz_applied;
+                       nr_hot_quota_exceeds = s->stat.qt_exceeds;
+               } else if (s->action == DAMOS_LRU_DEPRIO) {
+                       nr_lru_sort_tried_cold_regions = s->stat.nr_tried;
+                       bytes_lru_sort_tried_cold_regions = s->stat.sz_tried;
+                       nr_lru_sorted_cold_regions = s->stat.nr_applied;
+                       bytes_lru_sorted_cold_regions = s->stat.sz_applied;
+                       nr_cold_quota_exceeds = s->stat.qt_exceeds;
+               }
+       }
+
+       return damon_lru_sort_handle_commit_inputs();
+}
+
+static int damon_lru_sort_after_wmarks_check(struct damon_ctx *c)
+{
+       return damon_lru_sort_handle_commit_inputs();
+}
+
+static int __init damon_lru_sort_init(void)
+{
+       ctx = damon_new_ctx();
+       if (!ctx)
+               return -ENOMEM;
+
+       if (damon_select_ops(ctx, DAMON_OPS_PADDR)) {
+               damon_destroy_ctx(ctx);
+               return -EINVAL;
+       }
+
+       ctx->callback.after_wmarks_check = damon_lru_sort_after_wmarks_check;
+       ctx->callback.after_aggregation = damon_lru_sort_after_aggregation;
+
+       target = damon_new_target();
+       if (!target) {
+               damon_destroy_ctx(ctx);
+               return -ENOMEM;
+       }
+       damon_add_target(ctx, target);
+
+       schedule_delayed_work(&damon_lru_sort_timer, 0);
+
+       damon_lru_sort_initialized = true;
+       return 0;
+}
+
+module_init(damon_lru_sort_init);
index 10ef20b..b1335de 100644 (file)
@@ -130,3 +130,45 @@ int damon_pageout_score(struct damon_ctx *c, struct damon_region *r,
        /* Return coldness of the region */
        return DAMOS_MAX_SCORE - hotness;
 }
+
+int damon_hot_score(struct damon_ctx *c, struct damon_region *r,
+                       struct damos *s)
+{
+       unsigned int max_nr_accesses;
+       int freq_subscore;
+       unsigned int age_in_sec;
+       int age_in_log, age_subscore;
+       unsigned int freq_weight = s->quota.weight_nr_accesses;
+       unsigned int age_weight = s->quota.weight_age;
+       int hotness;
+
+       max_nr_accesses = c->aggr_interval / c->sample_interval;
+       freq_subscore = r->nr_accesses * DAMON_MAX_SUBSCORE / max_nr_accesses;
+
+       age_in_sec = (unsigned long)r->age * c->aggr_interval / 1000000;
+       for (age_in_log = 0; age_in_log < DAMON_MAX_AGE_IN_LOG && age_in_sec;
+                       age_in_log++, age_in_sec >>= 1)
+               ;
+
+       /* If frequency is 0, higher age means it's colder */
+       if (freq_subscore == 0)
+               age_in_log *= -1;
+
+       /*
+        * Now age_in_log is in [-DAMON_MAX_AGE_IN_LOG, DAMON_MAX_AGE_IN_LOG].
+        * Scale it to be in [0, 100] and set it as age subscore.
+        */
+       age_in_log += DAMON_MAX_AGE_IN_LOG;
+       age_subscore = age_in_log * DAMON_MAX_SUBSCORE /
+               DAMON_MAX_AGE_IN_LOG / 2;
+
+       hotness = (freq_weight * freq_subscore + age_weight * age_subscore);
+       if (freq_weight + age_weight)
+               hotness /= freq_weight + age_weight;
+       /*
+        * Transform it to fit in [0, DAMOS_MAX_SCORE]
+        */
+       hotness = hotness * DAMOS_MAX_SCORE / DAMON_MAX_SUBSCORE;
+
+       return hotness;
+}
index e790cb5..52329ff 100644 (file)
@@ -14,3 +14,5 @@ void damon_pmdp_mkold(pmd_t *pmd, struct mm_struct *mm, unsigned long addr);
 
 int damon_pageout_score(struct damon_ctx *c, struct damon_region *r,
                        struct damos *s);
+int damon_hot_score(struct damon_ctx *c, struct damon_region *r,
+                       struct damos *s);
index b40ff58..dc131c6 100644 (file)
@@ -204,16 +204,11 @@ static unsigned int damon_pa_check_accesses(struct damon_ctx *ctx)
        return max_nr_accesses;
 }
 
-static unsigned long damon_pa_apply_scheme(struct damon_ctx *ctx,
-               struct damon_target *t, struct damon_region *r,
-               struct damos *scheme)
+static unsigned long damon_pa_pageout(struct damon_region *r)
 {
        unsigned long addr, applied;
        LIST_HEAD(page_list);
 
-       if (scheme->action != DAMOS_PAGEOUT)
-               return 0;
-
        for (addr = r->ar.start; addr < r->ar.end; addr += PAGE_SIZE) {
                struct page *page = damon_get_page(PHYS_PFN(addr));
 
@@ -238,6 +233,55 @@ static unsigned long damon_pa_apply_scheme(struct damon_ctx *ctx,
        return applied * PAGE_SIZE;
 }
 
+static unsigned long damon_pa_mark_accessed(struct damon_region *r)
+{
+       unsigned long addr, applied = 0;
+
+       for (addr = r->ar.start; addr < r->ar.end; addr += PAGE_SIZE) {
+               struct page *page = damon_get_page(PHYS_PFN(addr));
+
+               if (!page)
+                       continue;
+               mark_page_accessed(page);
+               put_page(page);
+               applied++;
+       }
+       return applied * PAGE_SIZE;
+}
+
+static unsigned long damon_pa_deactivate_pages(struct damon_region *r)
+{
+       unsigned long addr, applied = 0;
+
+       for (addr = r->ar.start; addr < r->ar.end; addr += PAGE_SIZE) {
+               struct page *page = damon_get_page(PHYS_PFN(addr));
+
+               if (!page)
+                       continue;
+               deactivate_page(page);
+               put_page(page);
+               applied++;
+       }
+       return applied * PAGE_SIZE;
+}
+
+static unsigned long damon_pa_apply_scheme(struct damon_ctx *ctx,
+               struct damon_target *t, struct damon_region *r,
+               struct damos *scheme)
+{
+       switch (scheme->action) {
+       case DAMOS_PAGEOUT:
+               return damon_pa_pageout(r);
+       case DAMOS_LRU_PRIO:
+               return damon_pa_mark_accessed(r);
+       case DAMOS_LRU_DEPRIO:
+               return damon_pa_deactivate_pages(r);
+       default:
+               break;
+       }
+       return 0;
+}
+
 static int damon_pa_scheme_score(struct damon_ctx *context,
                struct damon_target *t, struct damon_region *r,
                struct damos *scheme)
@@ -245,6 +289,10 @@ static int damon_pa_scheme_score(struct damon_ctx *context,
        switch (scheme->action) {
        case DAMOS_PAGEOUT:
                return damon_pageout_score(context, r, scheme);
+       case DAMOS_LRU_PRIO:
+               return damon_hot_score(context, r, scheme);
+       case DAMOS_LRU_DEPRIO:
+               return damon_pageout_score(context, r, scheme);
        default:
                break;
        }
index 4b07c29..a7faf51 100644 (file)
@@ -353,7 +353,6 @@ static int damon_reclaim_turn(bool on)
        return 0;
 }
 
-#define ENABLE_CHECK_INTERVAL_MS       1000
 static struct delayed_work damon_reclaim_timer;
 static void damon_reclaim_timer_fn(struct work_struct *work)
 {
@@ -367,16 +366,12 @@ static void damon_reclaim_timer_fn(struct work_struct *work)
                else
                        enabled = last_enabled;
        }
-
-       if (enabled)
-               schedule_delayed_work(&damon_reclaim_timer,
-                       msecs_to_jiffies(ENABLE_CHECK_INTERVAL_MS));
 }
 static DECLARE_DELAYED_WORK(damon_reclaim_timer, damon_reclaim_timer_fn);
 
 static bool damon_reclaim_initialized;
 
-static int enabled_store(const char *val,
+static int damon_reclaim_enabled_store(const char *val,
                const struct kernel_param *kp)
 {
        int rc = param_set_bool(val, kp);
@@ -388,14 +383,12 @@ static int enabled_store(const char *val,
        if (!damon_reclaim_initialized)
                return rc;
 
-       if (enabled)
-               schedule_delayed_work(&damon_reclaim_timer, 0);
-
+       schedule_delayed_work(&damon_reclaim_timer, 0);
        return 0;
 }
 
 static const struct kernel_param_ops enabled_param_ops = {
-       .set = enabled_store,
+       .set = damon_reclaim_enabled_store,
        .get = param_get_bool,
 };
 
@@ -403,10 +396,21 @@ module_param_cb(enabled, &enabled_param_ops, &enabled, 0600);
 MODULE_PARM_DESC(enabled,
        "Enable or disable DAMON_RECLAIM (default: disabled)");
 
+static int damon_reclaim_handle_commit_inputs(void)
+{
+       int err;
+
+       if (!commit_inputs)
+               return 0;
+
+       err = damon_reclaim_apply_parameters();
+       commit_inputs = false;
+       return err;
+}
+
 static int damon_reclaim_after_aggregation(struct damon_ctx *c)
 {
        struct damos *s;
-       int err = 0;
 
        /* update the stats parameter */
        damon_for_each_scheme(s, c) {
@@ -417,22 +421,12 @@ static int damon_reclaim_after_aggregation(struct damon_ctx *c)
                nr_quota_exceeds = s->stat.qt_exceeds;
        }
 
-       if (commit_inputs) {
-               err = damon_reclaim_apply_parameters();
-               commit_inputs = false;
-       }
-       return err;
+       return damon_reclaim_handle_commit_inputs();
 }
 
 static int damon_reclaim_after_wmarks_check(struct damon_ctx *c)
 {
-       int err = 0;
-
-       if (commit_inputs) {
-               err = damon_reclaim_apply_parameters();
-               commit_inputs = false;
-       }
-       return err;
+       return damon_reclaim_handle_commit_inputs();
 }
 
 static int __init damon_reclaim_init(void)
@@ -441,8 +435,10 @@ static int __init damon_reclaim_init(void)
        if (!ctx)
                return -ENOMEM;
 
-       if (damon_select_ops(ctx, DAMON_OPS_PADDR))
+       if (damon_select_ops(ctx, DAMON_OPS_PADDR)) {
+               damon_destroy_ctx(ctx);
                return -EINVAL;
+       }
 
        ctx->callback.after_wmarks_check = damon_reclaim_after_wmarks_check;
        ctx->callback.after_aggregation = damon_reclaim_after_aggregation;
index 09f9e8c..7488e27 100644 (file)
@@ -762,6 +762,8 @@ static const char * const damon_sysfs_damos_action_strs[] = {
        "pageout",
        "hugepage",
        "nohugepage",
+       "lru_prio",
+       "lru_deprio",
        "stat",
 };
 
@@ -2136,8 +2138,7 @@ static void damon_sysfs_destroy_targets(struct damon_ctx *ctx)
        struct damon_target *t, *next;
 
        damon_for_each_target_safe(t, next, ctx) {
-               if (ctx->ops.id == DAMON_OPS_VADDR ||
-                               ctx->ops.id == DAMON_OPS_FVADDR)
+               if (damon_target_has_pid(ctx))
                        put_pid(t->pid);
                damon_destroy_target(t);
        }
@@ -2181,8 +2182,7 @@ static int damon_sysfs_add_target(struct damon_sysfs_target *sys_target,
 
        if (!t)
                return -ENOMEM;
-       if (ctx->ops.id == DAMON_OPS_VADDR ||
-                       ctx->ops.id == DAMON_OPS_FVADDR) {
+       if (damon_target_has_pid(ctx)) {
                t->pid = find_get_pid(sys_target->pid);
                if (!t->pid)
                        goto destroy_targets_out;
@@ -2210,7 +2210,7 @@ static struct damon_target *damon_sysfs_existing_target(
        struct pid *pid;
        struct damon_target *t;
 
-       if (ctx->ops.id == DAMON_OPS_PADDR) {
+       if (!damon_target_has_pid(ctx)) {
                /* Up to only one target for paddr could exist */
                damon_for_each_target(t, ctx)
                        return t;
@@ -2359,6 +2359,23 @@ static inline bool damon_sysfs_kdamond_running(
                damon_sysfs_ctx_running(kdamond->damon_ctx);
 }
 
+static int damon_sysfs_apply_inputs(struct damon_ctx *ctx,
+               struct damon_sysfs_context *sys_ctx)
+{
+       int err;
+
+       err = damon_select_ops(ctx, sys_ctx->ops_id);
+       if (err)
+               return err;
+       err = damon_sysfs_set_attrs(ctx, sys_ctx->attrs);
+       if (err)
+               return err;
+       err = damon_sysfs_set_targets(ctx, sys_ctx->targets);
+       if (err)
+               return err;
+       return damon_sysfs_set_schemes(ctx, sys_ctx->schemes);
+}
+
 /*
  * damon_sysfs_commit_input() - Commit user inputs to a running kdamond.
  * @kdamond:   The kobject wrapper for the associated kdamond.
@@ -2367,31 +2384,14 @@ static inline bool damon_sysfs_kdamond_running(
  */
 static int damon_sysfs_commit_input(struct damon_sysfs_kdamond *kdamond)
 {
-       struct damon_ctx *ctx = kdamond->damon_ctx;
-       struct damon_sysfs_context *sys_ctx;
-       int err = 0;
-
        if (!damon_sysfs_kdamond_running(kdamond))
                return -EINVAL;
        /* TODO: Support multiple contexts per kdamond */
        if (kdamond->contexts->nr != 1)
                return -EINVAL;
 
-       sys_ctx = kdamond->contexts->contexts_arr[0];
-
-       err = damon_select_ops(ctx, sys_ctx->ops_id);
-       if (err)
-               return err;
-       err = damon_sysfs_set_attrs(ctx, sys_ctx->attrs);
-       if (err)
-               return err;
-       err = damon_sysfs_set_targets(ctx, sys_ctx->targets);
-       if (err)
-               return err;
-       err = damon_sysfs_set_schemes(ctx, sys_ctx->schemes);
-       if (err)
-               return err;
-       return err;
+       return damon_sysfs_apply_inputs(kdamond->damon_ctx,
+                       kdamond->contexts->contexts_arr[0]);
 }
 
 /*
@@ -2438,27 +2438,16 @@ static struct damon_ctx *damon_sysfs_build_ctx(
        if (!ctx)
                return ERR_PTR(-ENOMEM);
 
-       err = damon_select_ops(ctx, sys_ctx->ops_id);
-       if (err)
-               goto out;
-       err = damon_sysfs_set_attrs(ctx, sys_ctx->attrs);
-       if (err)
-               goto out;
-       err = damon_sysfs_set_targets(ctx, sys_ctx->targets);
-       if (err)
-               goto out;
-       err = damon_sysfs_set_schemes(ctx, sys_ctx->schemes);
-       if (err)
-               goto out;
+       err = damon_sysfs_apply_inputs(ctx, sys_ctx);
+       if (err) {
+               damon_destroy_ctx(ctx);
+               return ERR_PTR(err);
+       }
 
        ctx->callback.after_wmarks_check = damon_sysfs_cmd_request_callback;
        ctx->callback.after_aggregation = damon_sysfs_cmd_request_callback;
        ctx->callback.before_terminate = damon_sysfs_before_terminate;
        return ctx;
-
-out:
-       damon_destroy_ctx(ctx);
-       return ERR_PTR(err);
 }
 
 static int damon_sysfs_turn_damon_on(struct damon_sysfs_kdamond *kdamond)
index 1ab091f..dc7df12 100644 (file)
@@ -35,7 +35,7 @@
 #include <asm/tlbflush.h>
 
 /*
- * Please refer Documentation/vm/arch_pgtable_helpers.rst for the semantics
+ * Please refer Documentation/mm/arch_pgtable_helpers.rst for the semantics
  * expectations that are being validated here. All future changes in here
  * or the documentation need to be in sync.
  */
index 0dec96e..1580033 100644 (file)
@@ -667,7 +667,7 @@ EXPORT_SYMBOL_GPL(filemap_range_has_writeback);
 int filemap_write_and_wait_range(struct address_space *mapping,
                                 loff_t lstart, loff_t lend)
 {
-       int err = 0;
+       int err = 0, err2;
 
        if (mapping_needs_writeback(mapping)) {
                err = __filemap_fdatawrite_range(mapping, lstart, lend,
@@ -678,18 +678,12 @@ int filemap_write_and_wait_range(struct address_space *mapping,
                 * But the -EIO is special case, it may indicate the worst
                 * thing (e.g. bug) happened, so we avoid waiting for it.
                 */
-               if (err != -EIO) {
-                       int err2 = filemap_fdatawait_range(mapping,
-                                               lstart, lend);
-                       if (!err)
-                               err = err2;
-               } else {
-                       /* Clear any previously stored errors */
-                       filemap_check_errors(mapping);
-               }
-       } else {
-               err = filemap_check_errors(mapping);
+               if (err != -EIO)
+                       __filemap_fdatawait_range(mapping, lstart, lend);
        }
+       err2 = filemap_check_errors(mapping);
+       if (!err)
+               err = err2;
        return err;
 }
 EXPORT_SYMBOL(filemap_write_and_wait_range);
index 6f69b04..1a97610 100644 (file)
@@ -4,7 +4,7 @@
  *
  * This code provides the generic "frontend" layer to call a matching
  * "backend" driver implementation of frontswap.  See
- * Documentation/vm/frontswap.rst for more information.
+ * Documentation/mm/frontswap.rst for more information.
  *
  * Copyright (C) 2009-2012 Oracle Corp.  All rights reserved.
  * Author: Dan Magenheimer
index e2a39e3..7328251 100644 (file)
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -134,7 +134,7 @@ struct folio *try_grab_folio(struct page *page, int refs, unsigned int flags)
                 * path.
                 */
                if (unlikely((flags & FOLL_LONGTERM) &&
-                            !is_pinnable_page(page)))
+                            !is_longterm_pinnable_page(page)))
                        return NULL;
 
                /*
@@ -953,6 +953,25 @@ static int faultin_page(struct vm_area_struct *vma,
        }
 
        ret = handle_mm_fault(vma, address, fault_flags, NULL);
+
+       if (ret & VM_FAULT_COMPLETED) {
+               /*
+                * With FAULT_FLAG_RETRY_NOWAIT we'll never release the
+                * mmap lock in the page fault handler. Sanity check this.
+                */
+               WARN_ON_ONCE(fault_flags & FAULT_FLAG_RETRY_NOWAIT);
+               if (locked)
+                       *locked = 0;
+               /*
+                * We should do the same as VM_FAULT_RETRY, but let's not
+                * return -EBUSY since that's not reflecting the reality of
+                * what has happened - we've just fully completed a page
+                * fault, with the mmap lock released.  Use -EAGAIN to show
+                * that we want to take the mmap lock _again_.
+                */
+               return -EAGAIN;
+       }
+
        if (ret & VM_FAULT_ERROR) {
                int err = vm_fault_to_errno(ret, *flags);
 
@@ -1179,6 +1198,7 @@ retry:
                        case 0:
                                goto retry;
                        case -EBUSY:
+                       case -EAGAIN:
                                ret = 0;
                                fallthrough;
                        case -EFAULT:
@@ -1305,6 +1325,18 @@ retry:
                return -EINTR;
 
        ret = handle_mm_fault(vma, address, fault_flags, NULL);
+
+       if (ret & VM_FAULT_COMPLETED) {
+               /*
+                * NOTE: it's a pity that we need to retake the lock here
+                * to pair with the unlock() in the callers. Ideally we
+                * could tell the callers so they do not need to unlock.
+                */
+               mmap_read_lock(mm);
+               *unlocked = true;
+               return 0;
+       }
+
        if (ret & VM_FAULT_ERROR) {
                int err = vm_fault_to_errno(ret, 0);
 
@@ -1370,7 +1402,7 @@ static __always_inline long __get_user_pages_locked(struct mm_struct *mm,
                        /* VM_FAULT_RETRY couldn't trigger, bypass */
                        return ret;
 
-               /* VM_FAULT_RETRY cannot return errors */
+               /* VM_FAULT_RETRY or VM_FAULT_COMPLETED cannot return errors */
                if (!*locked) {
                        BUG_ON(ret < 0);
                        BUG_ON(ret >= nr_pages);
@@ -1674,7 +1706,7 @@ static long __get_user_pages_locked(struct mm_struct *mm, unsigned long start,
                        goto finish_or_fault;
 
                if (pages) {
-                       pages[i] = virt_to_page(start);
+                       pages[i] = virt_to_page((void *)start);
                        if (pages[i])
                                get_page(pages[i]);
                }
@@ -1883,7 +1915,7 @@ static long check_and_migrate_movable_pages(unsigned long nr_pages,
        unsigned long isolation_error_count = 0, i;
        struct folio *prev_folio = NULL;
        LIST_HEAD(movable_page_list);
-       bool drain_allow = true;
+       bool drain_allow = true, coherent_pages = false;
        int ret = 0;
 
        for (i = 0; i < nr_pages; i++) {
@@ -1893,14 +1925,43 @@ static long check_and_migrate_movable_pages(unsigned long nr_pages,
                        continue;
                prev_folio = folio;
 
-               if (folio_is_pinnable(folio))
+               /*
+                * Device coherent pages are managed by a driver and should not
+                * be pinned indefinitely as it prevents the driver moving the
+                * page. So when trying to pin with FOLL_LONGTERM instead try
+                * to migrate the page out of device memory.
+                */
+               if (folio_is_device_coherent(folio)) {
+                       /*
+                        * We always want a new GUP lookup with device coherent
+                        * pages.
+                        */
+                       pages[i] = 0;
+                       coherent_pages = true;
+
+                       /*
+                        * Migration will fail if the page is pinned, so convert
+                        * the pin on the source page to a normal reference.
+                        */
+                       if (gup_flags & FOLL_PIN) {
+                               get_page(&folio->page);
+                               unpin_user_page(&folio->page);
+                       }
+
+                       ret = migrate_device_coherent_page(&folio->page);
+                       if (ret)
+                               goto unpin_pages;
+
                        continue;
+               }
 
+               if (folio_is_longterm_pinnable(folio))
+                       continue;
                /*
                 * Try to move out any movable page before pinning the range.
                 */
                if (folio_test_hugetlb(folio)) {
-                       if (!isolate_huge_page(&folio->page,
+                       if (isolate_hugetlb(&folio->page,
                                                &movable_page_list))
                                isolation_error_count++;
                        continue;
@@ -1921,7 +1982,8 @@ static long check_and_migrate_movable_pages(unsigned long nr_pages,
                                    folio_nr_pages(folio));
        }
 
-       if (!list_empty(&movable_page_list) || isolation_error_count)
+       if (!list_empty(&movable_page_list) || isolation_error_count ||
+           coherent_pages)
                goto unpin_pages;
 
        /*
@@ -1931,10 +1993,16 @@ static long check_and_migrate_movable_pages(unsigned long nr_pages,
        return nr_pages;
 
 unpin_pages:
-       if (gup_flags & FOLL_PIN) {
-               unpin_user_pages(pages, nr_pages);
-       } else {
-               for (i = 0; i < nr_pages; i++)
+       /*
+        * pages[i] might be NULL if any device coherent pages were found.
+        */
+       for (i = 0; i < nr_pages; i++) {
+               if (!pages[i])
+                       continue;
+
+               if (gup_flags & FOLL_PIN)
+                       unpin_user_page(pages[i]);
+               else
                        put_page(pages[i]);
        }
 
index d974dec..12b0a91 100644 (file)
@@ -53,7 +53,7 @@ static void verify_dma_pinned(unsigned int cmd, struct page **pages,
                                dump_page(page, "gup_test failure");
                                break;
                        } else if (cmd == PIN_LONGTERM_BENCHMARK &&
-                               WARN(!is_pinnable_page(page),
+                               WARN(!is_longterm_pinnable_page(page),
                                     "pages[%lu] is NOT pinnable but pinned\n",
                                     i)) {
                                dump_page(page, "gup_test failure");
index e32083e..c707d72 100644 (file)
@@ -150,7 +150,7 @@ struct page *__kmap_to_page(void *vaddr)
                return pte_page(pkmap_page_table[i]);
        }
 
-       return virt_to_page(addr);
+       return virt_to_page(vaddr);
 }
 EXPORT_SYMBOL(__kmap_to_page);
 
index 1596508..8a7c1b3 100644 (file)
@@ -70,21 +70,85 @@ static atomic_t huge_zero_refcount;
 struct page *huge_zero_page __read_mostly;
 unsigned long huge_zero_pfn __read_mostly = ~0UL;
 
-bool transparent_hugepage_active(struct vm_area_struct *vma)
+bool hugepage_vma_check(struct vm_area_struct *vma,
+                       unsigned long vm_flags,
+                       bool smaps, bool in_pf)
 {
-       /* The addr is used to check if the vma size fits */
-       unsigned long addr = (vma->vm_end & HPAGE_PMD_MASK) - HPAGE_PMD_SIZE;
+       if (!vma->vm_mm)                /* vdso */
+               return false;
+
+       /*
+        * Explicitly disabled through madvise or prctl, or some
+        * architectures may disable THP for some mappings, for
+        * example, s390 kvm.
+        * */
+       if ((vm_flags & VM_NOHUGEPAGE) ||
+           test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags))
+               return false;
+       /*
+        * If the hardware/firmware marked hugepage support disabled.
+        */
+       if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_NEVER_DAX))
+               return false;
 
-       if (!transhuge_vma_suitable(vma, addr))
+       /* khugepaged doesn't collapse DAX vma, but page fault is fine. */
+       if (vma_is_dax(vma))
+               return in_pf;
+
+       /*
+        * Special VMA and hugetlb VMA.
+        * Must be checked after dax since some dax mappings may have
+        * VM_MIXEDMAP set.
+        */
+       if (vm_flags & VM_NO_KHUGEPAGED)
                return false;
-       if (vma_is_anonymous(vma))
-               return __transparent_hugepage_enabled(vma);
-       if (vma_is_shmem(vma))
+
+       /*
+        * Check alignment for file vma and size for both file and anon vma.
+        *
+        * Skip the check for page fault. Huge fault does the check in fault
+        * handlers. And this check is not suitable for huge PUD fault.
+        */
+       if (!in_pf &&
+           !transhuge_vma_suitable(vma, (vma->vm_end - HPAGE_PMD_SIZE)))
+               return false;
+
+       /*
+        * Enabled via shmem mount options or sysfs settings.
+        * Must be done before hugepage flags check since shmem has its
+        * own flags.
+        */
+       if (!in_pf && shmem_file(vma->vm_file))
                return shmem_huge_enabled(vma);
-       if (transhuge_vma_enabled(vma, vma->vm_flags) && file_thp_enabled(vma))
+
+       if (!hugepage_flags_enabled())
+               return false;
+
+       /* THP settings require madvise. */
+       if (!(vm_flags & VM_HUGEPAGE) && !hugepage_flags_always())
+               return false;
+
+       /* Only regular file is valid */
+       if (!in_pf && file_thp_enabled(vma))
                return true;
 
-       return false;
+       if (!vma_is_anonymous(vma))
+               return false;
+
+       if (vma_is_temporary_stack(vma))
+               return false;
+
+       /*
+        * THPeligible bit of smaps should show 1 for proper VMAs even
+        * though anon_vma is not initialized yet.
+        *
+        * Allow page fault since anon_vma may be not initialized until
+        * the first page fault.
+        */
+       if (!vma->anon_vma)
+               return (smaps || in_pf);
+
+       return true;
 }
 
 static bool get_huge_zero_page(void)
@@ -213,8 +277,8 @@ static ssize_t enabled_store(struct kobject *kobj,
        }
        return ret;
 }
-static struct kobj_attribute enabled_attr =
-       __ATTR(enabled, 0644, enabled_show, enabled_store);
+
+static struct kobj_attribute enabled_attr = __ATTR_RW(enabled);
 
 ssize_t single_hugepage_flag_show(struct kobject *kobj,
                                  struct kobj_attribute *attr, char *buf,
@@ -303,8 +367,7 @@ static ssize_t defrag_store(struct kobject *kobj,
 
        return count;
 }
-static struct kobj_attribute defrag_attr =
-       __ATTR(defrag, 0644, defrag_show, defrag_store);
+static struct kobj_attribute defrag_attr = __ATTR_RW(defrag);
 
 static ssize_t use_zero_page_show(struct kobject *kobj,
                                  struct kobj_attribute *attr, char *buf)
@@ -318,8 +381,7 @@ static ssize_t use_zero_page_store(struct kobject *kobj,
        return single_hugepage_flag_store(kobj, attr, buf, count,
                                 TRANSPARENT_HUGEPAGE_USE_ZERO_PAGE_FLAG);
 }
-static struct kobj_attribute use_zero_page_attr =
-       __ATTR(use_zero_page, 0644, use_zero_page_show, use_zero_page_store);
+static struct kobj_attribute use_zero_page_attr = __ATTR_RW(use_zero_page);
 
 static ssize_t hpage_pmd_size_show(struct kobject *kobj,
                                   struct kobj_attribute *attr, char *buf)
@@ -424,10 +486,10 @@ static int __init hugepage_init(void)
        if (err)
                goto err_slab;
 
-       err = register_shrinker(&huge_zero_page_shrinker);
+       err = register_shrinker(&huge_zero_page_shrinker, "thp-zero");
        if (err)
                goto err_hzp_shrinker;
-       err = register_shrinker(&deferred_split_shrinker);
+       err = register_shrinker(&deferred_split_shrinker, "thp-deferred_split");
        if (err)
                goto err_split_shrinker;
 
@@ -520,7 +582,7 @@ static inline struct deferred_split *get_deferred_split_queue(struct page *page)
 void prep_transhuge_page(struct page *page)
 {
        /*
-        * we use page->mapping and page->indexlru in second tail page
+        * we use page->mapping and page->index in second tail page
         * as list_head: assuming THP order >= 2
         */
 
@@ -727,7 +789,7 @@ vm_fault_t do_huge_pmd_anonymous_page(struct vm_fault *vmf)
                return VM_FAULT_FALLBACK;
        if (unlikely(anon_vma_prepare(vma)))
                return VM_FAULT_OOM;
-       khugepaged_enter(vma, vma->vm_flags);
+       khugepaged_enter_vma(vma, vma->vm_flags);
 
        if (!(vmf->flags & FAULT_FLAG_WRITE) &&
                        !mm_forbids_zeropage(vma->vm_mm) &&
@@ -957,15 +1019,15 @@ EXPORT_SYMBOL_GPL(vmf_insert_pfn_pud_prot);
 #endif /* CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */
 
 static void touch_pmd(struct vm_area_struct *vma, unsigned long addr,
-               pmd_t *pmd, int flags)
+                     pmd_t *pmd, bool write)
 {
        pmd_t _pmd;
 
        _pmd = pmd_mkyoung(*pmd);
-       if (flags & FOLL_WRITE)
+       if (write)
                _pmd = pmd_mkdirty(_pmd);
        if (pmdp_set_access_flags(vma, addr & HPAGE_PMD_MASK,
-                               pmd, _pmd, flags & FOLL_WRITE))
+                                 pmd, _pmd, write))
                update_mmu_cache_pmd(vma, addr, pmd);
 }
 
@@ -998,7 +1060,7 @@ struct page *follow_devmap_pmd(struct vm_area_struct *vma, unsigned long addr,
                return NULL;
 
        if (flags & FOLL_TOUCH)
-               touch_pmd(vma, addr, pmd, flags);
+               touch_pmd(vma, addr, pmd, flags & FOLL_WRITE);
 
        /*
         * device mapped pages can only be returned if the
@@ -1121,15 +1183,15 @@ out:
 
 #ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
 static void touch_pud(struct vm_area_struct *vma, unsigned long addr,
-               pud_t *pud, int flags)
+                     pud_t *pud, bool write)
 {
        pud_t _pud;
 
        _pud = pud_mkyoung(*pud);
-       if (flags & FOLL_WRITE)
+       if (write)
                _pud = pud_mkdirty(_pud);
        if (pudp_set_access_flags(vma, addr & HPAGE_PUD_MASK,
-                               pud, _pud, flags & FOLL_WRITE))
+                                 pud, _pud, write))
                update_mmu_cache_pud(vma, addr, pud);
 }
 
@@ -1156,7 +1218,7 @@ struct page *follow_devmap_pud(struct vm_area_struct *vma, unsigned long addr,
                return NULL;
 
        if (flags & FOLL_TOUCH)
-               touch_pud(vma, addr, pud, flags);
+               touch_pud(vma, addr, pud, flags & FOLL_WRITE);
 
        /*
         * device mapped pages can only be returned if the
@@ -1221,21 +1283,13 @@ out_unlock:
 
 void huge_pud_set_accessed(struct vm_fault *vmf, pud_t orig_pud)
 {
-       pud_t entry;
-       unsigned long haddr;
        bool write = vmf->flags & FAULT_FLAG_WRITE;
 
        vmf->ptl = pud_lock(vmf->vma->vm_mm, vmf->pud);
        if (unlikely(!pud_same(*vmf->pud, orig_pud)))
                goto unlock;
 
-       entry = pud_mkyoung(orig_pud);
-       if (write)
-               entry = pud_mkdirty(entry);
-       haddr = vmf->address & HPAGE_PUD_MASK;
-       if (pudp_set_access_flags(vmf->vma, haddr, vmf->pud, entry, write))
-               update_mmu_cache_pud(vmf->vma, vmf->address, vmf->pud);
-
+       touch_pud(vmf->vma, vmf->address, vmf->pud, write);
 unlock:
        spin_unlock(vmf->ptl);
 }
@@ -1243,21 +1297,13 @@ unlock:
 
 void huge_pmd_set_accessed(struct vm_fault *vmf)
 {
-       pmd_t entry;
-       unsigned long haddr;
        bool write = vmf->flags & FAULT_FLAG_WRITE;
-       pmd_t orig_pmd = vmf->orig_pmd;
 
        vmf->ptl = pmd_lock(vmf->vma->vm_mm, vmf->pmd);
-       if (unlikely(!pmd_same(*vmf->pmd, orig_pmd)))
+       if (unlikely(!pmd_same(*vmf->pmd, vmf->orig_pmd)))
                goto unlock;
 
-       entry = pmd_mkyoung(orig_pmd);
-       if (write)
-               entry = pmd_mkdirty(entry);
-       haddr = vmf->address & HPAGE_PMD_MASK;
-       if (pmdp_set_access_flags(vmf->vma, haddr, vmf->pmd, entry, write))
-               update_mmu_cache_pmd(vmf->vma, vmf->address, vmf->pmd);
+       touch_pmd(vmf->vma, vmf->address, vmf->pmd, write);
 
 unlock:
        spin_unlock(vmf->ptl);
@@ -1393,7 +1439,7 @@ struct page *follow_trans_huge_pmd(struct vm_area_struct *vma,
                return ERR_PTR(-ENOMEM);
 
        if (flags & FOLL_TOUCH)
-               touch_pmd(vma, addr, pmd, flags);
+               touch_pmd(vma, addr, pmd, flags & FOLL_WRITE);
 
        page += (addr & ~HPAGE_PMD_MASK) >> PAGE_SHIFT;
        VM_BUG_ON_PAGE(!PageCompound(page) && !is_zone_device_page(page), page);
@@ -1686,7 +1732,7 @@ bool move_huge_pmd(struct vm_area_struct *vma, unsigned long old_addr,
                pmd = move_soft_dirty_pmd(pmd);
                set_pmd_at(mm, new_addr, new_pmd, pmd);
                if (force_flush)
-                       flush_tlb_range(vma, old_addr, old_addr + PMD_SIZE);
+                       flush_pmd_tlb_range(vma, old_addr, old_addr + PMD_SIZE);
                if (new_ptl != old_ptl)
                        spin_unlock(new_ptl);
                spin_unlock(old_ptl);
@@ -1843,10 +1889,10 @@ spinlock_t *__pmd_trans_huge_lock(pmd_t *pmd, struct vm_area_struct *vma)
 }
 
 /*
- * Returns true if a given pud maps a thp, false otherwise.
+ * Returns page table lock pointer if a given pud maps a thp, NULL otherwise.
  *
- * Note that if it returns true, this routine returns without unlocking page
- * table lock. So callers must unlock it.
+ * Note that if it returns page table lock pointer, this routine returns without
+ * unlocking page table lock. So callers must unlock it.
  */
 spinlock_t *__pud_trans_huge_lock(pud_t *pud, struct vm_area_struct *vma)
 {
@@ -1868,12 +1914,7 @@ int zap_huge_pud(struct mmu_gather *tlb, struct vm_area_struct *vma,
        ptl = __pud_trans_huge_lock(pud, vma);
        if (!ptl)
                return 0;
-       /*
-        * For architectures like ppc64 we look at deposited pgtable
-        * when calling pudp_huge_get_and_clear. So do the
-        * pgtable_trans_huge_withdraw after finishing pudp related
-        * operations.
-        */
+
        pudp_huge_get_and_clear_full(tlb->mm, addr, pud, tlb->fullmm);
        tlb_remove_pud_tlb_entry(tlb, pud, addr);
        if (vma_is_special_huge(vma)) {
@@ -1938,7 +1979,7 @@ static void __split_huge_zero_page_pmd(struct vm_area_struct *vma,
         * replacing a zero pmd write protected page with a zero pte write
         * protected page.
         *
-        * See Documentation/vm/mmu_notifier.rst
+        * See Documentation/mm/mmu_notifier.rst
         */
        pmdp_huge_clear_flush(vma, haddr, pmd);
 
@@ -2195,6 +2236,10 @@ void __split_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
 
        if (pmd_trans_huge(*pmd) || pmd_devmap(*pmd) ||
            is_pmd_migration_entry(*pmd)) {
+               /*
+                * It's safe to call pmd_page when folio is set because it's
+                * guaranteed that pmd is present.
+                */
                if (folio && folio != page_folio(pmd_page(*pmd)))
                        goto out;
                __split_huge_pmd_locked(vma, pmd, range.start, freeze);
@@ -2502,7 +2547,7 @@ static void __split_huge_page(struct page *page, struct list_head *list,
                 * requires taking the lru_lock so we do the put_page
                 * of the tail pages after the split is complete.
                 */
-               put_page(subpage);
+               free_page_and_swap_cache(subpage);
        }
 }
 
@@ -2821,9 +2866,12 @@ static void split_huge_pages_all(void)
        unsigned long total = 0, split = 0;
 
        pr_debug("Split all THPs\n");
-       for_each_populated_zone(zone) {
+       for_each_zone(zone) {
+               if (!managed_zone(zone))
+                       continue;
                max_zone_pfn = zone_end_pfn(zone);
                for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++) {
+                       int nr_pages;
                        if (!pfn_valid(pfn))
                                continue;
 
@@ -2839,8 +2887,10 @@ static void split_huge_pages_all(void)
 
                        total++;
                        lock_page(page);
+                       nr_pages = thp_nr_pages(page);
                        if (!split_huge_page(page))
                                split++;
+                       pfn += nr_pages - 1;
                        unlock_page(page);
 next:
                        put_page(page);
@@ -2898,10 +2948,10 @@ static int split_huge_pages_pid(int pid, unsigned long vaddr_start,
         * table filled with PTE-mapped THPs, each of which is distinct.
         */
        for (addr = vaddr_start; addr < vaddr_end; addr += PAGE_SIZE) {
-               struct vm_area_struct *vma = find_vma(mm, addr);
+               struct vm_area_struct *vma = vma_lookup(mm, addr);
                struct page *page;
 
-               if (!vma || addr < vma->vm_start)
+               if (!vma)
                        break;
 
                /* skip special VMA and hugetlb VMA */
@@ -2913,9 +2963,7 @@ static int split_huge_pages_pid(int pid, unsigned long vaddr_start,
                /* FOLL_DUMP to ignore special (like zero) pages */
                page = follow_page(vma, addr, FOLL_GET | FOLL_DUMP);
 
-               if (IS_ERR(page))
-                       continue;
-               if (!page)
+               if (IS_ERR_OR_NULL(page) || is_zone_device_page(page))
                        continue;
 
                if (!is_transparent_hugepage(page))
@@ -3137,7 +3185,7 @@ void remove_migration_pmd(struct page_vma_mapped_walk *pvmw, struct page *new)
        struct vm_area_struct *vma = pvmw->vma;
        struct mm_struct *mm = vma->vm_mm;
        unsigned long address = pvmw->address;
-       unsigned long mmun_start = address & HPAGE_PMD_MASK;
+       unsigned long haddr = address & HPAGE_PMD_MASK;
        pmd_t pmde;
        swp_entry_t entry;
 
@@ -3146,7 +3194,7 @@ void remove_migration_pmd(struct page_vma_mapped_walk *pvmw, struct page *new)
 
        entry = pmd_to_swp_entry(*pvmw->pmd);
        get_page(new);
-       pmde = pmd_mkold(mk_huge_pmd(new, vma->vm_page_prot));
+       pmde = pmd_mkold(mk_huge_pmd(new, READ_ONCE(vma->vm_page_prot)));
        if (pmd_swp_soft_dirty(*pvmw->pmd))
                pmde = pmd_mksoft_dirty(pmde);
        if (is_writable_migration_entry(entry))
@@ -3160,12 +3208,12 @@ void remove_migration_pmd(struct page_vma_mapped_walk *pvmw, struct page *new)
                if (!is_readable_migration_entry(entry))
                        rmap_flags |= RMAP_EXCLUSIVE;
 
-               page_add_anon_rmap(new, vma, mmun_start, rmap_flags);
+               page_add_anon_rmap(new, vma, haddr, rmap_flags);
        } else {
                page_add_file_rmap(new, vma, true);
        }
        VM_BUG_ON(pmd_write(pmde) && PageAnon(new) && !PageAnonExclusive(new));
-       set_pmd_at(mm, mmun_start, pvmw->pmd, pmde);
+       set_pmd_at(mm, haddr, pvmw->pmd, pmde);
 
        /* No need to invalidate - it was non-present before */
        update_mmu_cache_pmd(vma, address, pvmw->pmd);
index aa39534..f044962 100644 (file)
@@ -66,12 +66,6 @@ static bool hugetlb_cma_page(struct page *page, unsigned int order)
 #endif
 static unsigned long hugetlb_cma_size __initdata;
 
-/*
- * Minimum page order among possible hugepage sizes, set to a proper value
- * at boot time.
- */
-static unsigned int minimum_order __read_mostly = UINT_MAX;
-
 __initdata LIST_HEAD(huge_boot_pages);
 
 /* for command line parsing */
@@ -1135,7 +1129,7 @@ static struct page *dequeue_huge_page_node_exact(struct hstate *h, int nid)
 
        lockdep_assert_held(&hugetlb_lock);
        list_for_each_entry(page, &h->hugepage_freelists[nid], lru) {
-               if (pin && !is_pinnable_page(page))
+               if (pin && !is_longterm_pinnable_page(page))
                        continue;
 
                if (PageHWPoison(page))
@@ -2152,11 +2146,17 @@ int dissolve_free_huge_pages(unsigned long start_pfn, unsigned long end_pfn)
        unsigned long pfn;
        struct page *page;
        int rc = 0;
+       unsigned int order;
+       struct hstate *h;
 
        if (!hugepages_supported())
                return rc;
 
-       for (pfn = start_pfn; pfn < end_pfn; pfn += 1 << minimum_order) {
+       order = huge_page_order(&default_hstate);
+       for_each_hstate(h)
+               order = min(order, huge_page_order(h));
+
+       for (pfn = start_pfn; pfn < end_pfn; pfn += 1 << order) {
                page = pfn_to_page(pfn);
                rc = dissolve_free_huge_page(page);
                if (rc)
@@ -2766,8 +2766,7 @@ retry:
                 * Fail with -EBUSY if not possible.
                 */
                spin_unlock_irq(&hugetlb_lock);
-               if (!isolate_huge_page(old_page, list))
-                       ret = -EBUSY;
+               ret = isolate_hugetlb(old_page, list);
                spin_lock_irq(&hugetlb_lock);
                goto free_new;
        } else if (!HPageFreed(old_page)) {
@@ -2843,7 +2842,7 @@ int isolate_or_dissolve_huge_page(struct page *page, struct list_head *list)
        if (hstate_is_gigantic(h))
                return -ENOMEM;
 
-       if (page_count(head) && isolate_huge_page(head, list))
+       if (page_count(head) && !isolate_hugetlb(head, list))
                ret = 0;
        else if (!page_count(head))
                ret = alloc_and_dissolve_huge_page(h, head, list);
@@ -3149,9 +3148,6 @@ static void __init hugetlb_init_hstates(void)
        struct hstate *h, *h2;
 
        for_each_hstate(h) {
-               if (minimum_order > huge_page_order(h))
-                       minimum_order = huge_page_order(h);
-
                /* oversize hugepages were init'ed in early boot */
                if (!hstate_is_gigantic(h))
                        hugetlb_hstate_alloc_pages(h);
@@ -3176,7 +3172,6 @@ static void __init hugetlb_init_hstates(void)
                                h->demote_order = h2->order;
                }
        }
-       VM_BUG_ON(minimum_order == UINT_MAX);
 }
 
 static void __init report_hugepages(void)
@@ -4482,22 +4477,20 @@ int hugetlb_report_node_meminfo(char *buf, int len, int nid)
                             nid, h->surplus_huge_pages_node[nid]);
 }
 
-void hugetlb_show_meminfo(void)
+void hugetlb_show_meminfo_node(int nid)
 {
        struct hstate *h;
-       int nid;
 
        if (!hugepages_supported())
                return;
 
-       for_each_node_state(nid, N_MEMORY)
-               for_each_hstate(h)
-                       pr_info("Node %d hugepages_total=%u hugepages_free=%u hugepages_surp=%u hugepages_size=%lukB\n",
-                               nid,
-                               h->nr_huge_pages_node[nid],
-                               h->free_huge_pages_node[nid],
-                               h->surplus_huge_pages_node[nid],
-                               huge_page_size(h) / SZ_1K);
+       for_each_hstate(h)
+               printk("Node %d hugepages_total=%u hugepages_free=%u hugepages_surp=%u hugepages_size=%lukB\n",
+                       nid,
+                       h->nr_huge_pages_node[nid],
+                       h->free_huge_pages_node[nid],
+                       h->surplus_huge_pages_node[nid],
+                       huge_page_size(h) / SZ_1K);
 }
 
 void hugetlb_report_usage(struct seq_file *m, struct mm_struct *mm)
@@ -4732,6 +4725,7 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
        unsigned long npages = pages_per_huge_page(h);
        struct address_space *mapping = src_vma->vm_file->f_mapping;
        struct mmu_notifier_range range;
+       unsigned long last_addr_mask;
        int ret = 0;
 
        if (cow) {
@@ -4751,11 +4745,14 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
                i_mmap_lock_read(mapping);
        }
 
+       last_addr_mask = hugetlb_mask_last_page(h);
        for (addr = src_vma->vm_start; addr < src_vma->vm_end; addr += sz) {
                spinlock_t *src_ptl, *dst_ptl;
                src_pte = huge_pte_offset(src, addr, sz);
-               if (!src_pte)
+               if (!src_pte) {
+                       addr |= last_addr_mask;
                        continue;
+               }
                dst_pte = huge_pte_alloc(dst, dst_vma, addr, sz);
                if (!dst_pte) {
                        ret = -ENOMEM;
@@ -4772,8 +4769,10 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
                 * after taking the lock below.
                 */
                dst_entry = huge_ptep_get(dst_pte);
-               if ((dst_pte == src_pte) || !huge_pte_none(dst_entry))
+               if ((dst_pte == src_pte) || !huge_pte_none(dst_entry)) {
+                       addr |= last_addr_mask;
                        continue;
+               }
 
                dst_ptl = huge_pte_lock(h, dst, dst_pte);
                src_ptl = huge_pte_lockptr(h, src, src_pte);
@@ -4808,12 +4807,11 @@ again:
                                entry = swp_entry_to_pte(swp_entry);
                                if (userfaultfd_wp(src_vma) && uffd_wp)
                                        entry = huge_pte_mkuffd_wp(entry);
-                               set_huge_swap_pte_at(src, addr, src_pte,
-                                                    entry, sz);
+                               set_huge_pte_at(src, addr, src_pte, entry);
                        }
                        if (!userfaultfd_wp(dst_vma) && uffd_wp)
                                entry = huge_pte_clear_uffd_wp(entry);
-                       set_huge_swap_pte_at(dst, addr, dst_pte, entry, sz);
+                       set_huge_pte_at(dst, addr, dst_pte, entry);
                } else if (unlikely(is_pte_marker(entry))) {
                        /*
                         * We copy the pte marker only if the dst vma has
@@ -4880,7 +4878,7 @@ again:
                                 * table protection not changing it to point
                                 * to a new page.
                                 *
-                                * See Documentation/vm/mmu_notifier.rst
+                                * See Documentation/mm/mmu_notifier.rst
                                 */
                                huge_ptep_set_wrprotect(src, addr, src_pte);
                                entry = huge_pte_wrprotect(entry);
@@ -4939,7 +4937,7 @@ int move_hugetlb_page_tables(struct vm_area_struct *vma,
        unsigned long sz = huge_page_size(h);
        struct mm_struct *mm = vma->vm_mm;
        unsigned long old_end = old_addr + len;
-       unsigned long old_addr_copy;
+       unsigned long last_addr_mask;
        pte_t *src_pte, *dst_pte;
        struct mmu_notifier_range range;
        bool shared_pmd = false;
@@ -4954,23 +4952,23 @@ int move_hugetlb_page_tables(struct vm_area_struct *vma,
        flush_cache_range(vma, range.start, range.end);
 
        mmu_notifier_invalidate_range_start(&range);
+       last_addr_mask = hugetlb_mask_last_page(h);
        /* Prevent race with file truncation */
        i_mmap_lock_write(mapping);
        for (; old_addr < old_end; old_addr += sz, new_addr += sz) {
                src_pte = huge_pte_offset(mm, old_addr, sz);
-               if (!src_pte)
+               if (!src_pte) {
+                       old_addr |= last_addr_mask;
+                       new_addr |= last_addr_mask;
                        continue;
+               }
                if (huge_pte_none(huge_ptep_get(src_pte)))
                        continue;
 
-               /* old_addr arg to huge_pmd_unshare() is a pointer and so the
-                * arg may be modified. Pass a copy instead to preserve the
-                * value in old_addr.
-                */
-               old_addr_copy = old_addr;
-
-               if (huge_pmd_unshare(mm, vma, &old_addr_copy, src_pte)) {
+               if (huge_pmd_unshare(mm, vma, old_addr, src_pte)) {
                        shared_pmd = true;
+                       old_addr |= last_addr_mask;
+                       new_addr |= last_addr_mask;
                        continue;
                }
 
@@ -5004,6 +5002,7 @@ static void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct
        struct hstate *h = hstate_vma(vma);
        unsigned long sz = huge_page_size(h);
        struct mmu_notifier_range range;
+       unsigned long last_addr_mask;
        bool force_flush = false;
 
        WARN_ON(!is_vm_hugetlb_page(vma));
@@ -5024,17 +5023,21 @@ static void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct
                                end);
        adjust_range_if_pmd_sharing_possible(vma, &range.start, &range.end);
        mmu_notifier_invalidate_range_start(&range);
+       last_addr_mask = hugetlb_mask_last_page(h);
        address = start;
        for (; address < end; address += sz) {
                ptep = huge_pte_offset(mm, address, sz);
-               if (!ptep)
+               if (!ptep) {
+                       address |= last_addr_mask;
                        continue;
+               }
 
                ptl = huge_pte_lock(h, mm, ptep);
-               if (huge_pmd_unshare(mm, vma, &address, ptep)) {
+               if (huge_pmd_unshare(mm, vma, address, ptep)) {
                        spin_unlock(ptl);
                        tlb_flush_pmd_range(tlb, address & PUD_MASK, PUD_SIZE);
                        force_flush = true;
+                       address |= last_addr_mask;
                        continue;
                }
 
@@ -5714,7 +5717,7 @@ vm_fault_t hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,
                 */
                entry = huge_ptep_get(ptep);
                if (unlikely(is_hugetlb_entry_migration(entry))) {
-                       migration_entry_wait_huge(vma, mm, ptep);
+                       migration_entry_wait_huge(vma, ptep);
                        return 0;
                } else if (unlikely(is_hugetlb_entry_hwpoisoned(entry)))
                        return VM_FAULT_HWPOISON_LARGE |
@@ -6052,8 +6055,6 @@ int hugetlb_mcopy_atomic_pte(struct mm_struct *dst_mm,
 
        set_huge_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte);
 
-       (void)huge_ptep_set_access_flags(dst_vma, dst_addr, dst_pte, _dst_pte,
-                                       dst_vma->vm_flags & VM_WRITE);
        hugetlb_count_add(pages_per_huge_page(h), dst_mm);
 
        /* No need to invalidate - it was non-present before */
@@ -6305,6 +6306,7 @@ unsigned long hugetlb_change_protection(struct vm_area_struct *vma,
        unsigned long pages = 0, psize = huge_page_size(h);
        bool shared_pmd = false;
        struct mmu_notifier_range range;
+       unsigned long last_addr_mask;
        bool uffd_wp = cp_flags & MM_CP_UFFD_WP;
        bool uffd_wp_resolve = cp_flags & MM_CP_UFFD_WP_RESOLVE;
 
@@ -6321,14 +6323,17 @@ unsigned long hugetlb_change_protection(struct vm_area_struct *vma,
        flush_cache_range(vma, range.start, range.end);
 
        mmu_notifier_invalidate_range_start(&range);
+       last_addr_mask = hugetlb_mask_last_page(h);
        i_mmap_lock_write(vma->vm_file->f_mapping);
        for (; address < end; address += psize) {
                spinlock_t *ptl;
                ptep = huge_pte_offset(mm, address, psize);
-               if (!ptep)
+               if (!ptep) {
+                       address |= last_addr_mask;
                        continue;
+               }
                ptl = huge_pte_lock(h, mm, ptep);
-               if (huge_pmd_unshare(mm, vma, &address, ptep)) {
+               if (huge_pmd_unshare(mm, vma, address, ptep)) {
                        /*
                         * When uffd-wp is enabled on the vma, unshare
                         * shouldn't happen at all.  Warn about it if it
@@ -6338,6 +6343,7 @@ unsigned long hugetlb_change_protection(struct vm_area_struct *vma,
                        pages++;
                        spin_unlock(ptl);
                        shared_pmd = true;
+                       address |= last_addr_mask;
                        continue;
                }
                pte = huge_ptep_get(ptep);
@@ -6363,8 +6369,7 @@ unsigned long hugetlb_change_protection(struct vm_area_struct *vma,
                                        newpte = pte_swp_mkuffd_wp(newpte);
                                else if (uffd_wp_resolve)
                                        newpte = pte_swp_clear_uffd_wp(newpte);
-                               set_huge_swap_pte_at(mm, address, ptep,
-                                                    newpte, psize);
+                               set_huge_pte_at(mm, address, ptep, newpte);
                                pages++;
                        }
                        spin_unlock(ptl);
@@ -6415,7 +6420,7 @@ unsigned long hugetlb_change_protection(struct vm_area_struct *vma,
         * No need to call mmu_notifier_invalidate_range() we are downgrading
         * page table protection not changing it to point to a new page.
         *
-        * See Documentation/vm/mmu_notifier.rst
+        * See Documentation/mm/mmu_notifier.rst
         */
        i_mmap_unlock_write(vma->vm_file->f_mapping);
        mmu_notifier_invalidate_range_end(&range);
@@ -6761,11 +6766,11 @@ out:
  *         0 the underlying pte page is not shared, or it is the last user
  */
 int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma,
-                                       unsigned long *addr, pte_t *ptep)
+                                       unsigned long addr, pte_t *ptep)
 {
-       pgd_t *pgd = pgd_offset(mm, *addr);
-       p4d_t *p4d = p4d_offset(pgd, *addr);
-       pud_t *pud = pud_offset(p4d, *addr);
+       pgd_t *pgd = pgd_offset(mm, addr);
+       p4d_t *p4d = p4d_offset(pgd, addr);
+       pud_t *pud = pud_offset(p4d, addr);
 
        i_mmap_assert_write_locked(vma->vm_file->f_mapping);
        BUG_ON(page_count(virt_to_page(ptep)) == 0);
@@ -6775,14 +6780,6 @@ int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma,
        pud_clear(pud);
        put_page(virt_to_page(ptep));
        mm_dec_nr_pmds(mm);
-       /*
-        * This update of passed address optimizes loops sequentially
-        * processing addresses in increments of huge page size (PMD_SIZE
-        * in this case).  By clearing the pud, a PUD_SIZE area is unmapped.
-        * Update address to the 'last page' in the cleared area so that
-        * calling loop can move to first page past this area.
-        */
-       *addr |= PUD_SIZE - PMD_SIZE;
        return 1;
 }
 
@@ -6794,7 +6791,7 @@ pte_t *huge_pmd_share(struct mm_struct *mm, struct vm_area_struct *vma,
 }
 
 int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma,
-                               unsigned long *addr, pte_t *ptep)
+                               unsigned long addr, pte_t *ptep)
 {
        return 0;
 }
@@ -6877,6 +6874,37 @@ pte_t *huge_pte_offset(struct mm_struct *mm,
        return (pte_t *)pmd;
 }
 
+/*
+ * Return a mask that can be used to update an address to the last huge
+ * page in a page table page mapping size.  Used to skip non-present
+ * page table entries when linearly scanning address ranges.  Architectures
+ * with unique huge page to page table relationships can define their own
+ * version of this routine.
+ */
+unsigned long hugetlb_mask_last_page(struct hstate *h)
+{
+       unsigned long hp_size = huge_page_size(h);
+
+       if (hp_size == PUD_SIZE)
+               return P4D_SIZE - PUD_SIZE;
+       else if (hp_size == PMD_SIZE)
+               return PUD_SIZE - PMD_SIZE;
+       else
+               return 0UL;
+}
+
+#else
+
+/* See description above.  Architectures can provide their own version. */
+__weak unsigned long hugetlb_mask_last_page(struct hstate *h)
+{
+#ifdef CONFIG_ARCH_WANT_HUGE_PMD_SHARE
+       if (huge_page_size(h) == PMD_SIZE)
+               return PUD_SIZE - PMD_SIZE;
+#endif
+       return 0UL;
+}
+
 #endif /* CONFIG_ARCH_WANT_GENERAL_HUGETLB */
 
 /*
@@ -6940,7 +6968,7 @@ retry:
        } else {
                if (is_hugetlb_entry_migration(pte)) {
                        spin_unlock(ptl);
-                       __migration_entry_wait(mm, (pte_t *)pmd, ptl);
+                       __migration_entry_wait_huge((pte_t *)pmd, ptl);
                        goto retry;
                }
                /*
@@ -6972,15 +7000,15 @@ follow_huge_pgd(struct mm_struct *mm, unsigned long address, pgd_t *pgd, int fla
        return pte_page(*(pte_t *)pgd) + ((address & ~PGDIR_MASK) >> PAGE_SHIFT);
 }
 
-bool isolate_huge_page(struct page *page, struct list_head *list)
+int isolate_hugetlb(struct page *page, struct list_head *list)
 {
-       bool ret = true;
+       int ret = 0;
 
        spin_lock_irq(&hugetlb_lock);
        if (!PageHeadHuge(page) ||
            !HPageMigratable(page) ||
            !get_page_unless_zero(page)) {
-               ret = false;
+               ret = -EBUSY;
                goto unlock;
        }
        ClearHPageMigratable(page);
@@ -7100,21 +7128,18 @@ void hugetlb_unshare_all_pmds(struct vm_area_struct *vma)
        mmu_notifier_invalidate_range_start(&range);
        i_mmap_lock_write(vma->vm_file->f_mapping);
        for (address = start; address < end; address += PUD_SIZE) {
-               unsigned long tmp = address;
-
                ptep = huge_pte_offset(mm, address, sz);
                if (!ptep)
                        continue;
                ptl = huge_pte_lock(h, mm, ptep);
-               /* We don't want 'address' to be changed */
-               huge_pmd_unshare(mm, vma, &tmp, ptep);
+               huge_pmd_unshare(mm, vma, address, ptep);
                spin_unlock(ptl);
        }
        flush_hugetlb_tlb_range(vma, start, end);
        i_mmap_unlock_write(vma->vm_file->f_mapping);
        /*
         * No need to call mmu_notifier_invalidate_range(), see
-        * Documentation/vm/mmu_notifier.rst.
+        * Documentation/mm/mmu_notifier.rst.
         */
        mmu_notifier_invalidate_range_end(&range);
 }
index f994284..c86691c 100644 (file)
@@ -772,6 +772,7 @@ static void __init __hugetlb_cgroup_file_dfl_init(int idx)
        /* Add the numa stat file */
        cft = &h->cgroup_files_dfl[6];
        snprintf(cft->name, MAX_CFTYPE_NAME, "%s.numa_stat", buf);
+       cft->private = MEMFILE_PRIVATE(idx, 0);
        cft->seq_show = hugetlb_cgroup_read_numa_stat;
        cft->flags = CFTYPE_NOT_ON_ROOT;
 
index 1089ea8..1362feb 100644 (file)
@@ -6,11 +6,11 @@
  *
  *     Author: Muchun Song <songmuchun@bytedance.com>
  *
- * See Documentation/vm/vmemmap_dedup.rst
+ * See Documentation/mm/vmemmap_dedup.rst
  */
 #define pr_fmt(fmt)    "HugeTLB: " fmt
 
-#include <linux/memory_hotplug.h>
+#include <linux/memory.h>
 #include "hugetlb_vmemmap.h"
 
 /*
@@ -97,18 +97,68 @@ int hugetlb_vmemmap_alloc(struct hstate *h, struct page *head)
        return ret;
 }
 
+static unsigned int vmemmap_optimizable_pages(struct hstate *h,
+                                             struct page *head)
+{
+       if (READ_ONCE(vmemmap_optimize_mode) == VMEMMAP_OPTIMIZE_OFF)
+               return 0;
+
+       if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG)) {
+               pmd_t *pmdp, pmd;
+               struct page *vmemmap_page;
+               unsigned long vaddr = (unsigned long)head;
+
+               /*
+                * Only the vmemmap page's vmemmap page can be self-hosted.
+                * Walking the page tables to find the backing page of the
+                * vmemmap page.
+                */
+               pmdp = pmd_off_k(vaddr);
+               /*
+                * The READ_ONCE() is used to stabilize *pmdp in a register or
+                * on the stack so that it will stop changing under the code.
+                * The only concurrent operation where it can be changed is
+                * split_vmemmap_huge_pmd() (*pmdp will be stable after this
+                * operation).
+                */
+               pmd = READ_ONCE(*pmdp);
+               if (pmd_leaf(pmd))
+                       vmemmap_page = pmd_page(pmd) + pte_index(vaddr);
+               else
+                       vmemmap_page = pte_page(*pte_offset_kernel(pmdp, vaddr));
+               /*
+                * Due to HugeTLB alignment requirements and the vmemmap pages
+                * being at the start of the hotplugged memory region in
+                * memory_hotplug.memmap_on_memory case. Checking any vmemmap
+                * page's vmemmap page if it is marked as VmemmapSelfHosted is
+                * sufficient.
+                *
+                * [                  hotplugged memory                  ]
+                * [        section        ][...][        section        ]
+                * [ vmemmap ][              usable memory               ]
+                *   ^   |     |                                        |
+                *   +---+     |                                        |
+                *     ^       |                                        |
+                *     +-------+                                        |
+                *          ^                                           |
+                *          +-------------------------------------------+
+                */
+               if (PageVmemmapSelfHosted(vmemmap_page))
+                       return 0;
+       }
+
+       return hugetlb_optimize_vmemmap_pages(h);
+}
+
 void hugetlb_vmemmap_free(struct hstate *h, struct page *head)
 {
        unsigned long vmemmap_addr = (unsigned long)head;
        unsigned long vmemmap_end, vmemmap_reuse, vmemmap_pages;
 
-       vmemmap_pages = hugetlb_optimize_vmemmap_pages(h);
+       vmemmap_pages = vmemmap_optimizable_pages(h, head);
        if (!vmemmap_pages)
                return;
 
-       if (READ_ONCE(vmemmap_optimize_mode) == VMEMMAP_OPTIMIZE_OFF)
-               return;
-
        static_branch_inc(&hugetlb_optimize_vmemmap_key);
 
        vmemmap_addr    += RESERVE_VMEMMAP_SIZE;
@@ -199,10 +249,10 @@ static struct ctl_table hugetlb_vmemmap_sysctls[] = {
 static __init int hugetlb_vmemmap_sysctls_init(void)
 {
        /*
-        * If "memory_hotplug.memmap_on_memory" is enabled or "struct page"
-        * crosses page boundaries, the vmemmap pages cannot be optimized.
+        * If "struct page" crosses page boundaries, the vmemmap pages cannot
+        * be optimized.
         */
-       if (!mhp_memmap_on_memory() && is_power_of_2(sizeof(struct page)))
+       if (is_power_of_2(sizeof(struct page)))
                register_sysctl_init("vm", hugetlb_vmemmap_sysctls);
 
        return 0;
index ddd2d6a..7854098 100644 (file)
@@ -853,6 +853,7 @@ int numa_migrate_prep(struct page *page, struct vm_area_struct *vma,
                      unsigned long addr, int page_nid, int *flags);
 
 void free_zone_device_page(struct page *page);
+int migrate_device_coherent_page(struct page *page);
 
 /*
  * mm/gup.c
@@ -863,4 +864,22 @@ DECLARE_PER_CPU(struct per_cpu_nodestat, boot_nodestats);
 
 extern bool mirrored_kernelcore;
 
+static inline bool vma_soft_dirty_enabled(struct vm_area_struct *vma)
+{
+       /*
+        * NOTE: we must check this before VM_SOFTDIRTY on soft-dirty
+        * enablements, because when without soft-dirty being compiled in,
+        * VM_SOFTDIRTY is defined as 0x0, then !(vm_flags & VM_SOFTDIRTY)
+        * will be constantly true.
+        */
+       if (!IS_ENABLED(CONFIG_MEM_SOFT_DIRTY))
+               return false;
+
+       /*
+        * Soft-dirty is kind of special: its tracking is enabled when the
+        * vma flags not set.
+        */
+       return !(vma->vm_flags & VM_SOFTDIRTY);
+}
+
 #endif /* __MM_INTERNAL_H */
index 78be2be..69f5838 100644 (file)
@@ -344,7 +344,7 @@ static inline bool ____kasan_slab_free(struct kmem_cache *cache, void *object,
 
        if (unlikely(nearest_obj(cache, virt_to_slab(object), object) !=
            object)) {
-               kasan_report_invalid_free(tagged_object, ip);
+               kasan_report_invalid_free(tagged_object, ip, KASAN_REPORT_INVALID_FREE);
                return true;
        }
 
@@ -353,7 +353,7 @@ static inline bool ____kasan_slab_free(struct kmem_cache *cache, void *object,
                return false;
 
        if (!kasan_byte_accessible(tagged_object)) {
-               kasan_report_invalid_free(tagged_object, ip);
+               kasan_report_invalid_free(tagged_object, ip, KASAN_REPORT_DOUBLE_FREE);
                return true;
        }
 
@@ -378,12 +378,12 @@ bool __kasan_slab_free(struct kmem_cache *cache, void *object,
 static inline bool ____kasan_kfree_large(void *ptr, unsigned long ip)
 {
        if (ptr != page_address(virt_to_head_page(ptr))) {
-               kasan_report_invalid_free(ptr, ip);
+               kasan_report_invalid_free(ptr, ip, KASAN_REPORT_INVALID_FREE);
                return true;
        }
 
        if (!kasan_byte_accessible(ptr)) {
-               kasan_report_invalid_free(ptr, ip);
+               kasan_report_invalid_free(ptr, ip, KASAN_REPORT_DOUBLE_FREE);
                return true;
        }
 
index 9e1b654..9ad8eff 100644 (file)
@@ -257,27 +257,37 @@ static void unpoison_vmalloc_pages(const void *addr, u8 tag)
        }
 }
 
+static void init_vmalloc_pages(const void *start, unsigned long size)
+{
+       const void *addr;
+
+       for (addr = start; addr < start + size; addr += PAGE_SIZE) {
+               struct page *page = virt_to_page(addr);
+
+               clear_highpage_kasan_tagged(page);
+       }
+}
+
 void *__kasan_unpoison_vmalloc(const void *start, unsigned long size,
                                kasan_vmalloc_flags_t flags)
 {
        u8 tag;
        unsigned long redzone_start, redzone_size;
 
-       if (!kasan_vmalloc_enabled())
-               return (void *)start;
-
-       if (!is_vmalloc_or_module_addr(start))
+       if (!kasan_vmalloc_enabled() || !is_vmalloc_or_module_addr(start)) {
+               if (flags & KASAN_VMALLOC_INIT)
+                       init_vmalloc_pages(start, size);
                return (void *)start;
+       }
 
        /*
-        * Skip unpoisoning and assigning a pointer tag for non-VM_ALLOC
-        * mappings as:
+        * Don't tag non-VM_ALLOC mappings, as:
         *
         * 1. Unlike the software KASAN modes, hardware tag-based KASAN only
         *    supports tagging physical memory. Therefore, it can only tag a
         *    single mapping of normal physical pages.
         * 2. Hardware tag-based KASAN can only tag memory mapped with special
-        *    mapping protection bits, see arch_vmalloc_pgprot_modify().
+        *    mapping protection bits, see arch_vmap_pgprot_tagged().
         *    As non-VM_ALLOC mappings can be mapped outside of vmalloc code,
         *    providing these bits would require tracking all non-VM_ALLOC
         *    mappers.
@@ -289,15 +299,19 @@ void *__kasan_unpoison_vmalloc(const void *start, unsigned long size,
         *
         * For non-VM_ALLOC allocations, page_alloc memory is tagged as usual.
         */
-       if (!(flags & KASAN_VMALLOC_VM_ALLOC))
+       if (!(flags & KASAN_VMALLOC_VM_ALLOC)) {
+               WARN_ON(flags & KASAN_VMALLOC_INIT);
                return (void *)start;
+       }
 
        /*
         * Don't tag executable memory.
         * The kernel doesn't tolerate having the PC register tagged.
         */
-       if (!(flags & KASAN_VMALLOC_PROT_NORMAL))
+       if (!(flags & KASAN_VMALLOC_PROT_NORMAL)) {
+               WARN_ON(flags & KASAN_VMALLOC_INIT);
                return (void *)start;
+       }
 
        tag = kasan_random_tag();
        start = set_tag(start, tag);
index 610d60d..01c03e4 100644 (file)
@@ -125,6 +125,7 @@ static inline bool kasan_sync_fault_possible(void)
 enum kasan_report_type {
        KASAN_REPORT_ACCESS,
        KASAN_REPORT_INVALID_FREE,
+       KASAN_REPORT_DOUBLE_FREE,
 };
 
 struct kasan_report_info {
@@ -277,7 +278,7 @@ static inline void kasan_print_address_stack_frame(const void *addr) { }
 
 bool kasan_report(unsigned long addr, size_t size,
                bool is_write, unsigned long ip);
-void kasan_report_invalid_free(void *object, unsigned long ip);
+void kasan_report_invalid_free(void *object, unsigned long ip, enum kasan_report_type type);
 
 struct page *kasan_addr_to_page(const void *addr);
 struct slab *kasan_addr_to_slab(const void *addr);
index b341a19..fe3f606 100644 (file)
@@ -176,8 +176,12 @@ static void end_report(unsigned long *flags, void *addr)
 static void print_error_description(struct kasan_report_info *info)
 {
        if (info->type == KASAN_REPORT_INVALID_FREE) {
-               pr_err("BUG: KASAN: double-free or invalid-free in %pS\n",
-                      (void *)info->ip);
+               pr_err("BUG: KASAN: invalid-free in %pS\n", (void *)info->ip);
+               return;
+       }
+
+       if (info->type == KASAN_REPORT_DOUBLE_FREE) {
+               pr_err("BUG: KASAN: double-free in %pS\n", (void *)info->ip);
                return;
        }
 
@@ -433,7 +437,7 @@ static void print_report(struct kasan_report_info *info)
        }
 }
 
-void kasan_report_invalid_free(void *ptr, unsigned long ip)
+void kasan_report_invalid_free(void *ptr, unsigned long ip, enum kasan_report_type type)
 {
        unsigned long flags;
        struct kasan_report_info info;
@@ -448,7 +452,7 @@ void kasan_report_invalid_free(void *ptr, unsigned long ip)
 
        start_report(&flags, true);
 
-       info.type = KASAN_REPORT_INVALID_FREE;
+       info.type = type;
        info.access_addr = ptr;
        info.first_bad_addr = kasan_reset_tag(ptr);
        info.access_size = 0;
index 6aff49f..c252081 100644 (file)
@@ -546,7 +546,7 @@ static unsigned long kfence_init_pool(void)
        if (!arch_kfence_init_pool())
                return addr;
 
-       pages = virt_to_page(addr);
+       pages = virt_to_page(__kfence_pool);
 
        /*
         * Set up object pages: they must have PG_slab set, to avoid freeing
@@ -660,7 +660,7 @@ static bool kfence_init_pool_late(void)
        /* Same as above. */
        free_size = KFENCE_POOL_SIZE - (addr - (unsigned long)__kfence_pool);
 #ifdef CONFIG_CONTIG_ALLOC
-       free_contig_range(page_to_pfn(virt_to_page(addr)), free_size / PAGE_SIZE);
+       free_contig_range(page_to_pfn(virt_to_page((void *)addr)), free_size / PAGE_SIZE);
 #else
        free_pages_exact((void *)addr, free_size);
 #endif
index 16be62d..01f7178 100644 (file)
@@ -147,8 +147,7 @@ static ssize_t scan_sleep_millisecs_store(struct kobject *kobj,
        return count;
 }
 static struct kobj_attribute scan_sleep_millisecs_attr =
-       __ATTR(scan_sleep_millisecs, 0644, scan_sleep_millisecs_show,
-              scan_sleep_millisecs_store);
+       __ATTR_RW(scan_sleep_millisecs);
 
 static ssize_t alloc_sleep_millisecs_show(struct kobject *kobj,
                                          struct kobj_attribute *attr,
@@ -175,8 +174,7 @@ static ssize_t alloc_sleep_millisecs_store(struct kobject *kobj,
        return count;
 }
 static struct kobj_attribute alloc_sleep_millisecs_attr =
-       __ATTR(alloc_sleep_millisecs, 0644, alloc_sleep_millisecs_show,
-              alloc_sleep_millisecs_store);
+       __ATTR_RW(alloc_sleep_millisecs);
 
 static ssize_t pages_to_scan_show(struct kobject *kobj,
                                  struct kobj_attribute *attr,
@@ -200,8 +198,7 @@ static ssize_t pages_to_scan_store(struct kobject *kobj,
        return count;
 }
 static struct kobj_attribute pages_to_scan_attr =
-       __ATTR(pages_to_scan, 0644, pages_to_scan_show,
-              pages_to_scan_store);
+       __ATTR_RW(pages_to_scan);
 
 static ssize_t pages_collapsed_show(struct kobject *kobj,
                                    struct kobj_attribute *attr,
@@ -221,22 +218,21 @@ static ssize_t full_scans_show(struct kobject *kobj,
 static struct kobj_attribute full_scans_attr =
        __ATTR_RO(full_scans);
 
-static ssize_t khugepaged_defrag_show(struct kobject *kobj,
-                                     struct kobj_attribute *attr, char *buf)
+static ssize_t defrag_show(struct kobject *kobj,
+                          struct kobj_attribute *attr, char *buf)
 {
        return single_hugepage_flag_show(kobj, attr, buf,
                                         TRANSPARENT_HUGEPAGE_DEFRAG_KHUGEPAGED_FLAG);
 }
-static ssize_t khugepaged_defrag_store(struct kobject *kobj,
-                                      struct kobj_attribute *attr,
-                                      const char *buf, size_t count)
+static ssize_t defrag_store(struct kobject *kobj,
+                           struct kobj_attribute *attr,
+                           const char *buf, size_t count)
 {
        return single_hugepage_flag_store(kobj, attr, buf, count,
                                 TRANSPARENT_HUGEPAGE_DEFRAG_KHUGEPAGED_FLAG);
 }
 static struct kobj_attribute khugepaged_defrag_attr =
-       __ATTR(defrag, 0644, khugepaged_defrag_show,
-              khugepaged_defrag_store);
+       __ATTR_RW(defrag);
 
 /*
  * max_ptes_none controls if khugepaged should collapse hugepages over
@@ -246,21 +242,21 @@ static struct kobj_attribute khugepaged_defrag_attr =
  * runs. Increasing max_ptes_none will instead potentially reduce the
  * free memory in the system during the khugepaged scan.
  */
-static ssize_t khugepaged_max_ptes_none_show(struct kobject *kobj,
-                                            struct kobj_attribute *attr,
-                                            char *buf)
+static ssize_t max_ptes_none_show(struct kobject *kobj,
+                                 struct kobj_attribute *attr,
+                                 char *buf)
 {
        return sysfs_emit(buf, "%u\n", khugepaged_max_ptes_none);
 }
-static ssize_t khugepaged_max_ptes_none_store(struct kobject *kobj,
-                                             struct kobj_attribute *attr,
-                                             const char *buf, size_t count)
+static ssize_t max_ptes_none_store(struct kobject *kobj,
+                                  struct kobj_attribute *attr,
+                                  const char *buf, size_t count)
 {
        int err;
        unsigned long max_ptes_none;
 
        err = kstrtoul(buf, 10, &max_ptes_none);
-       if (err || max_ptes_none > HPAGE_PMD_NR-1)
+       if (err || max_ptes_none > HPAGE_PMD_NR - 1)
                return -EINVAL;
 
        khugepaged_max_ptes_none = max_ptes_none;
@@ -268,25 +264,24 @@ static ssize_t khugepaged_max_ptes_none_store(struct kobject *kobj,
        return count;
 }
 static struct kobj_attribute khugepaged_max_ptes_none_attr =
-       __ATTR(max_ptes_none, 0644, khugepaged_max_ptes_none_show,
-              khugepaged_max_ptes_none_store);
+       __ATTR_RW(max_ptes_none);
 
-static ssize_t khugepaged_max_ptes_swap_show(struct kobject *kobj,
-                                            struct kobj_attribute *attr,
-                                            char *buf)
+static ssize_t max_ptes_swap_show(struct kobject *kobj,
+                                 struct kobj_attribute *attr,
+                                 char *buf)
 {
        return sysfs_emit(buf, "%u\n", khugepaged_max_ptes_swap);
 }
 
-static ssize_t khugepaged_max_ptes_swap_store(struct kobject *kobj,
-                                             struct kobj_attribute *attr,
-                                             const char *buf, size_t count)
+static ssize_t max_ptes_swap_store(struct kobject *kobj,
+                                  struct kobj_attribute *attr,
+                                  const char *buf, size_t count)
 {
        int err;
        unsigned long max_ptes_swap;
 
        err  = kstrtoul(buf, 10, &max_ptes_swap);
-       if (err || max_ptes_swap > HPAGE_PMD_NR-1)
+       if (err || max_ptes_swap > HPAGE_PMD_NR - 1)
                return -EINVAL;
 
        khugepaged_max_ptes_swap = max_ptes_swap;
@@ -295,25 +290,24 @@ static ssize_t khugepaged_max_ptes_swap_store(struct kobject *kobj,
 }
 
 static struct kobj_attribute khugepaged_max_ptes_swap_attr =
-       __ATTR(max_ptes_swap, 0644, khugepaged_max_ptes_swap_show,
-              khugepaged_max_ptes_swap_store);
+       __ATTR_RW(max_ptes_swap);
 
-static ssize_t khugepaged_max_ptes_shared_show(struct kobject *kobj,
-                                              struct kobj_attribute *attr,
-                                              char *buf)
+static ssize_t max_ptes_shared_show(struct kobject *kobj,
+                                   struct kobj_attribute *attr,
+                                   char *buf)
 {
        return sysfs_emit(buf, "%u\n", khugepaged_max_ptes_shared);
 }
 
-static ssize_t khugepaged_max_ptes_shared_store(struct kobject *kobj,
-                                             struct kobj_attribute *attr,
-                                             const char *buf, size_t count)
+static ssize_t max_ptes_shared_store(struct kobject *kobj,
+                                    struct kobj_attribute *attr,
+                                    const char *buf, size_t count)
 {
        int err;
        unsigned long max_ptes_shared;
 
        err  = kstrtoul(buf, 10, &max_ptes_shared);
-       if (err || max_ptes_shared > HPAGE_PMD_NR-1)
+       if (err || max_ptes_shared > HPAGE_PMD_NR - 1)
                return -EINVAL;
 
        khugepaged_max_ptes_shared = max_ptes_shared;
@@ -322,8 +316,7 @@ static ssize_t khugepaged_max_ptes_shared_store(struct kobject *kobj,
 }
 
 static struct kobj_attribute khugepaged_max_ptes_shared_attr =
-       __ATTR(max_ptes_shared, 0644, khugepaged_max_ptes_shared_show,
-              khugepaged_max_ptes_shared_store);
+       __ATTR_RW(max_ptes_shared);
 
 static struct attribute *khugepaged_attr[] = {
        &khugepaged_defrag_attr.attr,
@@ -437,43 +430,6 @@ static inline int khugepaged_test_exit(struct mm_struct *mm)
        return atomic_read(&mm->mm_users) == 0;
 }
 
-bool hugepage_vma_check(struct vm_area_struct *vma,
-                       unsigned long vm_flags)
-{
-       if (!transhuge_vma_enabled(vma, vm_flags))
-               return false;
-
-       if (vm_flags & VM_NO_KHUGEPAGED)
-               return false;
-
-       /* Don't run khugepaged against DAX vma */
-       if (vma_is_dax(vma))
-               return false;
-
-       if (vma->vm_file && !IS_ALIGNED((vma->vm_start >> PAGE_SHIFT) -
-                               vma->vm_pgoff, HPAGE_PMD_NR))
-               return false;
-
-       /* Enabled via shmem mount options or sysfs settings. */
-       if (shmem_file(vma->vm_file))
-               return shmem_huge_enabled(vma);
-
-       /* THP settings require madvise. */
-       if (!(vm_flags & VM_HUGEPAGE) && !khugepaged_always())
-               return false;
-
-       /* Only regular file is valid */
-       if (file_thp_enabled(vma))
-               return true;
-
-       if (!vma->anon_vma || !vma_is_anonymous(vma))
-               return false;
-       if (vma_is_temporary_stack(vma))
-               return false;
-
-       return true;
-}
-
 void __khugepaged_enter(struct mm_struct *mm)
 {
        struct mm_slot *mm_slot;
@@ -509,10 +465,8 @@ void khugepaged_enter_vma(struct vm_area_struct *vma,
                          unsigned long vm_flags)
 {
        if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
-           khugepaged_enabled() &&
-           (((vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK) <
-            (vma->vm_end & HPAGE_PMD_MASK))) {
-               if (hugepage_vma_check(vma, vm_flags))
+           hugepage_flags_enabled()) {
+               if (hugepage_vma_check(vma, vm_flags, false, false))
                        __khugepaged_enter(vma->vm_mm);
        }
 }
@@ -599,7 +553,7 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma,
        int none_or_zero = 0, shared = 0, result = 0, referenced = 0;
        bool writable = false;
 
-       for (_pte = pte; _pte < pte+HPAGE_PMD_NR;
+       for (_pte = pte; _pte < pte + HPAGE_PMD_NR;
             _pte++, address += PAGE_SIZE) {
                pte_t pteval = *_pte;
                if (pte_none(pteval) || (pte_present(pteval) &&
@@ -618,7 +572,7 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma,
                        goto out;
                }
                page = vm_normal_page(vma, address, pteval);
-               if (unlikely(!page)) {
+               if (unlikely(!page) || unlikely(is_zone_device_page(page))) {
                        result = SCAN_PAGE_NULL;
                        goto out;
                }
@@ -762,7 +716,12 @@ static void __collapse_huge_page_copy(pte_t *pte, struct page *page,
 
        list_for_each_entry_safe(src_page, tmp, compound_pagelist, lru) {
                list_del(&src_page->lru);
-               release_pte_page(src_page);
+               mod_node_page_state(page_pgdat(src_page),
+                                   NR_ISOLATED_ANON + page_is_file_lru(src_page),
+                                   -compound_nr(src_page));
+               unlock_page(src_page);
+               free_swap_cache(src_page);
+               putback_lru_page(src_page);
        }
 }
 
@@ -802,6 +761,10 @@ static bool khugepaged_scan_abort(int nid)
        return false;
 }
 
+#define khugepaged_defrag()                                    \
+       (transparent_hugepage_flags &                           \
+        (1<<TRANSPARENT_HUGEPAGE_DEFRAG_KHUGEPAGED_FLAG))
+
 /* Defrag for khugepaged will enter direct reclaim/compaction if necessary */
 static inline gfp_t alloc_hugepage_khugepaged_gfpmask(void)
 {
@@ -899,7 +862,7 @@ static struct page *khugepaged_alloc_hugepage(bool *wait)
                        khugepaged_alloc_sleep();
                } else
                        count_vm_event(THP_COLLAPSE_ALLOC);
-       } while (unlikely(!hpage) && likely(khugepaged_enabled()));
+       } while (unlikely(!hpage) && likely(hugepage_flags_enabled()));
 
        return hpage;
 }
@@ -947,7 +910,6 @@ static int hugepage_vma_revalidate(struct mm_struct *mm, unsigned long address,
                struct vm_area_struct **vmap)
 {
        struct vm_area_struct *vma;
-       unsigned long hstart, hend;
 
        if (unlikely(khugepaged_test_exit(mm)))
                return SCAN_ANY_PROCESS;
@@ -956,13 +918,17 @@ static int hugepage_vma_revalidate(struct mm_struct *mm, unsigned long address,
        if (!vma)
                return SCAN_VMA_NULL;
 
-       hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK;
-       hend = vma->vm_end & HPAGE_PMD_MASK;
-       if (address < hstart || address + HPAGE_PMD_SIZE > hend)
+       if (!transhuge_vma_suitable(vma, address))
                return SCAN_ADDRESS_RANGE;
-       if (!hugepage_vma_check(vma, vma->vm_flags))
+       if (!hugepage_vma_check(vma, vma->vm_flags, false, false))
                return SCAN_VMA_CHECK;
-       /* Anon VMA expected */
+       /*
+        * Anon VMA expected, the address may be unmapped then
+        * remapped to file after khugepaged reaquired the mmap_lock.
+        *
+        * hugepage_vma_check may return true for qualified file
+        * vmas.
+        */
        if (!vma->anon_vma || !vma_is_anonymous(vma))
                return SCAN_VMA_CHECK;
        return 0;
@@ -972,8 +938,8 @@ static int hugepage_vma_revalidate(struct mm_struct *mm, unsigned long address,
  * Bring missing pages in from swap, to complete THP collapse.
  * Only done if khugepaged_scan_pmd believes it is worthwhile.
  *
- * Called and returns without pte mapped or spinlocks held,
- * but with mmap_lock held to protect against vma changes.
+ * Called and returns without pte mapped or spinlocks held.
+ * Note that if false is returned, mmap_lock will be released.
  */
 
 static bool __collapse_huge_page_swapin(struct mm_struct *mm,
@@ -1000,27 +966,24 @@ static bool __collapse_huge_page_swapin(struct mm_struct *mm,
                        pte_unmap(vmf.pte);
                        continue;
                }
-               swapped_in++;
                ret = do_swap_page(&vmf);
 
-               /* 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.
+                */
                if (ret & VM_FAULT_RETRY) {
-                       mmap_read_lock(mm);
-                       if (hugepage_vma_revalidate(mm, haddr, &vma)) {
-                               /* vma is no longer available, don't continue to swapin */
-                               trace_mm_collapse_huge_page_swapin(mm, swapped_in, referenced, 0);
-                               return false;
-                       }
-                       /* check if the pmd is still valid */
-                       if (mm_find_pmd(mm, haddr) != pmd) {
-                               trace_mm_collapse_huge_page_swapin(mm, swapped_in, referenced, 0);
-                               return false;
-                       }
+                       trace_mm_collapse_huge_page_swapin(mm, swapped_in, referenced, 0);
+                       return false;
                }
                if (ret & VM_FAULT_ERROR) {
+                       mmap_read_unlock(mm);
                        trace_mm_collapse_huge_page_swapin(mm, swapped_in, referenced, 0);
                        return false;
                }
+               swapped_in++;
        }
 
        /* Drain LRU add pagevec to remove extra pin on the swapped in pages */
@@ -1086,13 +1049,12 @@ static void collapse_huge_page(struct mm_struct *mm,
        }
 
        /*
-        * __collapse_huge_page_swapin always returns with mmap_lock locked.
-        * If it fails, we release mmap_lock and jump out_nolock.
+        * __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.
         */
        if (unmapped && !__collapse_huge_page_swapin(mm, vma, address,
                                                     pmd, referenced)) {
-               mmap_read_unlock(mm);
                goto out_nolock;
        }
 
@@ -1219,7 +1181,7 @@ static int khugepaged_scan_pmd(struct mm_struct *mm,
 
        memset(khugepaged_node_load, 0, sizeof(khugepaged_node_load));
        pte = pte_offset_map_lock(mm, pmd, address, &ptl);
-       for (_address = address, _pte = pte; _pte < pte+HPAGE_PMD_NR;
+       for (_address = address, _pte = pte; _pte < pte + HPAGE_PMD_NR;
             _pte++, _address += PAGE_SIZE) {
                pte_t pteval = *_pte;
                if (is_swap_pte(pteval)) {
@@ -1267,7 +1229,7 @@ static int khugepaged_scan_pmd(struct mm_struct *mm,
                        writable = true;
 
                page = vm_normal_page(vma, _address, pteval);
-               if (unlikely(!page)) {
+               if (unlikely(!page) || unlikely(is_zone_device_page(page))) {
                        result = SCAN_PAGE_NULL;
                        goto out_unmap;
                }
@@ -1309,7 +1271,7 @@ static int khugepaged_scan_pmd(struct mm_struct *mm,
                /*
                 * Check if the page has any GUP (or other external) pins.
                 *
-                * Here the check is racy it may see totmal_mapcount > refcount
+                * Here the check is racy it may see total_mapcount > refcount
                 * in some cases.
                 * For example, one process with one forked child process.
                 * The parent has the PMD split due to MADV_DONTNEED, then
@@ -1382,8 +1344,8 @@ static void collect_mm_slot(struct mm_slot *mm_slot)
  * Notify khugepaged that given addr of the mm is pte-mapped THP. Then
  * khugepaged should try to collapse the page table.
  */
-static int khugepaged_add_pte_mapped_thp(struct mm_struct *mm,
-                                        unsigned long addr)
+static void khugepaged_add_pte_mapped_thp(struct mm_struct *mm,
+                                         unsigned long addr)
 {
        struct mm_slot *mm_slot;
 
@@ -1394,7 +1356,6 @@ static int khugepaged_add_pte_mapped_thp(struct mm_struct *mm,
        if (likely(mm_slot && mm_slot->nr_pte_mapped_thp < MAX_PTE_MAPPED_THP))
                mm_slot->pte_mapped_thp[mm_slot->nr_pte_mapped_thp++] = addr;
        spin_unlock(&khugepaged_mm_lock);
-       return 0;
 }
 
 static void collapse_and_free_pmd(struct mm_struct *mm, struct vm_area_struct *vma,
@@ -1444,7 +1405,7 @@ void collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr)
         * the valid THP. Add extra VM_HUGEPAGE so hugepage_vma_check()
         * will not fail the vma for missing VM_HUGEPAGE
         */
-       if (!hugepage_vma_check(vma, vma->vm_flags | VM_HUGEPAGE))
+       if (!hugepage_vma_check(vma, vma->vm_flags | VM_HUGEPAGE, false, false))
                return;
 
        /* Keep pmd pgtable for uffd-wp; see comment in retract_page_tables() */
@@ -1479,7 +1440,8 @@ void collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr)
                        goto abort;
 
                page = vm_normal_page(vma, addr, *pte);
-
+               if (WARN_ON_ONCE(page && is_zone_device_page(page)))
+                       page = NULL;
                /*
                 * Note that uprobe, debugger, or MAP_PRIVATE may change the
                 * page table, but the new page will not be a subpage of hpage.
@@ -1497,6 +1459,8 @@ void collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr)
                if (pte_none(*pte))
                        continue;
                page = vm_normal_page(vma, addr, *pte);
+               if (WARN_ON_ONCE(page && is_zone_device_page(page)))
+                       goto abort;
                page_remove_rmap(page, vma, false);
        }
 
@@ -1557,7 +1521,7 @@ static void retract_page_tables(struct address_space *mapping, pgoff_t pgoff)
                 * mmap_write_lock(mm) as PMD-mapping is likely to be split
                 * later.
                 *
-                * Not that vma->anon_vma check is racy: it can be set up after
+                * Note that vma->anon_vma check is racy: it can be set up after
                 * the check but before we took mmap_lock by the fault path.
                 * But page lock would prevent establishing any new ptes of the
                 * page, so we are safe.
@@ -1885,8 +1849,8 @@ out_unlock:
 
        if (nr_none) {
                __mod_lruvec_page_state(new_page, NR_FILE_PAGES, nr_none);
-               if (is_shmem)
-                       __mod_lruvec_page_state(new_page, NR_SHMEM, nr_none);
+               /* nr_none is always 0 for non-shmem. */
+               __mod_lruvec_page_state(new_page, NR_SHMEM, nr_none);
        }
 
        /* Join all the small entries into a single multi-index entry */
@@ -1950,10 +1914,10 @@ xa_unlocked:
 
                /* Something went wrong: roll back page cache changes */
                xas_lock_irq(&xas);
-               mapping->nrpages -= nr_none;
-
-               if (is_shmem)
+               if (nr_none) {
+                       mapping->nrpages -= nr_none;
                        shmem_uncharge(mapping->host, nr_none);
+               }
 
                xas_set(&xas, start);
                xas_for_each(&xas, page, end - 1) {
@@ -2131,22 +2095,18 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages,
                        progress++;
                        break;
                }
-               if (!hugepage_vma_check(vma, vma->vm_flags)) {
+               if (!hugepage_vma_check(vma, vma->vm_flags, false, false)) {
 skip:
                        progress++;
                        continue;
                }
-               hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK;
-               hend = vma->vm_end & HPAGE_PMD_MASK;
-               if (hstart >= hend)
-                       goto skip;
+               hstart = round_up(vma->vm_start, HPAGE_PMD_SIZE);
+               hend = round_down(vma->vm_end, HPAGE_PMD_SIZE);
                if (khugepaged_scan.address > hend)
                        goto skip;
                if (khugepaged_scan.address < hstart)
                        khugepaged_scan.address = hstart;
                VM_BUG_ON(khugepaged_scan.address & ~HPAGE_PMD_MASK);
-               if (shmem_file(vma->vm_file) && !shmem_huge_enabled(vma))
-                       goto skip;
 
                while (khugepaged_scan.address < hend) {
                        int ret;
@@ -2216,7 +2176,7 @@ breakouterloop_mmap_lock:
 static int khugepaged_has_work(void)
 {
        return !list_empty(&khugepaged_scan.mm_head) &&
-               khugepaged_enabled();
+               hugepage_flags_enabled();
 }
 
 static int khugepaged_wait_event(void)
@@ -2281,7 +2241,7 @@ static void khugepaged_wait_work(void)
                return;
        }
 
-       if (khugepaged_enabled())
+       if (hugepage_flags_enabled())
                wait_event_freezable(khugepaged_wait, khugepaged_wait_event());
 }
 
@@ -2312,7 +2272,7 @@ static void set_recommended_min_free_kbytes(void)
        int nr_zones = 0;
        unsigned long recommended_min;
 
-       if (!khugepaged_enabled()) {
+       if (!hugepage_flags_enabled()) {
                calculate_min_free_kbytes();
                goto update_wmarks;
        }
@@ -2362,7 +2322,7 @@ int start_stop_khugepaged(void)
        int err = 0;
 
        mutex_lock(&khugepaged_mutex);
-       if (khugepaged_enabled()) {
+       if (hugepage_flags_enabled()) {
                if (!khugepaged_thread)
                        khugepaged_thread = kthread_run(khugepaged, NULL,
                                                        "khugepaged");
@@ -2388,7 +2348,7 @@ fail:
 void khugepaged_min_free_kbytes_update(void)
 {
        mutex_lock(&khugepaged_mutex);
-       if (khugepaged_enabled() && khugepaged_thread)
+       if (hugepage_flags_enabled() && khugepaged_thread)
                set_recommended_min_free_kbytes();
        mutex_unlock(&khugepaged_mutex);
 }
index a182f5d..1eddc01 100644 (file)
  * The following locks and mutexes are used by kmemleak:
  *
  * - kmemleak_lock (raw_spinlock_t): protects the object_list modifications and
- *   accesses to the object_tree_root. The object_list is the main list
- *   holding the metadata (struct kmemleak_object) for the allocated memory
- *   blocks. The object_tree_root is a red black tree used to look-up
- *   metadata based on a pointer to the corresponding memory block.  The
- *   kmemleak_object structures are added to the object_list and
- *   object_tree_root in the create_object() function called from the
- *   kmemleak_alloc() callback and removed in delete_object() called from the
- *   kmemleak_free() callback
+ *   accesses to the object_tree_root (or object_phys_tree_root). The
+ *   object_list is the main list holding the metadata (struct kmemleak_object)
+ *   for the allocated memory blocks. The object_tree_root and object_phys_tree_root
+ *   are red black trees used to look-up metadata based on a pointer to the
+ *   corresponding memory block. The object_phys_tree_root is for objects
+ *   allocated with physical address. The kmemleak_object structures are
+ *   added to the object_list and object_tree_root (or object_phys_tree_root)
+ *   in the create_object() function called from the kmemleak_alloc() (or
+ *   kmemleak_alloc_phys()) callback and removed in delete_object() called from
+ *   the kmemleak_free() callback
  * - kmemleak_object.lock (raw_spinlock_t): protects a kmemleak_object.
  *   Accesses to the metadata (e.g. count) are protected by this lock. Note
  *   that some members of this structure may be protected by other means
@@ -172,6 +174,8 @@ struct kmemleak_object {
 #define OBJECT_NO_SCAN         (1 << 2)
 /* flag set to fully scan the object when scan_area allocation failed */
 #define OBJECT_FULL_SCAN       (1 << 3)
+/* flag set for object allocated with physical address */
+#define OBJECT_PHYS            (1 << 4)
 
 #define HEX_PREFIX             "    "
 /* number of bytes to print per line; must be 16 or 32 */
@@ -193,7 +197,9 @@ static int mem_pool_free_count = ARRAY_SIZE(mem_pool);
 static LIST_HEAD(mem_pool_free_list);
 /* search tree for object boundaries */
 static struct rb_root object_tree_root = RB_ROOT;
-/* protecting the access to object_list and object_tree_root */
+/* search tree for object (with OBJECT_PHYS flag) boundaries */
+static struct rb_root object_phys_tree_root = RB_ROOT;
+/* protecting the access to object_list, object_tree_root (or object_phys_tree_root) */
 static DEFINE_RAW_SPINLOCK(kmemleak_lock);
 
 /* allocation caches for kmemleak internal data */
@@ -285,6 +291,9 @@ static void hex_dump_object(struct seq_file *seq,
        const u8 *ptr = (const u8 *)object->pointer;
        size_t len;
 
+       if (WARN_ON_ONCE(object->flags & OBJECT_PHYS))
+               return;
+
        /* limit the number of lines to HEX_MAX_LINES */
        len = min_t(size_t, object->size, HEX_MAX_LINES * HEX_ROW_SIZE);
 
@@ -378,9 +387,11 @@ static void dump_object_info(struct kmemleak_object *object)
  * beginning of the memory block are allowed. The kmemleak_lock must be held
  * when calling this function.
  */
-static struct kmemleak_object *lookup_object(unsigned long ptr, int alias)
+static struct kmemleak_object *__lookup_object(unsigned long ptr, int alias,
+                                              bool is_phys)
 {
-       struct rb_node *rb = object_tree_root.rb_node;
+       struct rb_node *rb = is_phys ? object_phys_tree_root.rb_node :
+                            object_tree_root.rb_node;
        unsigned long untagged_ptr = (unsigned long)kasan_reset_tag((void *)ptr);
 
        while (rb) {
@@ -406,6 +417,12 @@ static struct kmemleak_object *lookup_object(unsigned long ptr, int alias)
        return NULL;
 }
 
+/* Look-up a kmemleak object which allocated with virtual address. */
+static struct kmemleak_object *lookup_object(unsigned long ptr, int alias)
+{
+       return __lookup_object(ptr, alias, false);
+}
+
 /*
  * Increment the object use_count. Return 1 if successful or 0 otherwise. Note
  * that once an object's use_count reached 0, the RCU freeing was already
@@ -515,14 +532,15 @@ static void put_object(struct kmemleak_object *object)
 /*
  * Look up an object in the object search tree and increase its use_count.
  */
-static struct kmemleak_object *find_and_get_object(unsigned long ptr, int alias)
+static struct kmemleak_object *__find_and_get_object(unsigned long ptr, int alias,
+                                                    bool is_phys)
 {
        unsigned long flags;
        struct kmemleak_object *object;
 
        rcu_read_lock();
        raw_spin_lock_irqsave(&kmemleak_lock, flags);
-       object = lookup_object(ptr, alias);
+       object = __lookup_object(ptr, alias, is_phys);
        raw_spin_unlock_irqrestore(&kmemleak_lock, flags);
 
        /* check whether the object is still available */
@@ -533,28 +551,39 @@ static struct kmemleak_object *find_and_get_object(unsigned long ptr, int alias)
        return object;
 }
 
+/* Look up and get an object which allocated with virtual address. */
+static struct kmemleak_object *find_and_get_object(unsigned long ptr, int alias)
+{
+       return __find_and_get_object(ptr, alias, false);
+}
+
 /*
- * Remove an object from the object_tree_root and object_list. Must be called
- * with the kmemleak_lock held _if_ kmemleak is still enabled.
+ * Remove an object from the object_tree_root (or object_phys_tree_root)
+ * and object_list. Must be called with the kmemleak_lock held _if_ kmemleak
+ * is still enabled.
  */
 static void __remove_object(struct kmemleak_object *object)
 {
-       rb_erase(&object->rb_node, &object_tree_root);
+       rb_erase(&object->rb_node, object->flags & OBJECT_PHYS ?
+                                  &object_phys_tree_root :
+                                  &object_tree_root);
        list_del_rcu(&object->object_list);
 }
 
 /*
  * Look up an object in the object search tree and remove it from both
- * object_tree_root and object_list. The returned object's use_count should be
- * at least 1, as initially set by create_object().
+ * object_tree_root (or object_phys_tree_root) and object_list. The
+ * returned object's use_count should be at least 1, as initially set
+ * by create_object().
  */
-static struct kmemleak_object *find_and_remove_object(unsigned long ptr, int alias)
+static struct kmemleak_object *find_and_remove_object(unsigned long ptr, int alias,
+                                                     bool is_phys)
 {
        unsigned long flags;
        struct kmemleak_object *object;
 
        raw_spin_lock_irqsave(&kmemleak_lock, flags);
-       object = lookup_object(ptr, alias);
+       object = __lookup_object(ptr, alias, is_phys);
        if (object)
                __remove_object(object);
        raw_spin_unlock_irqrestore(&kmemleak_lock, flags);
@@ -572,10 +601,12 @@ static int __save_stack_trace(unsigned long *trace)
 
 /*
  * Create the metadata (struct kmemleak_object) corresponding to an allocated
- * memory block and add it to the object_list and object_tree_root.
+ * memory block and add it to the object_list and object_tree_root (or
+ * object_phys_tree_root).
  */
-static struct kmemleak_object *create_object(unsigned long ptr, size_t size,
-                                            int min_count, gfp_t gfp)
+static struct kmemleak_object *__create_object(unsigned long ptr, size_t size,
+                                            int min_count, gfp_t gfp,
+                                            bool is_phys)
 {
        unsigned long flags;
        struct kmemleak_object *object, *parent;
@@ -595,7 +626,7 @@ static struct kmemleak_object *create_object(unsigned long ptr, size_t size,
        INIT_HLIST_HEAD(&object->area_list);
        raw_spin_lock_init(&object->lock);
        atomic_set(&object->use_count, 1);
-       object->flags = OBJECT_ALLOCATED;
+       object->flags = OBJECT_ALLOCATED | (is_phys ? OBJECT_PHYS : 0);
        object->pointer = ptr;
        object->size = kfence_ksize((void *)ptr) ?: size;
        object->excess_ref = 0;
@@ -628,9 +659,16 @@ static struct kmemleak_object *create_object(unsigned long ptr, size_t size,
        raw_spin_lock_irqsave(&kmemleak_lock, flags);
 
        untagged_ptr = (unsigned long)kasan_reset_tag((void *)ptr);
-       min_addr = min(min_addr, untagged_ptr);
-       max_addr = max(max_addr, untagged_ptr + size);
-       link = &object_tree_root.rb_node;
+       /*
+        * Only update min_addr and max_addr with object
+        * storing virtual address.
+        */
+       if (!is_phys) {
+               min_addr = min(min_addr, untagged_ptr);
+               max_addr = max(max_addr, untagged_ptr + size);
+       }
+       link = is_phys ? &object_phys_tree_root.rb_node :
+               &object_tree_root.rb_node;
        rb_parent = NULL;
        while (*link) {
                rb_parent = *link;
@@ -654,7 +692,8 @@ static struct kmemleak_object *create_object(unsigned long ptr, size_t size,
                }
        }
        rb_link_node(&object->rb_node, rb_parent, link);
-       rb_insert_color(&object->rb_node, &object_tree_root);
+       rb_insert_color(&object->rb_node, is_phys ? &object_phys_tree_root :
+                                         &object_tree_root);
 
        list_add_tail_rcu(&object->object_list, &object_list);
 out:
@@ -662,6 +701,20 @@ out:
        return object;
 }
 
+/* Create kmemleak object which allocated with virtual address. */
+static struct kmemleak_object *create_object(unsigned long ptr, size_t size,
+                                            int min_count, gfp_t gfp)
+{
+       return __create_object(ptr, size, min_count, gfp, false);
+}
+
+/* Create kmemleak object which allocated with physical address. */
+static struct kmemleak_object *create_object_phys(unsigned long ptr, size_t size,
+                                            int min_count, gfp_t gfp)
+{
+       return __create_object(ptr, size, min_count, gfp, true);
+}
+
 /*
  * Mark the object as not allocated and schedule RCU freeing via put_object().
  */
@@ -690,7 +743,7 @@ static void delete_object_full(unsigned long ptr)
 {
        struct kmemleak_object *object;
 
-       object = find_and_remove_object(ptr, 0);
+       object = find_and_remove_object(ptr, 0, false);
        if (!object) {
 #ifdef DEBUG
                kmemleak_warn("Freeing unknown object at 0x%08lx\n",
@@ -706,12 +759,12 @@ static void delete_object_full(unsigned long ptr)
  * delete it. If the memory block is partially freed, the function may create
  * additional metadata for the remaining parts of the block.
  */
-static void delete_object_part(unsigned long ptr, size_t size)
+static void delete_object_part(unsigned long ptr, size_t size, bool is_phys)
 {
        struct kmemleak_object *object;
        unsigned long start, end;
 
-       object = find_and_remove_object(ptr, 1);
+       object = find_and_remove_object(ptr, 1, is_phys);
        if (!object) {
 #ifdef DEBUG
                kmemleak_warn("Partially freeing unknown object at 0x%08lx (size %zu)\n",
@@ -728,11 +781,11 @@ static void delete_object_part(unsigned long ptr, size_t size)
        start = object->pointer;
        end = object->pointer + object->size;
        if (ptr > start)
-               create_object(start, ptr - start, object->min_count,
-                             GFP_KERNEL);
+               __create_object(start, ptr - start, object->min_count,
+                             GFP_KERNEL, is_phys);
        if (ptr + size < end)
-               create_object(ptr + size, end - ptr - size, object->min_count,
-                             GFP_KERNEL);
+               __create_object(ptr + size, end - ptr - size, object->min_count,
+                             GFP_KERNEL, is_phys);
 
        __delete_object(object);
 }
@@ -753,11 +806,11 @@ static void paint_it(struct kmemleak_object *object, int color)
        raw_spin_unlock_irqrestore(&object->lock, flags);
 }
 
-static void paint_ptr(unsigned long ptr, int color)
+static void paint_ptr(unsigned long ptr, int color, bool is_phys)
 {
        struct kmemleak_object *object;
 
-       object = find_and_get_object(ptr, 0);
+       object = __find_and_get_object(ptr, 0, is_phys);
        if (!object) {
                kmemleak_warn("Trying to color unknown object at 0x%08lx as %s\n",
                              ptr,
@@ -775,16 +828,16 @@ static void paint_ptr(unsigned long ptr, int color)
  */
 static void make_gray_object(unsigned long ptr)
 {
-       paint_ptr(ptr, KMEMLEAK_GREY);
+       paint_ptr(ptr, KMEMLEAK_GREY, false);
 }
 
 /*
  * Mark the object as black-colored so that it is ignored from scans and
  * reporting.
  */
-static void make_black_object(unsigned long ptr)
+static void make_black_object(unsigned long ptr, bool is_phys)
 {
-       paint_ptr(ptr, KMEMLEAK_BLACK);
+       paint_ptr(ptr, KMEMLEAK_BLACK, is_phys);
 }
 
 /*
@@ -990,7 +1043,7 @@ void __ref kmemleak_free_part(const void *ptr, size_t size)
        pr_debug("%s(0x%p)\n", __func__, ptr);
 
        if (kmemleak_enabled && ptr && !IS_ERR(ptr))
-               delete_object_part((unsigned long)ptr, size);
+               delete_object_part((unsigned long)ptr, size, false);
 }
 EXPORT_SYMBOL_GPL(kmemleak_free_part);
 
@@ -1078,7 +1131,7 @@ void __ref kmemleak_ignore(const void *ptr)
        pr_debug("%s(0x%p)\n", __func__, ptr);
 
        if (kmemleak_enabled && ptr && !IS_ERR(ptr))
-               make_black_object((unsigned long)ptr);
+               make_black_object((unsigned long)ptr, false);
 }
 EXPORT_SYMBOL(kmemleak_ignore);
 
@@ -1125,15 +1178,18 @@ EXPORT_SYMBOL(kmemleak_no_scan);
  *                      address argument
  * @phys:      physical address of the object
  * @size:      size of the object
- * @min_count: minimum number of references to this object.
- *              See kmemleak_alloc()
  * @gfp:       kmalloc() flags used for kmemleak internal memory allocations
  */
-void __ref kmemleak_alloc_phys(phys_addr_t phys, size_t size, int min_count,
-                              gfp_t gfp)
+void __ref kmemleak_alloc_phys(phys_addr_t phys, size_t size, gfp_t gfp)
 {
-       if (PHYS_PFN(phys) >= min_low_pfn && PHYS_PFN(phys) < max_low_pfn)
-               kmemleak_alloc(__va(phys), size, min_count, gfp);
+       pr_debug("%s(0x%pa, %zu)\n", __func__, &phys, size);
+
+       if (kmemleak_enabled)
+               /*
+                * Create object with OBJECT_PHYS flag and
+                * assume min_count 0.
+                */
+               create_object_phys((unsigned long)phys, size, 0, gfp);
 }
 EXPORT_SYMBOL(kmemleak_alloc_phys);
 
@@ -1146,22 +1202,12 @@ EXPORT_SYMBOL(kmemleak_alloc_phys);
  */
 void __ref kmemleak_free_part_phys(phys_addr_t phys, size_t size)
 {
-       if (PHYS_PFN(phys) >= min_low_pfn && PHYS_PFN(phys) < max_low_pfn)
-               kmemleak_free_part(__va(phys), size);
-}
-EXPORT_SYMBOL(kmemleak_free_part_phys);
+       pr_debug("%s(0x%pa)\n", __func__, &phys);
 
-/**
- * kmemleak_not_leak_phys - similar to kmemleak_not_leak but taking a physical
- *                         address argument
- * @phys:      physical address of the object
- */
-void __ref kmemleak_not_leak_phys(phys_addr_t phys)
-{
-       if (PHYS_PFN(phys) >= min_low_pfn && PHYS_PFN(phys) < max_low_pfn)
-               kmemleak_not_leak(__va(phys));
+       if (kmemleak_enabled)
+               delete_object_part((unsigned long)phys, size, true);
 }
-EXPORT_SYMBOL(kmemleak_not_leak_phys);
+EXPORT_SYMBOL(kmemleak_free_part_phys);
 
 /**
  * kmemleak_ignore_phys - similar to kmemleak_ignore but taking a physical
@@ -1170,8 +1216,10 @@ EXPORT_SYMBOL(kmemleak_not_leak_phys);
  */
 void __ref kmemleak_ignore_phys(phys_addr_t phys)
 {
-       if (PHYS_PFN(phys) >= min_low_pfn && PHYS_PFN(phys) < max_low_pfn)
-               kmemleak_ignore(__va(phys));
+       pr_debug("%s(0x%pa)\n", __func__, &phys);
+
+       if (kmemleak_enabled)
+               make_black_object((unsigned long)phys, true);
 }
 EXPORT_SYMBOL(kmemleak_ignore_phys);
 
@@ -1182,6 +1230,9 @@ static bool update_checksum(struct kmemleak_object *object)
 {
        u32 old_csum = object->checksum;
 
+       if (WARN_ON_ONCE(object->flags & OBJECT_PHYS))
+               return false;
+
        kasan_disable_current();
        kcsan_disable_current();
        object->checksum = crc32(0, kasan_reset_tag((void *)object->pointer), object->size);
@@ -1335,6 +1386,7 @@ static void scan_object(struct kmemleak_object *object)
 {
        struct kmemleak_scan_area *area;
        unsigned long flags;
+       void *obj_ptr;
 
        /*
         * Once the object->lock is acquired, the corresponding memory block
@@ -1346,10 +1398,15 @@ static void scan_object(struct kmemleak_object *object)
        if (!(object->flags & OBJECT_ALLOCATED))
                /* already freed object */
                goto out;
+
+       obj_ptr = object->flags & OBJECT_PHYS ?
+                 __va((phys_addr_t)object->pointer) :
+                 (void *)object->pointer;
+
        if (hlist_empty(&object->area_list) ||
            object->flags & OBJECT_FULL_SCAN) {
-               void *start = (void *)object->pointer;
-               void *end = (void *)(object->pointer + object->size);
+               void *start = obj_ptr;
+               void *end = obj_ptr + object->size;
                void *next;
 
                do {
@@ -1413,18 +1470,21 @@ static void scan_gray_list(void)
  */
 static void kmemleak_scan(void)
 {
-       unsigned long flags;
        struct kmemleak_object *object;
        struct zone *zone;
        int __maybe_unused i;
        int new_leaks = 0;
+       int loop1_cnt = 0;
 
        jiffies_last_scan = jiffies;
 
        /* prepare the kmemleak_object's */
        rcu_read_lock();
        list_for_each_entry_rcu(object, &object_list, object_list) {
-               raw_spin_lock_irqsave(&object->lock, flags);
+               bool obj_pinned = false;
+
+               loop1_cnt++;
+               raw_spin_lock_irq(&object->lock);
 #ifdef DEBUG
                /*
                 * With a few exceptions there should be a maximum of
@@ -1436,12 +1496,45 @@ static void kmemleak_scan(void)
                        dump_object_info(object);
                }
 #endif
+
+               /* ignore objects outside lowmem (paint them black) */
+               if ((object->flags & OBJECT_PHYS) &&
+                  !(object->flags & OBJECT_NO_SCAN)) {
+                       unsigned long phys = object->pointer;
+
+                       if (PHYS_PFN(phys) < min_low_pfn ||
+                           PHYS_PFN(phys + object->size) >= max_low_pfn)
+                               __paint_it(object, KMEMLEAK_BLACK);
+               }
+
                /* reset the reference count (whiten the object) */
                object->count = 0;
-               if (color_gray(object) && get_object(object))
+               if (color_gray(object) && get_object(object)) {
                        list_add_tail(&object->gray_list, &gray_list);
+                       obj_pinned = true;
+               }
 
-               raw_spin_unlock_irqrestore(&object->lock, flags);
+               raw_spin_unlock_irq(&object->lock);
+
+               /*
+                * Do a cond_resched() to avoid soft lockup every 64k objects.
+                * Make sure a reference has been taken so that the object
+                * won't go away without RCU read lock.
+                */
+               if (!(loop1_cnt & 0xffff)) {
+                       if (!obj_pinned && !get_object(object)) {
+                               /* Try the next object instead */
+                               loop1_cnt--;
+                               continue;
+                       }
+
+                       rcu_read_unlock();
+                       cond_resched();
+                       rcu_read_lock();
+
+                       if (!obj_pinned)
+                               put_object(object);
+               }
        }
        rcu_read_unlock();
 
@@ -1509,14 +1602,21 @@ static void kmemleak_scan(void)
         */
        rcu_read_lock();
        list_for_each_entry_rcu(object, &object_list, object_list) {
-               raw_spin_lock_irqsave(&object->lock, flags);
+               /*
+                * This is racy but we can save the overhead of lock/unlock
+                * calls. The missed objects, if any, should be caught in
+                * the next scan.
+                */
+               if (!color_white(object))
+                       continue;
+               raw_spin_lock_irq(&object->lock);
                if (color_white(object) && (object->flags & OBJECT_ALLOCATED)
                    && update_checksum(object) && get_object(object)) {
                        /* color it gray temporarily */
                        object->count = object->min_count;
                        list_add_tail(&object->gray_list, &gray_list);
                }
-               raw_spin_unlock_irqrestore(&object->lock, flags);
+               raw_spin_unlock_irq(&object->lock);
        }
        rcu_read_unlock();
 
@@ -1536,7 +1636,14 @@ static void kmemleak_scan(void)
         */
        rcu_read_lock();
        list_for_each_entry_rcu(object, &object_list, object_list) {
-               raw_spin_lock_irqsave(&object->lock, flags);
+               /*
+                * This is racy but we can save the overhead of lock/unlock
+                * calls. The missed objects, if any, should be caught in
+                * the next scan.
+                */
+               if (!color_white(object))
+                       continue;
+               raw_spin_lock_irq(&object->lock);
                if (unreferenced_object(object) &&
                    !(object->flags & OBJECT_REPORTED)) {
                        object->flags |= OBJECT_REPORTED;
@@ -1546,7 +1653,7 @@ static void kmemleak_scan(void)
 
                        new_leaks++;
                }
-               raw_spin_unlock_irqrestore(&object->lock, flags);
+               raw_spin_unlock_irq(&object->lock);
        }
        rcu_read_unlock();
 
@@ -1748,15 +1855,14 @@ static int dump_str_object_info(const char *str)
 static void kmemleak_clear(void)
 {
        struct kmemleak_object *object;
-       unsigned long flags;
 
        rcu_read_lock();
        list_for_each_entry_rcu(object, &object_list, object_list) {
-               raw_spin_lock_irqsave(&object->lock, flags);
+               raw_spin_lock_irq(&object->lock);
                if ((object->flags & OBJECT_REPORTED) &&
                    unreferenced_object(object))
                        __paint_it(object, KMEMLEAK_GREY);
-               raw_spin_unlock_irqrestore(&object->lock, flags);
+               raw_spin_unlock_irq(&object->lock);
        }
        rcu_read_unlock();
 
index e8f8c1a..42ab153 100644 (file)
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -475,7 +475,7 @@ static int break_ksm(struct vm_area_struct *vma, unsigned long addr)
                cond_resched();
                page = follow_page(vma, addr,
                                FOLL_GET | FOLL_MIGRATION | FOLL_REMOTE);
-               if (IS_ERR_OR_NULL(page))
+               if (IS_ERR_OR_NULL(page) || is_zone_device_page(page))
                        break;
                if (PageKsm(page))
                        ret = handle_mm_fault(vma, addr,
@@ -560,7 +560,7 @@ static struct page *get_mergeable_page(struct rmap_item *rmap_item)
                goto out;
 
        page = follow_page(vma, addr, FOLL_GET);
-       if (IS_ERR_OR_NULL(page))
+       if (IS_ERR_OR_NULL(page) || is_zone_device_page(page))
                goto out;
        if (PageAnon(page)) {
                flush_anon_page(vma, page, addr);
@@ -1083,7 +1083,7 @@ static int write_protect_page(struct vm_area_struct *vma, struct page *page,
                 * No need to notify as we are downgrading page table to read
                 * only not changing it to point to a new page.
                 *
-                * See Documentation/vm/mmu_notifier.rst
+                * See Documentation/mm/mmu_notifier.rst
                 */
                entry = ptep_clear_flush(vma, pvmw.address, pvmw.pte);
                /*
@@ -1186,7 +1186,7 @@ static int replace_page(struct vm_area_struct *vma, struct page *page,
         * No need to notify as we are replacing a read only page with another
         * read only page with the same content.
         *
-        * See Documentation/vm/mmu_notifier.rst
+        * See Documentation/mm/mmu_notifier.rst
         */
        ptep_clear_flush(vma, addr, ptep);
        set_pte_at_notify(mm, addr, ptep, newpte);
@@ -2308,7 +2308,7 @@ next_mm:
                        if (ksm_test_exit(mm))
                                break;
                        *page = follow_page(vma, ksm_scan.address, FOLL_GET);
-                       if (IS_ERR_OR_NULL(*page)) {
+                       if (IS_ERR_OR_NULL(*page) || is_zone_device_page(*page)) {
                                ksm_scan.address += PAGE_SIZE;
                                cond_resched();
                                continue;
index ba76428..a05e5be 100644 (file)
@@ -71,7 +71,7 @@ list_lru_from_kmem(struct list_lru *lru, int nid, void *ptr,
        if (!list_lru_memcg_aware(lru))
                goto out;
 
-       memcg = mem_cgroup_from_obj(ptr);
+       memcg = mem_cgroup_from_slab_obj(ptr);
        if (!memcg)
                goto out;
 
index 0316bbc..5f0f094 100644 (file)
@@ -195,7 +195,6 @@ success:
 static int swapin_walk_pmd_entry(pmd_t *pmd, unsigned long start,
        unsigned long end, struct mm_walk *walk)
 {
-       pte_t *orig_pte;
        struct vm_area_struct *vma = walk->private;
        unsigned long index;
        struct swap_iocb *splug = NULL;
@@ -208,12 +207,13 @@ static int swapin_walk_pmd_entry(pmd_t *pmd, unsigned long start,
                swp_entry_t entry;
                struct page *page;
                spinlock_t *ptl;
+               pte_t *ptep;
 
-               orig_pte = pte_offset_map_lock(vma->vm_mm, pmd, start, &ptl);
-               pte = *(orig_pte + ((index - start) / PAGE_SIZE));
-               pte_unmap_unlock(orig_pte, ptl);
+               ptep = pte_offset_map_lock(vma->vm_mm, pmd, index, &ptl);
+               pte = *ptep;
+               pte_unmap_unlock(ptep, ptl);
 
-               if (pte_present(pte) || pte_none(pte))
+               if (!is_swap_pte(pte))
                        continue;
                entry = pte_to_swp_entry(pte);
                if (unlikely(non_swap_entry(entry)))
@@ -421,7 +421,7 @@ regular_page:
                        continue;
 
                page = vm_normal_page(vma, addr, ptent);
-               if (!page)
+               if (!page || is_zone_device_page(page))
                        continue;
 
                /*
@@ -639,7 +639,7 @@ static int madvise_free_pte_range(pmd_t *pmd, unsigned long addr,
                }
 
                page = vm_normal_page(vma, addr, ptent);
-               if (!page)
+               if (!page || is_zone_device_page(page))
                        continue;
 
                /*
index a9f18b9..c0894c1 100644 (file)
 # define INIT_MEMBLOCK_RESERVED_REGIONS                INIT_MEMBLOCK_REGIONS
 #endif
 
+#ifndef INIT_MEMBLOCK_MEMORY_REGIONS
+#define INIT_MEMBLOCK_MEMORY_REGIONS           INIT_MEMBLOCK_REGIONS
+#endif
+
 /**
  * DOC: memblock overview
  *
@@ -55,9 +59,9 @@
  * the allocator metadata. The "memory" and "reserved" types are nicely
  * wrapped with struct memblock. This structure is statically
  * initialized at build time. The region arrays are initially sized to
- * %INIT_MEMBLOCK_REGIONS for "memory" and %INIT_MEMBLOCK_RESERVED_REGIONS
- * for "reserved". The region array for "physmem" is initially sized to
- * %INIT_PHYSMEM_REGIONS.
+ * %INIT_MEMBLOCK_MEMORY_REGIONS for "memory" and
+ * %INIT_MEMBLOCK_RESERVED_REGIONS for "reserved". The region array
+ * for "physmem" is initially sized to %INIT_PHYSMEM_REGIONS.
  * The memblock_allow_resize() enables automatic resizing of the region
  * arrays during addition of new regions. This feature should be used
  * with care so that memory allocated for the region array will not
@@ -102,7 +106,7 @@ unsigned long min_low_pfn;
 unsigned long max_pfn;
 unsigned long long max_possible_pfn;
 
-static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_memblock;
+static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_MEMORY_REGIONS] __initdata_memblock;
 static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_RESERVED_REGIONS] __initdata_memblock;
 #ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP
 static struct memblock_region memblock_physmem_init_regions[INIT_PHYSMEM_REGIONS];
@@ -111,7 +115,7 @@ static struct memblock_region memblock_physmem_init_regions[INIT_PHYSMEM_REGIONS
 struct memblock memblock __initdata_memblock = {
        .memory.regions         = memblock_memory_init_regions,
        .memory.cnt             = 1,    /* empty dummy entry */
-       .memory.max             = INIT_MEMBLOCK_REGIONS,
+       .memory.max             = INIT_MEMBLOCK_MEMORY_REGIONS,
        .memory.name            = "memory",
 
        .reserved.regions       = memblock_reserved_init_regions,
@@ -1348,8 +1352,8 @@ __next_mem_pfn_range_in_zone(u64 *idx, struct zone *zone,
  * from the regions with mirroring enabled and then retried from any
  * memory region.
  *
- * In addition, function sets the min_count to 0 using kmemleak_alloc_phys for
- * allocated boot memory block, so that it is never reported as leaks.
+ * In addition, function using kmemleak_alloc_phys for allocated boot
+ * memory block, it is never reported as leaks.
  *
  * Return:
  * Physical address of allocated memory block on success, %0 on failure.
@@ -1401,12 +1405,12 @@ done:
         */
        if (end != MEMBLOCK_ALLOC_NOLEAKTRACE)
                /*
-                * The min_count is set to 0 so that memblock allocated
-                * blocks are never reported as leaks. This is because many
-                * of these blocks are only referred via the physical
-                * address which is not looked up by kmemleak.
+                * Memblock allocated blocks are never reported as
+                * leaks. This is because many of these blocks are
+                * only referred via the physical address which is
+                * not looked up by kmemleak.
                 */
-               kmemleak_alloc_phys(found, size, 0, 0);
+               kmemleak_alloc_phys(found, size, 0);
 
        return found;
 }
index 618c366..b69979c 100644 (file)
@@ -626,7 +626,14 @@ static inline void memcg_rstat_updated(struct mem_cgroup *memcg, int val)
 
        x = __this_cpu_add_return(stats_updates, abs(val));
        if (x > MEMCG_CHARGE_BATCH) {
-               atomic_add(x / MEMCG_CHARGE_BATCH, &stats_flush_threshold);
+               /*
+                * If stats_flush_threshold exceeds the threshold
+                * (>num_online_cpus()), cgroup stats update will be triggered
+                * in __mem_cgroup_flush_stats(). Increasing this var further
+                * is redundant and simply adds overhead in atomic update.
+                */
+               if (atomic_read(&stats_flush_threshold) <= num_online_cpus())
+                       atomic_add(x / MEMCG_CHARGE_BATCH, &stats_flush_threshold);
                __this_cpu_write(stats_updates, 0);
        }
 }
@@ -783,7 +790,7 @@ void __mod_lruvec_kmem_state(void *p, enum node_stat_item idx, int val)
        struct lruvec *lruvec;
 
        rcu_read_lock();
-       memcg = mem_cgroup_from_obj(p);
+       memcg = mem_cgroup_from_slab_obj(p);
 
        /*
         * Untracked pages have no memcg, no lruvec. Update only the
@@ -1460,14 +1467,35 @@ static inline unsigned long memcg_page_state_output(struct mem_cgroup *memcg,
        return memcg_page_state(memcg, item) * memcg_page_state_unit(item);
 }
 
-static char *memory_stat_format(struct mem_cgroup *memcg)
+/* Subset of vm_event_item to report for memcg event stats */
+static const unsigned int memcg_vm_event_stat[] = {
+       PGSCAN_KSWAPD,
+       PGSCAN_DIRECT,
+       PGSTEAL_KSWAPD,
+       PGSTEAL_DIRECT,
+       PGFAULT,
+       PGMAJFAULT,
+       PGREFILL,
+       PGACTIVATE,
+       PGDEACTIVATE,
+       PGLAZYFREE,
+       PGLAZYFREED,
+#if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_ZSWAP)
+       ZSWPIN,
+       ZSWPOUT,
+#endif
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+       THP_FAULT_ALLOC,
+       THP_COLLAPSE_ALLOC,
+#endif
+};
+
+static void memory_stat_format(struct mem_cgroup *memcg, char *buf, int bufsize)
 {
        struct seq_buf s;
        int i;
 
-       seq_buf_init(&s, kmalloc(PAGE_SIZE, GFP_KERNEL), PAGE_SIZE);
-       if (!s.buffer)
-               return NULL;
+       seq_buf_init(&s, buf, bufsize);
 
        /*
         * Provide statistics on the state of the memory subsystem as
@@ -1495,46 +1523,20 @@ static char *memory_stat_format(struct mem_cgroup *memcg)
        }
 
        /* Accumulated memory events */
-
-       seq_buf_printf(&s, "%s %lu\n", vm_event_name(PGFAULT),
-                      memcg_events(memcg, PGFAULT));
-       seq_buf_printf(&s, "%s %lu\n", vm_event_name(PGMAJFAULT),
-                      memcg_events(memcg, PGMAJFAULT));
-       seq_buf_printf(&s, "%s %lu\n",  vm_event_name(PGREFILL),
-                      memcg_events(memcg, PGREFILL));
        seq_buf_printf(&s, "pgscan %lu\n",
                       memcg_events(memcg, PGSCAN_KSWAPD) +
                       memcg_events(memcg, PGSCAN_DIRECT));
        seq_buf_printf(&s, "pgsteal %lu\n",
                       memcg_events(memcg, PGSTEAL_KSWAPD) +
                       memcg_events(memcg, PGSTEAL_DIRECT));
-       seq_buf_printf(&s, "%s %lu\n", vm_event_name(PGACTIVATE),
-                      memcg_events(memcg, PGACTIVATE));
-       seq_buf_printf(&s, "%s %lu\n", vm_event_name(PGDEACTIVATE),
-                      memcg_events(memcg, PGDEACTIVATE));
-       seq_buf_printf(&s, "%s %lu\n", vm_event_name(PGLAZYFREE),
-                      memcg_events(memcg, PGLAZYFREE));
-       seq_buf_printf(&s, "%s %lu\n", vm_event_name(PGLAZYFREED),
-                      memcg_events(memcg, PGLAZYFREED));
-
-#if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_ZSWAP)
-       seq_buf_printf(&s, "%s %lu\n", vm_event_name(ZSWPIN),
-                      memcg_events(memcg, ZSWPIN));
-       seq_buf_printf(&s, "%s %lu\n", vm_event_name(ZSWPOUT),
-                      memcg_events(memcg, ZSWPOUT));
-#endif
 
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
-       seq_buf_printf(&s, "%s %lu\n", vm_event_name(THP_FAULT_ALLOC),
-                      memcg_events(memcg, THP_FAULT_ALLOC));
-       seq_buf_printf(&s, "%s %lu\n", vm_event_name(THP_COLLAPSE_ALLOC),
-                      memcg_events(memcg, THP_COLLAPSE_ALLOC));
-#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
+       for (i = 0; i < ARRAY_SIZE(memcg_vm_event_stat); i++)
+               seq_buf_printf(&s, "%s %lu\n",
+                              vm_event_name(memcg_vm_event_stat[i]),
+                              memcg_events(memcg, memcg_vm_event_stat[i]));
 
        /* The above should easily fit into one page */
        WARN_ON_ONCE(seq_buf_has_overflowed(&s));
-
-       return s.buffer;
 }
 
 #define K(x) ((x) << (PAGE_SHIFT-10))
@@ -1570,7 +1572,10 @@ void mem_cgroup_print_oom_context(struct mem_cgroup *memcg, struct task_struct *
  */
 void mem_cgroup_print_oom_meminfo(struct mem_cgroup *memcg)
 {
-       char *buf;
+       /* Use static buffer, for the caller is holding oom_lock. */
+       static char buf[PAGE_SIZE];
+
+       lockdep_assert_held(&oom_lock);
 
        pr_info("memory: usage %llukB, limit %llukB, failcnt %lu\n",
                K((u64)page_counter_read(&memcg->memory)),
@@ -1591,11 +1596,8 @@ void mem_cgroup_print_oom_meminfo(struct mem_cgroup *memcg)
        pr_info("Memory cgroup stats for ");
        pr_cont_cgroup_path(memcg->css.cgroup);
        pr_cont(":");
-       buf = memory_stat_format(memcg);
-       if (!buf)
-               return;
+       memory_stat_format(memcg, buf, sizeof(buf));
        pr_info("%s", buf);
-       kfree(buf);
 }
 
 /*
@@ -2331,7 +2333,8 @@ static unsigned long reclaim_high(struct mem_cgroup *memcg,
 
                psi_memstall_enter(&pflags);
                nr_reclaimed += try_to_free_mem_cgroup_pages(memcg, nr_pages,
-                                                            gfp_mask, true);
+                                                       gfp_mask,
+                                                       MEMCG_RECLAIM_MAY_SWAP);
                psi_memstall_leave(&pflags);
        } while ((memcg = parent_mem_cgroup(memcg)) &&
                 !mem_cgroup_is_root(memcg));
@@ -2576,8 +2579,9 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,
        struct page_counter *counter;
        unsigned long nr_reclaimed;
        bool passed_oom = false;
-       bool may_swap = true;
+       unsigned int reclaim_options = MEMCG_RECLAIM_MAY_SWAP;
        bool drained = false;
+       bool raised_max_event = false;
        unsigned long pflags;
 
 retry:
@@ -2593,7 +2597,7 @@ retry:
                mem_over_limit = mem_cgroup_from_counter(counter, memory);
        } else {
                mem_over_limit = mem_cgroup_from_counter(counter, memsw);
-               may_swap = false;
+               reclaim_options &= ~MEMCG_RECLAIM_MAY_SWAP;
        }
 
        if (batch > nr_pages) {
@@ -2617,10 +2621,11 @@ retry:
                goto nomem;
 
        memcg_memory_event(mem_over_limit, MEMCG_MAX);
+       raised_max_event = true;
 
        psi_memstall_enter(&pflags);
        nr_reclaimed = try_to_free_mem_cgroup_pages(mem_over_limit, nr_pages,
-                                                   gfp_mask, may_swap);
+                                                   gfp_mask, reclaim_options);
        psi_memstall_leave(&pflags);
 
        if (mem_cgroup_margin(mem_over_limit) >= nr_pages)
@@ -2683,6 +2688,13 @@ nomem:
        if (!(gfp_mask & (__GFP_NOFAIL | __GFP_HIGH)))
                return -ENOMEM;
 force:
+       /*
+        * If the allocation has to be enforced, don't forget to raise
+        * a MEMCG_MAX event.
+        */
+       if (!raised_max_event)
+               memcg_memory_event(mem_over_limit, MEMCG_MAX);
+
        /*
         * The allocation either can't fail or will lead to more memory
         * being freed very soon.  Allow memory usage go over the limit
@@ -2842,27 +2854,9 @@ int memcg_alloc_slab_cgroups(struct slab *slab, struct kmem_cache *s,
        return 0;
 }
 
-/*
- * Returns a pointer to the memory cgroup to which the kernel object is charged.
- *
- * A passed kernel object can be a slab object or a generic kernel page, so
- * different mechanisms for getting the memory cgroup pointer should be used.
- * In certain cases (e.g. kernel stacks or large kmallocs with SLUB) the caller
- * can not know for sure how the kernel object is implemented.
- * mem_cgroup_from_obj() can be safely used in such cases.
- *
- * The caller must ensure the memcg lifetime, e.g. by taking rcu_read_lock(),
- * cgroup_mutex, etc.
- */
-struct mem_cgroup *mem_cgroup_from_obj(void *p)
+static __always_inline
+struct mem_cgroup *mem_cgroup_from_obj_folio(struct folio *folio, void *p)
 {
-       struct folio *folio;
-
-       if (mem_cgroup_disabled())
-               return NULL;
-
-       folio = virt_to_folio(p);
-
        /*
         * Slab objects are accounted individually, not per-page.
         * Memcg membership data for each individual object is saved in
@@ -2895,6 +2889,53 @@ struct mem_cgroup *mem_cgroup_from_obj(void *p)
        return page_memcg_check(folio_page(folio, 0));
 }
 
+/*
+ * Returns a pointer to the memory cgroup to which the kernel object is charged.
+ *
+ * A passed kernel object can be a slab object, vmalloc object or a generic
+ * kernel page, so different mechanisms for getting the memory cgroup pointer
+ * should be used.
+ *
+ * In certain cases (e.g. kernel stacks or large kmallocs with SLUB) the caller
+ * can not know for sure how the kernel object is implemented.
+ * mem_cgroup_from_obj() can be safely used in such cases.
+ *
+ * The caller must ensure the memcg lifetime, e.g. by taking rcu_read_lock(),
+ * cgroup_mutex, etc.
+ */
+struct mem_cgroup *mem_cgroup_from_obj(void *p)
+{
+       struct folio *folio;
+
+       if (mem_cgroup_disabled())
+               return NULL;
+
+       if (unlikely(is_vmalloc_addr(p)))
+               folio = page_folio(vmalloc_to_page(p));
+       else
+               folio = virt_to_folio(p);
+
+       return mem_cgroup_from_obj_folio(folio, p);
+}
+
+/*
+ * Returns a pointer to the memory cgroup to which the kernel object is charged.
+ * Similar to mem_cgroup_from_obj(), but faster and not suitable for objects,
+ * allocated using vmalloc().
+ *
+ * A passed kernel object must be a slab object or a generic kernel page.
+ *
+ * The caller must ensure the memcg lifetime, e.g. by taking rcu_read_lock(),
+ * cgroup_mutex, etc.
+ */
+struct mem_cgroup *mem_cgroup_from_slab_obj(void *p)
+{
+       if (mem_cgroup_disabled())
+               return NULL;
+
+       return mem_cgroup_from_obj_folio(virt_to_folio(p), p);
+}
+
 static struct obj_cgroup *__get_obj_cgroup_from_memcg(struct mem_cgroup *memcg)
 {
        struct obj_cgroup *objcg = NULL;
@@ -3402,8 +3443,8 @@ static int mem_cgroup_resize_max(struct mem_cgroup *memcg,
                        continue;
                }
 
-               if (!try_to_free_mem_cgroup_pages(memcg, 1,
-                                       GFP_KERNEL, !memsw)) {
+               if (!try_to_free_mem_cgroup_pages(memcg, 1, GFP_KERNEL,
+                                       memsw ? 0 : MEMCG_RECLAIM_MAY_SWAP)) {
                        ret = -EBUSY;
                        break;
                }
@@ -3513,7 +3554,8 @@ static int mem_cgroup_force_empty(struct mem_cgroup *memcg)
                if (signal_pending(current))
                        return -EINTR;
 
-               if (!try_to_free_mem_cgroup_pages(memcg, 1, GFP_KERNEL, true))
+               if (!try_to_free_mem_cgroup_pages(memcg, 1, GFP_KERNEL,
+                                                 MEMCG_RECLAIM_MAY_SWAP))
                        nr_retries--;
        }
 
@@ -3625,7 +3667,7 @@ static int memcg_online_kmem(struct mem_cgroup *memcg)
 {
        struct obj_cgroup *objcg;
 
-       if (cgroup_memory_nokmem)
+       if (mem_cgroup_kmem_disabled())
                return 0;
 
        if (unlikely(mem_cgroup_is_root(memcg)))
@@ -3649,7 +3691,7 @@ static void memcg_offline_kmem(struct mem_cgroup *memcg)
 {
        struct mem_cgroup *parent;
 
-       if (cgroup_memory_nokmem)
+       if (mem_cgroup_kmem_disabled())
                return;
 
        if (unlikely(mem_cgroup_is_root(memcg)))
@@ -5060,6 +5102,29 @@ struct mem_cgroup *mem_cgroup_from_id(unsigned short id)
        return idr_find(&mem_cgroup_idr, id);
 }
 
+#ifdef CONFIG_SHRINKER_DEBUG
+struct mem_cgroup *mem_cgroup_get_from_ino(unsigned long ino)
+{
+       struct cgroup *cgrp;
+       struct cgroup_subsys_state *css;
+       struct mem_cgroup *memcg;
+
+       cgrp = cgroup_get_from_id(ino);
+       if (!cgrp)
+               return ERR_PTR(-ENOENT);
+
+       css = cgroup_get_e_css(cgrp, &memory_cgrp_subsys);
+       if (css)
+               memcg = container_of(css, struct mem_cgroup, css);
+       else
+               memcg = ERR_PTR(-ENOENT);
+
+       cgroup_put(cgrp);
+
+       return memcg;
+}
+#endif
+
 static int alloc_mem_cgroup_per_node_info(struct mem_cgroup *memcg, int node)
 {
        struct mem_cgroup_per_node *pn;
@@ -5665,8 +5730,8 @@ out:
  *   2(MC_TARGET_SWAP): if the swap entry corresponding to this pte is a
  *     target for charge migration. if @target is not NULL, the entry is stored
  *     in target->ent.
- *   3(MC_TARGET_DEVICE): like MC_TARGET_PAGE  but page is MEMORY_DEVICE_PRIVATE
- *     (so ZONE_DEVICE page and thus not on the lru).
+ *   3(MC_TARGET_DEVICE): like MC_TARGET_PAGE  but page is device memory and
+ *   thus not on the lru.
  *     For now we such page is charge like a regular page would be as for all
  *     intent and purposes it is just special memory taking the place of a
  *     regular page.
@@ -5704,7 +5769,8 @@ static enum mc_target_type get_mctgt_type(struct vm_area_struct *vma,
                 */
                if (page_memcg(page) == mc.from) {
                        ret = MC_TARGET_PAGE;
-                       if (is_device_private_page(page))
+                       if (is_device_private_page(page) ||
+                           is_device_coherent_page(page))
                                ret = MC_TARGET_DEVICE;
                        if (target)
                                target->page = page;
@@ -6241,7 +6307,7 @@ static ssize_t memory_high_write(struct kernfs_open_file *of,
                }
 
                reclaimed = try_to_free_mem_cgroup_pages(memcg, nr_pages - high,
-                                                        GFP_KERNEL, true);
+                                       GFP_KERNEL, MEMCG_RECLAIM_MAY_SWAP);
 
                if (!reclaimed && !nr_retries--)
                        break;
@@ -6290,7 +6356,7 @@ static ssize_t memory_max_write(struct kernfs_open_file *of,
 
                if (nr_reclaims) {
                        if (!try_to_free_mem_cgroup_pages(memcg, nr_pages - max,
-                                                         GFP_KERNEL, true))
+                                       GFP_KERNEL, MEMCG_RECLAIM_MAY_SWAP))
                                nr_reclaims--;
                        continue;
                }
@@ -6335,11 +6401,11 @@ static int memory_events_local_show(struct seq_file *m, void *v)
 static int memory_stat_show(struct seq_file *m, void *v)
 {
        struct mem_cgroup *memcg = mem_cgroup_from_seq(m);
-       char *buf;
+       char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
 
-       buf = memory_stat_format(memcg);
        if (!buf)
                return -ENOMEM;
+       memory_stat_format(memcg, buf, PAGE_SIZE);
        seq_puts(m, buf);
        kfree(buf);
        return 0;
@@ -6419,6 +6485,7 @@ static ssize_t memory_reclaim(struct kernfs_open_file *of, char *buf,
        struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of));
        unsigned int nr_retries = MAX_RECLAIM_RETRIES;
        unsigned long nr_to_reclaim, nr_reclaimed = 0;
+       unsigned int reclaim_options;
        int err;
 
        buf = strstrip(buf);
@@ -6426,6 +6493,7 @@ static ssize_t memory_reclaim(struct kernfs_open_file *of, char *buf,
        if (err)
                return err;
 
+       reclaim_options = MEMCG_RECLAIM_MAY_SWAP | MEMCG_RECLAIM_PROACTIVE;
        while (nr_reclaimed < nr_to_reclaim) {
                unsigned long reclaimed;
 
@@ -6442,7 +6510,7 @@ static ssize_t memory_reclaim(struct kernfs_open_file *of, char *buf,
 
                reclaimed = try_to_free_mem_cgroup_pages(memcg,
                                                nr_to_reclaim - nr_reclaimed,
-                                               GFP_KERNEL, true);
+                                               GFP_KERNEL, reclaim_options);
 
                if (!reclaimed && !nr_retries--)
                        return -EAGAIN;
index b864c2e..9a7a228 100644 (file)
@@ -33,6 +33,9 @@
  * are rare we hope to get away with this. This avoids impacting the core 
  * VM.
  */
+
+#define pr_fmt(fmt) "Memory failure: " fmt
+
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/page-flags.h>
@@ -252,7 +255,7 @@ static int kill_proc(struct to_kill *tk, unsigned long pfn, int flags)
        short addr_lsb = tk->size_shift;
        int ret = 0;
 
-       pr_err("Memory failure: %#lx: Sending SIGBUS to %s:%d due to hardware memory corruption\n",
+       pr_err("%#lx: Sending SIGBUS to %s:%d due to hardware memory corruption\n",
                        pfn, t->comm, t->pid);
 
        if ((flags & MF_ACTION_REQUIRED) && (t == current))
@@ -270,7 +273,7 @@ static int kill_proc(struct to_kill *tk, unsigned long pfn, int flags)
                ret = send_sig_mceerr(BUS_MCEERR_AO, (void __user *)tk->addr,
                                      addr_lsb, t);  /* synchronous? */
        if (ret < 0)
-               pr_info("Memory failure: Error sending signal to %s:%d: %d\n",
+               pr_info("Error sending signal to %s:%d: %d\n",
                        t->comm, t->pid, ret);
        return ret;
 }
@@ -297,10 +300,9 @@ void shake_page(struct page *p)
 }
 EXPORT_SYMBOL_GPL(shake_page);
 
-static unsigned long dev_pagemap_mapping_shift(struct page *page,
-               struct vm_area_struct *vma)
+static unsigned long dev_pagemap_mapping_shift(struct vm_area_struct *vma,
+               unsigned long address)
 {
-       unsigned long address = vma_address(page, vma);
        unsigned long ret = 0;
        pgd_t *pgd;
        p4d_t *p4d;
@@ -340,23 +342,33 @@ static unsigned long dev_pagemap_mapping_shift(struct page *page,
 /*
  * Schedule a process for later kill.
  * Uses GFP_ATOMIC allocations to avoid potential recursions in the VM.
+ *
+ * Notice: @fsdax_pgoff is used only when @p is a fsdax page.
+ *   In other cases, such as anonymous and file-backend page, the address to be
+ *   killed can be caculated by @p itself.
  */
 static void add_to_kill(struct task_struct *tsk, struct page *p,
-                      struct vm_area_struct *vma,
-                      struct list_head *to_kill)
+                       pgoff_t fsdax_pgoff, struct vm_area_struct *vma,
+                       struct list_head *to_kill)
 {
        struct to_kill *tk;
 
        tk = kmalloc(sizeof(struct to_kill), GFP_ATOMIC);
        if (!tk) {
-               pr_err("Memory failure: Out of memory while machine check handling\n");
+               pr_err("Out of memory while machine check handling\n");
                return;
        }
 
        tk->addr = page_address_in_vma(p, vma);
-       if (is_zone_device_page(p))
-               tk->size_shift = dev_pagemap_mapping_shift(p, vma);
-       else
+       if (is_zone_device_page(p)) {
+               /*
+                * Since page->mapping is not used for fsdax, we need
+                * calculate the address based on the vma.
+                */
+               if (p->pgmap->type == MEMORY_DEVICE_FS_DAX)
+                       tk->addr = vma_pgoff_address(fsdax_pgoff, 1, vma);
+               tk->size_shift = dev_pagemap_mapping_shift(vma, tk->addr);
+       } else
                tk->size_shift = page_shift(compound_head(p));
 
        /*
@@ -370,7 +382,7 @@ static void add_to_kill(struct task_struct *tsk, struct page *p,
         * has a mapping for the page.
         */
        if (tk->addr == -EFAULT) {
-               pr_info("Memory failure: Unable to find user space address %lx in %s\n",
+               pr_info("Unable to find user space address %lx in %s\n",
                        page_to_pfn(p), tsk->comm);
        } else if (tk->size_shift == 0) {
                kfree(tk);
@@ -403,7 +415,7 @@ static void kill_procs(struct list_head *to_kill, int forcekill, bool fail,
                         * signal and then access the memory. Just kill it.
                         */
                        if (fail || tk->addr == -EFAULT) {
-                               pr_err("Memory failure: %#lx: forcibly killing %s:%d because of failure to unmap corrupted page\n",
+                               pr_err("%#lx: forcibly killing %s:%d because of failure to unmap corrupted page\n",
                                       pfn, tk->tsk->comm, tk->tsk->pid);
                                do_send_sig_info(SIGKILL, SEND_SIG_PRIV,
                                                 tk->tsk, PIDTYPE_PID);
@@ -416,7 +428,7 @@ static void kill_procs(struct list_head *to_kill, int forcekill, bool fail,
                         * process anyways.
                         */
                        else if (kill_proc(tk, pfn, flags) < 0)
-                               pr_err("Memory failure: %#lx: Cannot send advisory machine check signal to %s:%d\n",
+                               pr_err("%#lx: Cannot send advisory machine check signal to %s:%d\n",
                                       pfn, tk->tsk->comm, tk->tsk->pid);
                }
                put_task_struct(tk->tsk);
@@ -505,7 +517,7 @@ static void collect_procs_anon(struct page *page, struct list_head *to_kill,
                        if (!page_mapped_in_vma(page, vma))
                                continue;
                        if (vma->vm_mm == t->mm)
-                               add_to_kill(t, page, vma, to_kill);
+                               add_to_kill(t, page, 0, vma, to_kill);
                }
        }
        read_unlock(&tasklist_lock);
@@ -541,12 +553,40 @@ static void collect_procs_file(struct page *page, struct list_head *to_kill,
                         * to be informed of all such data corruptions.
                         */
                        if (vma->vm_mm == t->mm)
-                               add_to_kill(t, page, vma, to_kill);
+                               add_to_kill(t, page, 0, vma, to_kill);
+               }
+       }
+       read_unlock(&tasklist_lock);
+       i_mmap_unlock_read(mapping);
+}
+
+#ifdef CONFIG_FS_DAX
+/*
+ * Collect processes when the error hit a fsdax page.
+ */
+static void collect_procs_fsdax(struct page *page,
+               struct address_space *mapping, pgoff_t pgoff,
+               struct list_head *to_kill)
+{
+       struct vm_area_struct *vma;
+       struct task_struct *tsk;
+
+       i_mmap_lock_read(mapping);
+       read_lock(&tasklist_lock);
+       for_each_process(tsk) {
+               struct task_struct *t = task_early_kill(tsk, true);
+
+               if (!t)
+                       continue;
+               vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff, pgoff) {
+                       if (vma->vm_mm == t->mm)
+                               add_to_kill(t, page, pgoff, vma, to_kill);
                }
        }
        read_unlock(&tasklist_lock);
        i_mmap_unlock_read(mapping);
 }
+#endif /* CONFIG_FS_DAX */
 
 /*
  * Collect the processes who have the corrupted page mapped to kill.
@@ -779,12 +819,10 @@ static int truncate_error_page(struct page *p, unsigned long pfn,
                int err = mapping->a_ops->error_remove_page(mapping, p);
 
                if (err != 0) {
-                       pr_info("Memory failure: %#lx: Failed to punch page: %d\n",
-                               pfn, err);
+                       pr_info("%#lx: Failed to punch page: %d\n", pfn, err);
                } else if (page_has_private(p) &&
                           !try_to_release_page(p, GFP_NOIO)) {
-                       pr_info("Memory failure: %#lx: failed to release buffers\n",
-                               pfn);
+                       pr_info("%#lx: failed to release buffers\n", pfn);
                } else {
                        ret = MF_RECOVERED;
                }
@@ -796,8 +834,7 @@ static int truncate_error_page(struct page *p, unsigned long pfn,
                if (invalidate_inode_page(p))
                        ret = MF_RECOVERED;
                else
-                       pr_info("Memory failure: %#lx: Failed to invalidate\n",
-                               pfn);
+                       pr_info("%#lx: Failed to invalidate\n", pfn);
        }
 
        return ret;
@@ -827,7 +864,7 @@ static bool has_extra_refcount(struct page_state *ps, struct page *p,
                count -= 1;
 
        if (count > 0) {
-               pr_err("Memory failure: %#lx: %s still referenced by %d users\n",
+               pr_err("%#lx: %s still referenced by %d users\n",
                       page_to_pfn(p), action_page_types[ps->type], count);
                return true;
        }
@@ -851,7 +888,7 @@ static int me_kernel(struct page_state *ps, struct page *p)
  */
 static int me_unknown(struct page_state *ps, struct page *p)
 {
-       pr_err("Memory failure: %#lx: Unknown page state\n", page_to_pfn(p));
+       pr_err("%#lx: Unknown page state\n", page_to_pfn(p));
        unlock_page(p);
        return MF_FAILED;
 }
@@ -1007,12 +1044,13 @@ static int me_swapcache_dirty(struct page_state *ps, struct page *p)
 
 static int me_swapcache_clean(struct page_state *ps, struct page *p)
 {
+       struct folio *folio = page_folio(p);
        int ret;
 
-       delete_from_swap_cache(p);
+       delete_from_swap_cache(folio);
 
        ret = delete_from_lru_cache(p) ? MF_FAILED : MF_RECOVERED;
-       unlock_page(p);
+       folio_unlock(folio);
 
        if (has_extra_refcount(ps, p, false))
                ret = MF_FAILED;
@@ -1135,7 +1173,7 @@ static void action_result(unsigned long pfn, enum mf_action_page_type type,
        trace_memory_failure_event(pfn, type, result);
 
        num_poisoned_pages_inc();
-       pr_err("Memory failure: %#lx: recovery action for %s: %s\n",
+       pr_err("%#lx: recovery action for %s: %s\n",
                pfn, action_page_types[type], action_name[result]);
 }
 
@@ -1210,8 +1248,7 @@ static int __get_hwpoison_page(struct page *page, unsigned long flags)
                if (head == compound_head(page))
                        return 1;
 
-               pr_info("Memory failure: %#lx cannot catch tail\n",
-                       page_to_pfn(page));
+               pr_info("%#lx cannot catch tail\n", page_to_pfn(page));
                put_page(head);
        }
 
@@ -1274,7 +1311,7 @@ try_again:
        }
 out:
        if (ret == -EIO)
-               pr_err("Memory failure: %#lx: unhandlable page.\n", page_to_pfn(p));
+               pr_err("%#lx: unhandlable page.\n", page_to_pfn(p));
 
        return ret;
 }
@@ -1373,13 +1410,12 @@ static bool hwpoison_user_mappings(struct page *p, unsigned long pfn,
                return true;
 
        if (PageKsm(p)) {
-               pr_err("Memory failure: %#lx: can't handle KSM pages.\n", pfn);
+               pr_err("%#lx: can't handle KSM pages.\n", pfn);
                return false;
        }
 
        if (PageSwapCache(p)) {
-               pr_err("Memory failure: %#lx: keeping poisoned page in swap cache\n",
-                       pfn);
+               pr_err("%#lx: keeping poisoned page in swap cache\n", pfn);
                ttu |= TTU_IGNORE_HWPOISON;
        }
 
@@ -1397,7 +1433,7 @@ static bool hwpoison_user_mappings(struct page *p, unsigned long pfn,
                } else {
                        kill = 0;
                        ttu |= TTU_IGNORE_HWPOISON;
-                       pr_info("Memory failure: %#lx: corrupted page was clean: dropped without side effects\n",
+                       pr_info("%#lx: corrupted page was clean: dropped without side effects\n",
                                pfn);
                }
        }
@@ -1426,14 +1462,14 @@ static bool hwpoison_user_mappings(struct page *p, unsigned long pfn,
                        try_to_unmap(folio, ttu|TTU_RMAP_LOCKED);
                        i_mmap_unlock_write(mapping);
                } else
-                       pr_info("Memory failure: %#lx: could not lock mapping for mapped huge page\n", pfn);
+                       pr_info("%#lx: could not lock mapping for mapped huge page\n", pfn);
        } else {
                try_to_unmap(folio, ttu);
        }
 
        unmap_success = !page_mapped(hpage);
        if (!unmap_success)
-               pr_err("Memory failure: %#lx: failed to unmap page (mapcount=%d)\n",
+               pr_err("%#lx: failed to unmap page (mapcount=%d)\n",
                       pfn, page_mapcount(hpage));
 
        /*
@@ -1498,6 +1534,134 @@ static int try_to_split_thp_page(struct page *page, const char *msg)
        return 0;
 }
 
+static void unmap_and_kill(struct list_head *to_kill, unsigned long pfn,
+               struct address_space *mapping, pgoff_t index, int flags)
+{
+       struct to_kill *tk;
+       unsigned long size = 0;
+
+       list_for_each_entry(tk, to_kill, nd)
+               if (tk->size_shift)
+                       size = max(size, 1UL << tk->size_shift);
+
+       if (size) {
+               /*
+                * Unmap the largest mapping to avoid breaking up device-dax
+                * mappings which are constant size. The actual size of the
+                * mapping being torn down is communicated in siginfo, see
+                * kill_proc()
+                */
+               loff_t start = (index << PAGE_SHIFT) & ~(size - 1);
+
+               unmap_mapping_range(mapping, start, size, 0);
+       }
+
+       kill_procs(to_kill, flags & MF_MUST_KILL, false, pfn, flags);
+}
+
+static int mf_generic_kill_procs(unsigned long long pfn, int flags,
+               struct dev_pagemap *pgmap)
+{
+       struct page *page = pfn_to_page(pfn);
+       LIST_HEAD(to_kill);
+       dax_entry_t cookie;
+       int rc = 0;
+
+       /*
+        * Pages instantiated by device-dax (not filesystem-dax)
+        * may be compound pages.
+        */
+       page = compound_head(page);
+
+       /*
+        * Prevent the inode from being freed while we are interrogating
+        * the address_space, typically this would be handled by
+        * lock_page(), but dax pages do not use the page lock. This
+        * also prevents changes to the mapping of this pfn until
+        * poison signaling is complete.
+        */
+       cookie = dax_lock_page(page);
+       if (!cookie)
+               return -EBUSY;
+
+       if (hwpoison_filter(page)) {
+               rc = -EOPNOTSUPP;
+               goto unlock;
+       }
+
+       switch (pgmap->type) {
+       case MEMORY_DEVICE_PRIVATE:
+       case MEMORY_DEVICE_COHERENT:
+               /*
+                * TODO: Handle device pages which may need coordination
+                * with device-side memory.
+                */
+               rc = -ENXIO;
+               goto unlock;
+       default:
+               break;
+       }
+
+       /*
+        * Use this flag as an indication that the dax page has been
+        * remapped UC to prevent speculative consumption of poison.
+        */
+       SetPageHWPoison(page);
+
+       /*
+        * Unlike System-RAM there is no possibility to swap in a
+        * different physical page at a given virtual address, so all
+        * userspace consumption of ZONE_DEVICE memory necessitates
+        * SIGBUS (i.e. MF_MUST_KILL)
+        */
+       flags |= MF_ACTION_REQUIRED | MF_MUST_KILL;
+       collect_procs(page, &to_kill, true);
+
+       unmap_and_kill(&to_kill, pfn, page->mapping, page->index, flags);
+unlock:
+       dax_unlock_page(page, cookie);
+       return rc;
+}
+
+#ifdef CONFIG_FS_DAX
+/**
+ * mf_dax_kill_procs - Collect and kill processes who are using this file range
+ * @mapping:   address_space of the file in use
+ * @index:     start pgoff of the range within the file
+ * @count:     length of the range, in unit of PAGE_SIZE
+ * @mf_flags:  memory failure flags
+ */
+int mf_dax_kill_procs(struct address_space *mapping, pgoff_t index,
+               unsigned long count, int mf_flags)
+{
+       LIST_HEAD(to_kill);
+       dax_entry_t cookie;
+       struct page *page;
+       size_t end = index + count;
+
+       mf_flags |= MF_ACTION_REQUIRED | MF_MUST_KILL;
+
+       for (; index < end; index++) {
+               page = NULL;
+               cookie = dax_lock_mapping_entry(mapping, index, &page);
+               if (!cookie)
+                       return -EBUSY;
+               if (!page)
+                       goto unlock;
+
+               SetPageHWPoison(page);
+
+               collect_procs_fsdax(page, mapping, index, &to_kill);
+               unmap_and_kill(&to_kill, page_to_pfn(page), mapping,
+                               index, mf_flags);
+unlock:
+               dax_unlock_mapping_entry(mapping, index, cookie);
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mf_dax_kill_procs);
+#endif /* CONFIG_FS_DAX */
+
 /*
  * Called from hugetlb code with hugetlb_lock held.
  *
@@ -1566,7 +1730,7 @@ retry:
                *hugetlb = 0;
                return 0;
        } else if (res == -EHWPOISON) {
-               pr_err("Memory failure: %#lx: already hardware poisoned\n", pfn);
+               pr_err("%#lx: already hardware poisoned\n", pfn);
                if (flags & MF_ACTION_REQUIRED) {
                        head = compound_head(p);
                        res = kill_accessing_process(current, page_to_pfn(head), flags);
@@ -1633,23 +1797,20 @@ out:
        unlock_page(head);
        return res;
 }
+
 #else
 static inline int try_memory_failure_hugetlb(unsigned long pfn, int flags, int *hugetlb)
 {
        return 0;
 }
-#endif
+
+#endif /* CONFIG_HUGETLB_PAGE */
 
 static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
                struct dev_pagemap *pgmap)
 {
        struct page *page = pfn_to_page(pfn);
-       unsigned long size = 0;
-       struct to_kill *tk;
-       LIST_HEAD(tokill);
-       int rc = -EBUSY;
-       loff_t start;
-       dax_entry_t cookie;
+       int rc = -ENXIO;
 
        if (flags & MF_COUNT_INCREASED)
                /*
@@ -1658,73 +1819,24 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
                put_page(page);
 
        /* device metadata space is not recoverable */
-       if (!pgmap_pfn_valid(pgmap, pfn)) {
-               rc = -ENXIO;
+       if (!pgmap_pfn_valid(pgmap, pfn))
                goto out;
-       }
 
        /*
-        * Pages instantiated by device-dax (not filesystem-dax)
-        * may be compound pages.
+        * Call driver's implementation to handle the memory failure, otherwise
+        * fall back to generic handler.
         */
-       page = compound_head(page);
-
-       /*
-        * Prevent the inode from being freed while we are interrogating
-        * the address_space, typically this would be handled by
-        * lock_page(), but dax pages do not use the page lock. This
-        * also prevents changes to the mapping of this pfn until
-        * poison signaling is complete.
-        */
-       cookie = dax_lock_page(page);
-       if (!cookie)
-               goto out;
-
-       if (hwpoison_filter(page)) {
-               rc = -EOPNOTSUPP;
-               goto unlock;
-       }
-
-       if (pgmap->type == MEMORY_DEVICE_PRIVATE) {
+       if (pgmap->ops->memory_failure) {
+               rc = pgmap->ops->memory_failure(pgmap, pfn, 1, flags);
                /*
-                * TODO: Handle HMM pages which may need coordination
-                * with device-side memory.
+                * Fall back to generic handler too if operation is not
+                * supported inside the driver/device/filesystem.
                 */
-               goto unlock;
+               if (rc != -EOPNOTSUPP)
+                       goto out;
        }
 
-       /*
-        * Use this flag as an indication that the dax page has been
-        * remapped UC to prevent speculative consumption of poison.
-        */
-       SetPageHWPoison(page);
-
-       /*
-        * Unlike System-RAM there is no possibility to swap in a
-        * different physical page at a given virtual address, so all
-        * userspace consumption of ZONE_DEVICE memory necessitates
-        * SIGBUS (i.e. MF_MUST_KILL)
-        */
-       flags |= MF_ACTION_REQUIRED | MF_MUST_KILL;
-       collect_procs(page, &tokill, true);
-
-       list_for_each_entry(tk, &tokill, nd)
-               if (tk->size_shift)
-                       size = max(size, 1UL << tk->size_shift);
-       if (size) {
-               /*
-                * Unmap the largest mapping to avoid breaking up
-                * device-dax mappings which are constant size. The
-                * actual size of the mapping being torn down is
-                * communicated in siginfo, see kill_proc()
-                */
-               start = (page->index << PAGE_SHIFT) & ~(size - 1);
-               unmap_mapping_range(page->mapping, start, size, 0);
-       }
-       kill_procs(&tokill, true, false, pfn, flags);
-       rc = 0;
-unlock:
-       dax_unlock_page(page, cookie);
+       rc = mf_generic_kill_procs(pfn, flags, pgmap);
 out:
        /* drop pgmap ref acquired in caller */
        put_dev_pagemap(pgmap);
@@ -1787,8 +1899,7 @@ int memory_failure(unsigned long pfn, int flags)
                                goto unlock_mutex;
                        }
                }
-               pr_err("Memory failure: %#lx: memory outside kernel control\n",
-                       pfn);
+               pr_err("%#lx: memory outside kernel control\n", pfn);
                res = -ENXIO;
                goto unlock_mutex;
        }
@@ -1799,8 +1910,7 @@ try_again:
                goto unlock_mutex;
 
        if (TestSetPageHWPoison(p)) {
-               pr_err("Memory failure: %#lx: already hardware poisoned\n",
-                       pfn);
+               pr_err("%#lx: already hardware poisoned\n", pfn);
                res = -EHWPOISON;
                if (flags & MF_ACTION_REQUIRED)
                        res = kill_accessing_process(current, pfn, flags);
@@ -2016,7 +2126,7 @@ void memory_failure_queue(unsigned long pfn, int flags)
        if (kfifo_put(&mf_cpu->fifo, entry))
                schedule_work_on(smp_processor_id(), &mf_cpu->work);
        else
-               pr_err("Memory failure: buffer overflow when queuing memory failure at %#lx\n",
+               pr_err("buffer overflow when queuing memory failure at %#lx\n",
                       pfn);
        spin_unlock_irqrestore(&mf_cpu->lock, proc_flags);
        put_cpu_var(memory_failure_cpu);
@@ -2073,6 +2183,8 @@ static int __init memory_failure_init(void)
 }
 core_initcall(memory_failure_init);
 
+#undef pr_fmt
+#define pr_fmt(fmt)    "" fmt
 #define unpoison_pr_info(fmt, pfn, rs)                 \
 ({                                                     \
        if (__ratelimit(rs))                            \
@@ -2178,7 +2290,7 @@ static bool isolate_page(struct page *page, struct list_head *pagelist)
        bool lru = PageLRU(page);
 
        if (PageHuge(page)) {
-               isolated = isolate_huge_page(page, pagelist);
+               isolated = !isolate_hugetlb(page, pagelist);
        } else {
                if (lru)
                        isolated = !isolate_lru_page(page);
index 1c6027a..4ba73f5 100644 (file)
@@ -624,6 +624,14 @@ struct page *vm_normal_page(struct vm_area_struct *vma, unsigned long addr,
                if (is_zero_pfn(pfn))
                        return NULL;
                if (pte_devmap(pte))
+               /*
+                * NOTE: New users of ZONE_DEVICE will not set pte_devmap()
+                * and will have refcounts incremented on their struct pages
+                * when they are inserted into PTEs, thus they are safe to
+                * return here. Legacy ZONE_DEVICE pages that set pte_devmap()
+                * do not have refcounts. Example of legacy ZONE_DEVICE is
+                * MEMORY_DEVICE_FS_DAX type in pmem or virtio_fs drivers.
+                */
                        return NULL;
 
                print_bad_pte(vma, addr, pte, NULL);
@@ -736,7 +744,7 @@ static void restore_exclusive_pte(struct vm_area_struct *vma,
                 * Currently device exclusive access only supports anonymous
                 * memory so the entry shouldn't point to a filebacked page.
                 */
-               WARN_ON_ONCE(!PageAnon(page));
+               WARN_ON_ONCE(1);
 
        set_pte_at(vma->vm_mm, address, ptep, pte);
 
@@ -1245,7 +1253,7 @@ vma_needs_copy(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma)
        if (userfaultfd_wp(dst_vma))
                return true;
 
-       if (src_vma->vm_flags & (VM_HUGETLB | VM_PFNMAP | VM_MIXEDMAP))
+       if (src_vma->vm_flags & (VM_PFNMAP | VM_MIXEDMAP))
                return true;
 
        if (src_vma->anon_vma)
@@ -3020,7 +3028,7 @@ static vm_fault_t fault_dirty_shared_page(struct vm_fault *vmf)
                balance_dirty_pages_ratelimited(mapping);
                if (fpin) {
                        fput(fpin);
-                       return VM_FAULT_RETRY;
+                       return VM_FAULT_COMPLETED;
                }
        }
 
@@ -4434,10 +4442,6 @@ late_initcall(fault_around_debugfs);
  * It uses vm_ops->map_pages() to map the pages, which skips the page if it's
  * not ready to be mapped: not up-to-date, locked, etc.
  *
- * This function is called with the page table lock taken. In the split ptlock
- * case the page table lock only protects only those entries which belong to
- * the page table corresponding to the fault address.
- *
  * This function doesn't cross the VMA boundaries, in order to call map_pages()
  * only once.
  *
@@ -4696,7 +4700,7 @@ static vm_fault_t do_numa_page(struct vm_fault *vmf)
        pte = pte_modify(old_pte, vma->vm_page_prot);
 
        page = vm_normal_page(vma, vmf->address, pte);
-       if (!page)
+       if (!page || is_zone_device_page(page))
                goto out_map;
 
        /* TODO: handle PTE-mapped THP */
@@ -4966,6 +4970,7 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
                .gfp_mask = __get_fault_gfp_mask(vma),
        };
        struct mm_struct *mm = vma->vm_mm;
+       unsigned long vm_flags = vma->vm_flags;
        pgd_t *pgd;
        p4d_t *p4d;
        vm_fault_t ret;
@@ -4979,7 +4984,8 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
        if (!vmf.pud)
                return VM_FAULT_OOM;
 retry_pud:
-       if (pud_none(*vmf.pud) && __transparent_hugepage_enabled(vma)) {
+       if (pud_none(*vmf.pud) &&
+           hugepage_vma_check(vma, vm_flags, false, true)) {
                ret = create_huge_pud(&vmf);
                if (!(ret & VM_FAULT_FALLBACK))
                        return ret;
@@ -5012,7 +5018,8 @@ retry_pud:
        if (pud_trans_unstable(vmf.pud))
                goto retry_pud;
 
-       if (pmd_none(*vmf.pmd) && __transparent_hugepage_enabled(vma)) {
+       if (pmd_none(*vmf.pmd) &&
+           hugepage_vma_check(vma, vm_flags, false, true)) {
                ret = create_huge_pmd(&vmf);
                if (!(ret & VM_FAULT_FALLBACK))
                        return ret;
index 1213d0c..fad6d1f 100644 (file)
 #include "shuffle.h"
 
 #ifdef CONFIG_MHP_MEMMAP_ON_MEMORY
-static int memmap_on_memory_set(const char *val, const struct kernel_param *kp)
-{
-       if (hugetlb_optimize_vmemmap_enabled())
-               return 0;
-       return param_set_bool(val, kp);
-}
-
-static const struct kernel_param_ops memmap_on_memory_ops = {
-       .flags  = KERNEL_PARAM_OPS_FL_NOARG,
-       .set    = memmap_on_memory_set,
-       .get    = param_get_bool,
-};
-
 /*
  * memory_hotplug.memmap_on_memory parameter
  */
 static bool memmap_on_memory __ro_after_init;
-module_param_cb(memmap_on_memory, &memmap_on_memory_ops, &memmap_on_memory, 0444);
+module_param(memmap_on_memory, bool, 0444);
 MODULE_PARM_DESC(memmap_on_memory, "Enable memmap on memory for memory hotplug");
 
-bool mhp_memmap_on_memory(void)
+static inline bool mhp_memmap_on_memory(void)
 {
        return memmap_on_memory;
 }
+#else
+static inline bool mhp_memmap_on_memory(void)
+{
+       return false;
+}
 #endif
 
 enum {
@@ -237,8 +229,7 @@ static void release_memory_resource(struct resource *res)
        kfree(res);
 }
 
-static int check_pfn_span(unsigned long pfn, unsigned long nr_pages,
-               const char *reason)
+static int check_pfn_span(unsigned long pfn, unsigned long nr_pages)
 {
        /*
         * Disallow all operations smaller than a sub-section and only
@@ -255,12 +246,8 @@ static int check_pfn_span(unsigned long pfn, unsigned long nr_pages,
                min_align = PAGES_PER_SUBSECTION;
        else
                min_align = PAGES_PER_SECTION;
-       if (!IS_ALIGNED(pfn, min_align)
-                       || !IS_ALIGNED(nr_pages, min_align)) {
-               WARN(1, "Misaligned __%s_pages start: %#lx end: #%lx\n",
-                               reason, pfn, pfn + nr_pages - 1);
+       if (!IS_ALIGNED(pfn | nr_pages, min_align))
                return -EINVAL;
-       }
        return 0;
 }
 
@@ -337,9 +324,10 @@ int __ref __add_pages(int nid, unsigned long pfn, unsigned long nr_pages,
                altmap->alloc = 0;
        }
 
-       err = check_pfn_span(pfn, nr_pages, "add");
-       if (err)
-               return err;
+       if (check_pfn_span(pfn, nr_pages)) {
+               WARN(1, "Misaligned %s start: %#lx end: #%lx\n", __func__, pfn, pfn + nr_pages - 1);
+               return -EINVAL;
+       }
 
        for (; pfn < end_pfn; pfn += cur_nr_pages) {
                /* Select all remaining pages up to the next section boundary */
@@ -536,8 +524,10 @@ void __remove_pages(unsigned long pfn, unsigned long nr_pages,
 
        map_offset = vmem_altmap_offset(altmap);
 
-       if (check_pfn_span(pfn, nr_pages, "remove"))
+       if (check_pfn_span(pfn, nr_pages)) {
+               WARN(1, "Misaligned %s start: %#lx end: #%lx\n", __func__, pfn, pfn + nr_pages - 1);
                return;
+       }
 
        for (; pfn < end_pfn; pfn += cur_nr_pages) {
                cond_resched();
@@ -672,12 +662,18 @@ static void __meminit resize_pgdat_range(struct pglist_data *pgdat, unsigned lon
 
 }
 
+#ifdef CONFIG_ZONE_DEVICE
 static void section_taint_zone_device(unsigned long pfn)
 {
        struct mem_section *ms = __pfn_to_section(pfn);
 
        ms->section_mem_map |= SECTION_TAINT_ZONE_DEVICE;
 }
+#else
+static inline void section_taint_zone_device(unsigned long pfn)
+{
+}
+#endif
 
 /*
  * Associate the pfn range with the given zone, initializing the memmaps
@@ -936,7 +932,7 @@ static struct zone *auto_movable_zone_for_pfn(int nid,
                        if (!page)
                                continue;
                        /* If anything is !MOVABLE online the rest !MOVABLE. */
-                       if (page_zonenum(page) != ZONE_MOVABLE)
+                       if (!is_zone_movable_page(page))
                                goto kernel_zone;
                        online_pages += PAGES_PER_SECTION;
                }
@@ -1031,7 +1027,7 @@ int mhp_init_memmap_on_memory(unsigned long pfn, unsigned long nr_pages,
                              struct zone *zone)
 {
        unsigned long end_pfn = pfn + nr_pages;
-       int ret;
+       int ret, i;
 
        ret = kasan_add_zero_shadow(__va(PFN_PHYS(pfn)), PFN_PHYS(nr_pages));
        if (ret)
@@ -1039,6 +1035,9 @@ int mhp_init_memmap_on_memory(unsigned long pfn, unsigned long nr_pages,
 
        move_pfn_range_to_zone(zone, pfn, nr_pages, NULL, MIGRATE_UNMOVABLE);
 
+       for (i = 0; i < nr_pages; i++)
+               SetPageVmemmapSelfHosted(pfn_to_page(pfn + i));
+
        /*
         * It might be that the vmemmap_pages fully span sections. If that is
         * the case, mark those sections online here as otherwise they will be
@@ -1643,7 +1642,7 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
 
                if (PageHuge(page)) {
                        pfn = page_to_pfn(head) + compound_nr(head) - 1;
-                       isolate_huge_page(head, &source);
+                       isolate_hugetlb(head, &source);
                        continue;
                } else if (PageTransHuge(page))
                        pfn = page_to_pfn(head) + thp_nr_pages(page) - 1;
index d39b01f..b73d324 100644 (file)
@@ -465,9 +465,8 @@ static int queue_pages_pmd(pmd_t *pmd, spinlock_t *ptl, unsigned long addr,
        }
        page = pmd_page(*pmd);
        if (is_huge_zero_page(page)) {
-               spin_unlock(ptl);
                walk->action = ACTION_CONTINUE;
-               goto out;
+               goto unlock;
        }
        if (!queue_pages_required(page, qp))
                goto unlock;
@@ -484,7 +483,6 @@ static int queue_pages_pmd(pmd_t *pmd, spinlock_t *ptl, unsigned long addr,
                ret = -EIO;
 unlock:
        spin_unlock(ptl);
-out:
        return ret;
 }
 
@@ -523,7 +521,7 @@ static int queue_pages_pte_range(pmd_t *pmd, unsigned long addr,
                if (!pte_present(*pte))
                        continue;
                page = vm_normal_page(vma, addr, *pte);
-               if (!page)
+               if (!page || is_zone_device_page(page))
                        continue;
                /*
                 * vm_normal_page() filters out zero pages, but there might
@@ -602,7 +600,7 @@ static int queue_pages_hugetlb(pte_t *pte, unsigned long hmask,
        /* With MPOL_MF_MOVE, we migrate only unshared hugepage. */
        if (flags & (MPOL_MF_MOVE_ALL) ||
            (flags & MPOL_MF_MOVE && page_mapcount(page) == 1)) {
-               if (!isolate_huge_page(page, qp->pagelist) &&
+               if (isolate_hugetlb(page, qp->pagelist) &&
                        (flags & MPOL_MF_STRICT))
                        /*
                         * Failed to isolate page but allow migrating pages
@@ -1388,7 +1386,7 @@ static int get_nodes(nodemask_t *nodes, const unsigned long __user *nmask,
                unsigned long bits = min_t(unsigned long, maxnode, BITS_PER_LONG);
                unsigned long t;
 
-               if (get_bitmap(&t, &nmask[maxnode / BITS_PER_LONG], bits))
+               if (get_bitmap(&t, &nmask[(maxnode - 1) / BITS_PER_LONG], bits))
                        return -EFAULT;
 
                if (maxnode - bits >= MAX_NUMNODES) {
index b933d0f..96488b1 100644 (file)
@@ -379,7 +379,7 @@ void *mempool_alloc(mempool_t *pool, gfp_t gfp_mask)
        gfp_t gfp_temp;
 
        VM_WARN_ON_ONCE(gfp_mask & __GFP_ZERO);
-       might_sleep_if(gfp_mask & __GFP_DIRECT_RECLAIM);
+       might_alloc(gfp_mask);
 
        gfp_mask |= __GFP_NOMEMALLOC;   /* don't allocate emergency reserves */
        gfp_mask |= __GFP_NORETRY;      /* don't loop in __alloc_pages */
index 745eea0..58b20c3 100644 (file)
@@ -141,10 +141,10 @@ void memunmap_pages(struct dev_pagemap *pgmap)
        for (i = 0; i < pgmap->nr_range; i++)
                percpu_ref_put_many(&pgmap->ref, pfn_len(pgmap, i));
        wait_for_completion(&pgmap->done);
-       percpu_ref_exit(&pgmap->ref);
 
        for (i = 0; i < pgmap->nr_range; i++)
                pageunmap_range(pgmap, i);
+       percpu_ref_exit(&pgmap->ref);
 
        WARN_ONCE(pgmap->altmap.alloc, "failed to free all reserved pages\n");
        devmap_managed_enable_put(pgmap);
@@ -279,8 +279,8 @@ err_pfn_remap:
 
 
 /*
- * Not device managed version of dev_memremap_pages, undone by
- * memunmap_pages().  Please use dev_memremap_pages if you have a struct
+ * Not device managed version of devm_memremap_pages, undone by
+ * memunmap_pages().  Please use devm_memremap_pages if you have a struct
  * device available.
  */
 void *memremap_pages(struct dev_pagemap *pgmap, int nid)
@@ -315,6 +315,16 @@ void *memremap_pages(struct dev_pagemap *pgmap, int nid)
                        return ERR_PTR(-EINVAL);
                }
                break;
+       case MEMORY_DEVICE_COHERENT:
+               if (!pgmap->ops->page_free) {
+                       WARN(1, "Missing page_free method\n");
+                       return ERR_PTR(-EINVAL);
+               }
+               if (!pgmap->owner) {
+                       WARN(1, "Missing owner\n");
+                       return ERR_PTR(-EINVAL);
+               }
+               break;
        case MEMORY_DEVICE_FS_DAX:
                if (IS_ENABLED(CONFIG_FS_DAX_LIMITED)) {
                        WARN(1, "File system DAX not supported\n");
index 1b4b977..6a1597c 100644 (file)
@@ -132,7 +132,7 @@ static void putback_movable_page(struct page *page)
  *
  * This function shall be used whenever the isolated pageset has been
  * built from lru, balloon, hugetlbfs page. See isolate_migratepages_range()
- * and isolate_huge_page().
+ * and isolate_hugetlb().
  */
 void putback_movable_pages(struct list_head *l)
 {
@@ -314,13 +314,28 @@ void migration_entry_wait(struct mm_struct *mm, pmd_t *pmd,
        __migration_entry_wait(mm, ptep, ptl);
 }
 
-void migration_entry_wait_huge(struct vm_area_struct *vma,
-               struct mm_struct *mm, pte_t *pte)
+#ifdef CONFIG_HUGETLB_PAGE
+void __migration_entry_wait_huge(pte_t *ptep, spinlock_t *ptl)
 {
-       spinlock_t *ptl = huge_pte_lockptr(hstate_vma(vma), mm, pte);
-       __migration_entry_wait(mm, pte, ptl);
+       pte_t pte;
+
+       spin_lock(ptl);
+       pte = huge_ptep_get(ptep);
+
+       if (unlikely(!is_hugetlb_entry_migration(pte)))
+               spin_unlock(ptl);
+       else
+               migration_entry_wait_on_locked(pte_to_swp_entry(pte), NULL, ptl);
 }
 
+void migration_entry_wait_huge(struct vm_area_struct *vma, pte_t *pte)
+{
+       spinlock_t *ptl = huge_pte_lockptr(hstate_vma(vma), vma->vm_mm, pte);
+
+       __migration_entry_wait_huge(pte, ptl);
+}
+#endif
+
 #ifdef CONFIG_ARCH_ENABLE_THP_MIGRATION
 void pmd_migration_entry_wait(struct mm_struct *mm, pmd_t *pmd)
 {
@@ -1132,15 +1147,10 @@ static int unmap_and_move(new_page_t get_new_page,
                return -ENOSYS;
 
        if (page_count(page) == 1) {
-               /* page was freed from under us. So we are done. */
+               /* Page was freed from under us. So we are done. */
                ClearPageActive(page);
                ClearPageUnevictable(page);
-               if (unlikely(__PageMovable(page))) {
-                       lock_page(page);
-                       if (!PageMovable(page))
-                               ClearPageIsolated(page);
-                       unlock_page(page);
-               }
+               /* free_pages_prepare() will clear PG_isolated. */
                goto out;
        }
 
@@ -1662,7 +1672,7 @@ static int add_page_for_migration(struct mm_struct *mm, unsigned long addr,
                goto out;
 
        err = -ENOENT;
-       if (!page)
+       if (!page || is_zone_device_page(page))
                goto out;
 
        err = 0;
@@ -1675,8 +1685,9 @@ static int add_page_for_migration(struct mm_struct *mm, unsigned long addr,
 
        if (PageHuge(page)) {
                if (PageHead(page)) {
-                       isolate_huge_page(page, pagelist);
-                       err = 1;
+                       err = isolate_hugetlb(page, pagelist);
+                       if (!err)
+                               err = 1;
                }
        } else {
                struct page *head;
@@ -1852,7 +1863,7 @@ static void do_pages_stat_array(struct mm_struct *mm, unsigned long nr_pages,
                if (IS_ERR(page))
                        goto set_status;
 
-               if (page) {
+               if (page && !is_zone_device_page(page)) {
                        err = page_to_nid(page);
                        put_page(page);
                } else {
index 5dd97c3..27fb37d 100644 (file)
@@ -148,15 +148,21 @@ again:
                        if (is_writable_device_private_entry(entry))
                                mpfn |= MIGRATE_PFN_WRITE;
                } else {
-                       if (!(migrate->flags & MIGRATE_VMA_SELECT_SYSTEM))
-                               goto next;
                        pfn = pte_pfn(pte);
-                       if (is_zero_pfn(pfn)) {
+                       if (is_zero_pfn(pfn) &&
+                           (migrate->flags & MIGRATE_VMA_SELECT_SYSTEM)) {
                                mpfn = MIGRATE_PFN_MIGRATE;
                                migrate->cpages++;
                                goto next;
                        }
                        page = vm_normal_page(migrate->vma, addr, pte);
+                       if (page && !is_zone_device_page(page) &&
+                           !(migrate->flags & MIGRATE_VMA_SELECT_SYSTEM))
+                               goto next;
+                       else if (page && is_device_coherent_page(page) &&
+                           (!(migrate->flags & MIGRATE_VMA_SELECT_DEVICE_COHERENT) ||
+                            page->pgmap->owner != migrate->pgmap_owner))
+                               goto next;
                        mpfn = migrate_pfn(pfn) | MIGRATE_PFN_MIGRATE;
                        mpfn |= pte_write(pte) ? MIGRATE_PFN_WRITE : 0;
                }
@@ -518,7 +524,7 @@ EXPORT_SYMBOL(migrate_vma_setup);
  *     handle_pte_fault()
  *       do_anonymous_page()
  * to map in an anonymous zero page but the struct page will be a ZONE_DEVICE
- * private page.
+ * private or coherent page.
  */
 static void migrate_vma_insert_page(struct migrate_vma *migrate,
                                    unsigned long addr,
@@ -594,11 +600,8 @@ static void migrate_vma_insert_page(struct migrate_vma *migrate,
                                                page_to_pfn(page));
                entry = swp_entry_to_pte(swp_entry);
        } else {
-               /*
-                * For now we only support migrating to un-addressable device
-                * memory.
-                */
-               if (is_zone_device_page(page)) {
+               if (is_zone_device_page(page) &&
+                   !is_device_coherent_page(page)) {
                        pr_warn_once("Unsupported ZONE_DEVICE page type.\n");
                        goto abort;
                }
@@ -683,6 +686,12 @@ void migrate_vma_pages(struct migrate_vma *migrate)
                }
 
                if (!page) {
+                       /*
+                        * The only time there is no vma is when called from
+                        * migrate_device_coherent_page(). However this isn't
+                        * called if the page could not be unmapped.
+                        */
+                       VM_BUG_ON(!migrate->vma);
                        if (!(migrate->src[i] & MIGRATE_PFN_MIGRATE))
                                continue;
                        if (!notified) {
@@ -701,10 +710,11 @@ void migrate_vma_pages(struct migrate_vma *migrate)
 
                mapping = page_mapping(page);
 
-               if (is_device_private_page(newpage)) {
+               if (is_device_private_page(newpage) ||
+                   is_device_coherent_page(newpage)) {
                        /*
-                        * For now only support private anonymous when migrating
-                        * to un-addressable device memory.
+                        * For now only support anonymous memory migrating to
+                        * device private or coherent memory.
                         */
                        if (mapping) {
                                migrate->src[i] &= ~MIGRATE_PFN_MIGRATE;
@@ -791,3 +801,49 @@ void migrate_vma_finalize(struct migrate_vma *migrate)
        }
 }
 EXPORT_SYMBOL(migrate_vma_finalize);
+
+/*
+ * Migrate a device coherent page back to normal memory. The caller should have
+ * a reference on page which will be copied to the new page if migration is
+ * successful or dropped on failure.
+ */
+int migrate_device_coherent_page(struct page *page)
+{
+       unsigned long src_pfn, dst_pfn = 0;
+       struct migrate_vma args;
+       struct page *dpage;
+
+       WARN_ON_ONCE(PageCompound(page));
+
+       lock_page(page);
+       src_pfn = migrate_pfn(page_to_pfn(page)) | MIGRATE_PFN_MIGRATE;
+       args.src = &src_pfn;
+       args.dst = &dst_pfn;
+       args.cpages = 1;
+       args.npages = 1;
+       args.vma = NULL;
+
+       /*
+        * We don't have a VMA and don't need to walk the page tables to find
+        * the source page. So call migrate_vma_unmap() directly to unmap the
+        * page as migrate_vma_setup() will fail if args.vma == NULL.
+        */
+       migrate_vma_unmap(&args);
+       if (!(src_pfn & MIGRATE_PFN_MIGRATE))
+               return -EBUSY;
+
+       dpage = alloc_page(GFP_USER | __GFP_NOWARN);
+       if (dpage) {
+               lock_page(dpage);
+               dst_pfn = migrate_pfn(page_to_pfn(dpage));
+       }
+
+       migrate_vma_pages(&args);
+       if (src_pfn & MIGRATE_PFN_MIGRATE)
+               copy_highpage(dpage, page);
+       migrate_vma_finalize(&args);
+
+       if (src_pfn & MIGRATE_PFN_MIGRATE)
+               return 0;
+       return -EBUSY;
+}
index 716caf8..b14e929 100644 (file)
@@ -333,7 +333,7 @@ static int mlock_pte_range(pmd_t *pmd, unsigned long addr,
                if (!pte_present(*pte))
                        continue;
                page = vm_normal_page(vma, addr, *pte);
-               if (!page)
+               if (!page || is_zone_device_page(page))
                        continue;
                if (PageTransCompound(page))
                        continue;
index 61e6135..c035020 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -81,53 +81,6 @@ static void unmap_region(struct mm_struct *mm,
                struct vm_area_struct *vma, struct vm_area_struct *prev,
                unsigned long start, unsigned long end);
 
-/* description of effects of mapping type and prot in current implementation.
- * this is due to the limited x86 page protection hardware.  The expected
- * behavior is in parens:
- *
- * map_type    prot
- *             PROT_NONE       PROT_READ       PROT_WRITE      PROT_EXEC
- * MAP_SHARED  r: (no) no      r: (yes) yes    r: (no) yes     r: (no) yes
- *             w: (no) no      w: (no) no      w: (yes) yes    w: (no) no
- *             x: (no) no      x: (no) yes     x: (no) yes     x: (yes) yes
- *
- * MAP_PRIVATE r: (no) no      r: (yes) yes    r: (no) yes     r: (no) yes
- *             w: (no) no      w: (no) no      w: (copy) copy  w: (no) no
- *             x: (no) no      x: (no) yes     x: (no) yes     x: (yes) yes
- *
- * On arm64, PROT_EXEC has the following behaviour for both MAP_SHARED and
- * MAP_PRIVATE (with Enhanced PAN supported):
- *                                                             r: (no) no
- *                                                             w: (no) no
- *                                                             x: (yes) yes
- */
-pgprot_t protection_map[16] __ro_after_init = {
-       [VM_NONE]                                       = __P000,
-       [VM_READ]                                       = __P001,
-       [VM_WRITE]                                      = __P010,
-       [VM_WRITE | VM_READ]                            = __P011,
-       [VM_EXEC]                                       = __P100,
-       [VM_EXEC | VM_READ]                             = __P101,
-       [VM_EXEC | VM_WRITE]                            = __P110,
-       [VM_EXEC | VM_WRITE | VM_READ]                  = __P111,
-       [VM_SHARED]                                     = __S000,
-       [VM_SHARED | VM_READ]                           = __S001,
-       [VM_SHARED | VM_WRITE]                          = __S010,
-       [VM_SHARED | VM_WRITE | VM_READ]                = __S011,
-       [VM_SHARED | VM_EXEC]                           = __S100,
-       [VM_SHARED | VM_EXEC | VM_READ]                 = __S101,
-       [VM_SHARED | VM_EXEC | VM_WRITE]                = __S110,
-       [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = __S111
-};
-
-#ifndef CONFIG_ARCH_HAS_VM_GET_PAGE_PROT
-pgprot_t vm_get_page_prot(unsigned long vm_flags)
-{
-       return protection_map[vm_flags & (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)];
-}
-EXPORT_SYMBOL(vm_get_page_prot);
-#endif /* CONFIG_ARCH_HAS_VM_GET_PAGE_PROT */
-
 static pgprot_t vm_pgprot_modify(pgprot_t oldprot, unsigned long vm_flags)
 {
        return pgprot_modify(oldprot, vm_get_page_prot(vm_flags));
@@ -1694,7 +1647,7 @@ int vma_wants_writenotify(struct vm_area_struct *vma, pgprot_t vm_page_prot)
                return 0;
 
        /* Do we need to track softdirty? */
-       if (IS_ENABLED(CONFIG_MEM_SOFT_DIRTY) && !(vm_flags & VM_SOFTDIRTY))
+       if (vma_soft_dirty_enabled(vma))
                return 1;
 
        /* Specialty mapping? */
@@ -1894,7 +1847,6 @@ unmap_and_free_vma:
 
        /* Undo any partial mapping done by a device driver. */
        unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end);
-       charged = 0;
        if (vm_flags & VM_SHARED)
                mapping_unmap_writable(file->f_mapping);
 free_vma:
@@ -2588,7 +2540,6 @@ find_extend_vma(struct mm_struct *mm, unsigned long addr)
        vma = find_vma_prev(mm, addr, &prev);
        if (vma && (vma->vm_start <= addr))
                return vma;
-       /* don't alter vm_end if the coredump is running */
        if (!prev || expand_stack(prev, addr))
                return NULL;
        if (prev->vm_flags & VM_LOCKED)
@@ -2944,7 +2895,7 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size,
        unsigned long ret = -EINVAL;
        struct file *file;
 
-       pr_warn_once("%s (%d) uses deprecated remap_file_pages() syscall. See Documentation/vm/remap_file_pages.rst.\n",
+       pr_warn_once("%s (%d) uses deprecated remap_file_pages() syscall. See Documentation/mm/remap_file_pages.rst.\n",
                     current->comm, current->pid);
 
        if (prot)
index ba55926..3a23dde 100644 (file)
 
 #include "internal.h"
 
+static inline bool can_change_pte_writable(struct vm_area_struct *vma,
+                                          unsigned long addr, pte_t pte)
+{
+       struct page *page;
+
+       VM_BUG_ON(!(vma->vm_flags & VM_WRITE) || pte_write(pte));
+
+       if (pte_protnone(pte) || !pte_dirty(pte))
+               return false;
+
+       /* Do we need write faults for softdirty tracking? */
+       if (vma_soft_dirty_enabled(vma) && !pte_soft_dirty(pte))
+               return false;
+
+       /* Do we need write faults for uffd-wp tracking? */
+       if (userfaultfd_pte_wp(vma, pte))
+               return false;
+
+       if (!(vma->vm_flags & VM_SHARED)) {
+               /*
+                * We can only special-case on exclusive anonymous pages,
+                * because we know that our write-fault handler similarly would
+                * map them writable without any additional checks while holding
+                * the PT lock.
+                */
+               page = vm_normal_page(vma, addr, pte);
+               if (!page || !PageAnon(page) || !PageAnonExclusive(page))
+                       return false;
+       }
+
+       return true;
+}
+
 static unsigned long change_pte_range(struct mmu_gather *tlb,
                struct vm_area_struct *vma, pmd_t *pmd, unsigned long addr,
                unsigned long end, pgprot_t newprot, unsigned long cp_flags)
@@ -46,7 +79,6 @@ static unsigned long change_pte_range(struct mmu_gather *tlb,
        spinlock_t *ptl;
        unsigned long pages = 0;
        int target_node = NUMA_NO_NODE;
-       bool dirty_accountable = cp_flags & MM_CP_DIRTY_ACCT;
        bool prot_numa = cp_flags & MM_CP_PROT_NUMA;
        bool uffd_wp = cp_flags & MM_CP_UFFD_WP;
        bool uffd_wp_resolve = cp_flags & MM_CP_UFFD_WP_RESOLVE;
@@ -95,7 +127,7 @@ static unsigned long change_pte_range(struct mmu_gather *tlb,
                                        continue;
 
                                page = vm_normal_page(vma, addr, oldpte);
-                               if (!page || PageKsm(page))
+                               if (!page || is_zone_device_page(page) || PageKsm(page))
                                        continue;
 
                                /* Also skip shared copy-on-write pages */
@@ -137,21 +169,27 @@ static unsigned long change_pte_range(struct mmu_gather *tlb,
                                ptent = pte_wrprotect(ptent);
                                ptent = pte_mkuffd_wp(ptent);
                        } else if (uffd_wp_resolve) {
-                               /*
-                                * Leave the write bit to be handled
-                                * by PF interrupt handler, then
-                                * things like COW could be properly
-                                * handled.
-                                */
                                ptent = pte_clear_uffd_wp(ptent);
                        }
 
-                       /* Avoid taking write faults for known dirty pages */
-                       if (dirty_accountable && pte_dirty(ptent) &&
-                                       (pte_soft_dirty(ptent) ||
-                                        !(vma->vm_flags & VM_SOFTDIRTY))) {
+                       /*
+                        * In some writable, shared mappings, we might want
+                        * to catch actual write access -- see
+                        * vma_wants_writenotify().
+                        *
+                        * In all writable, private mappings, we have to
+                        * properly handle COW.
+                        *
+                        * In both cases, we can sometimes still change PTEs
+                        * writable and avoid the write-fault handler, for
+                        * example, if a PTE is already dirty and no other
+                        * COW or special handling is required.
+                        */
+                       if ((cp_flags & MM_CP_TRY_CHANGE_WRITABLE) &&
+                           !pte_write(ptent) &&
+                           can_change_pte_writable(vma, addr, ptent))
                                ptent = pte_mkwrite(ptent);
-                       }
+
                        ptep_modify_prot_commit(vma, addr, pte, oldpte, ptent);
                        if (pte_needs_flush(oldpte, ptent))
                                tlb_flush_pte_range(tlb, addr, PAGE_SIZE);
@@ -505,9 +543,9 @@ mprotect_fixup(struct mmu_gather *tlb, struct vm_area_struct *vma,
        unsigned long oldflags = vma->vm_flags;
        long nrpages = (end - start) >> PAGE_SHIFT;
        unsigned long charged = 0;
+       bool try_change_writable;
        pgoff_t pgoff;
        int error;
-       int dirty_accountable = 0;
 
        if (newflags == oldflags) {
                *pprev = vma;
@@ -583,11 +621,20 @@ success:
         * held in write mode.
         */
        vma->vm_flags = newflags;
-       dirty_accountable = vma_wants_writenotify(vma, vma->vm_page_prot);
+       /*
+        * We want to check manually if we can change individual PTEs writable
+        * if we can't do that automatically for all PTEs in a mapping. For
+        * private mappings, that's always the case when we have write
+        * permissions as we properly have to handle COW.
+        */
+       if (vma->vm_flags & VM_SHARED)
+               try_change_writable = vma_wants_writenotify(vma, vma->vm_page_prot);
+       else
+               try_change_writable = !!(vma->vm_flags & VM_WRITE);
        vma_set_page_prot(vma);
 
        change_protection(tlb, vma, start, end, vma->vm_page_prot,
-                         dirty_accountable ? MM_CP_DIRTY_ACCT : 0);
+                         try_change_writable ? MM_CP_TRY_CHANGE_WRITABLE : 0);
 
        /*
         * Private VM_LOCKED VMA becoming writable: trigger COW to avoid major
@@ -616,7 +663,7 @@ static int do_mprotect_pkey(unsigned long start, size_t len,
 {
        unsigned long nstart, end, tmp, reqprot;
        struct vm_area_struct *vma, *prev;
-       int error = -EINVAL;
+       int error;
        const int grows = prot & (PROT_GROWSDOWN|PROT_GROWSUP);
        const bool rier = (current->personality & READ_IMPLIES_EXEC) &&
                                (prot & PROT_READ);
index 9d7afc2..e819cbc 100644 (file)
@@ -500,7 +500,7 @@ static void delete_nommu_region(struct vm_region *region)
 static void free_page_series(unsigned long from, unsigned long to)
 {
        for (; from < to; from += PAGE_SIZE) {
-               struct page *page = virt_to_page(from);
+               struct page *page = virt_to_page((void *)from);
 
                atomic_long_dec(&mmap_pages_allocated);
                put_page(page);
index 170bbf1..e5486d4 100644 (file)
@@ -126,13 +126,97 @@ typedef int __bitwise fpi_t;
 static DEFINE_MUTEX(pcp_batch_high_lock);
 #define MIN_PERCPU_PAGELIST_HIGH_FRACTION (8)
 
-struct pagesets {
-       local_lock_t lock;
-};
-static DEFINE_PER_CPU(struct pagesets, pagesets) = {
-       .lock = INIT_LOCAL_LOCK(lock),
-};
+#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT)
+/*
+ * On SMP, spin_trylock is sufficient protection.
+ * On PREEMPT_RT, spin_trylock is equivalent on both SMP and UP.
+ */
+#define pcp_trylock_prepare(flags)     do { } while (0)
+#define pcp_trylock_finish(flag)       do { } while (0)
+#else
+
+/* UP spin_trylock always succeeds so disable IRQs to prevent re-entrancy. */
+#define pcp_trylock_prepare(flags)     local_irq_save(flags)
+#define pcp_trylock_finish(flags)      local_irq_restore(flags)
+#endif
+
+/*
+ * Locking a pcp requires a PCP lookup followed by a spinlock. To avoid
+ * a migration causing the wrong PCP to be locked and remote memory being
+ * potentially allocated, pin the task to the CPU for the lookup+lock.
+ * preempt_disable is used on !RT because it is faster than migrate_disable.
+ * migrate_disable is used on RT because otherwise RT spinlock usage is
+ * interfered with and a high priority task cannot preempt the allocator.
+ */
+#ifndef CONFIG_PREEMPT_RT
+#define pcpu_task_pin()                preempt_disable()
+#define pcpu_task_unpin()      preempt_enable()
+#else
+#define pcpu_task_pin()                migrate_disable()
+#define pcpu_task_unpin()      migrate_enable()
+#endif
 
+/*
+ * Generic helper to lookup and a per-cpu variable with an embedded spinlock.
+ * Return value should be used with equivalent unlock helper.
+ */
+#define pcpu_spin_lock(type, member, ptr)                              \
+({                                                                     \
+       type *_ret;                                                     \
+       pcpu_task_pin();                                                \
+       _ret = this_cpu_ptr(ptr);                                       \
+       spin_lock(&_ret->member);                                       \
+       _ret;                                                           \
+})
+
+#define pcpu_spin_lock_irqsave(type, member, ptr, flags)               \
+({                                                                     \
+       type *_ret;                                                     \
+       pcpu_task_pin();                                                \
+       _ret = this_cpu_ptr(ptr);                                       \
+       spin_lock_irqsave(&_ret->member, flags);                        \
+       _ret;                                                           \
+})
+
+#define pcpu_spin_trylock_irqsave(type, member, ptr, flags)            \
+({                                                                     \
+       type *_ret;                                                     \
+       pcpu_task_pin();                                                \
+       _ret = this_cpu_ptr(ptr);                                       \
+       if (!spin_trylock_irqsave(&_ret->member, flags)) {              \
+               pcpu_task_unpin();                                      \
+               _ret = NULL;                                            \
+       }                                                               \
+       _ret;                                                           \
+})
+
+#define pcpu_spin_unlock(member, ptr)                                  \
+({                                                                     \
+       spin_unlock(&ptr->member);                                      \
+       pcpu_task_unpin();                                              \
+})
+
+#define pcpu_spin_unlock_irqrestore(member, ptr, flags)                        \
+({                                                                     \
+       spin_unlock_irqrestore(&ptr->member, flags);                    \
+       pcpu_task_unpin();                                              \
+})
+
+/* struct per_cpu_pages specific helpers. */
+#define pcp_spin_lock(ptr)                                             \
+       pcpu_spin_lock(struct per_cpu_pages, lock, ptr)
+
+#define pcp_spin_lock_irqsave(ptr, flags)                              \
+       pcpu_spin_lock_irqsave(struct per_cpu_pages, lock, ptr, flags)
+
+#define pcp_spin_trylock_irqsave(ptr, flags)                           \
+       pcpu_spin_trylock_irqsave(struct per_cpu_pages, lock, ptr, flags)
+
+#define pcp_spin_unlock(ptr)                                           \
+       pcpu_spin_unlock(lock, ptr)
+
+#define pcp_spin_unlock_irqrestore(ptr, flags)                         \
+       pcpu_spin_unlock_irqrestore(lock, ptr, flags)
 #ifdef CONFIG_USE_PERCPU_NUMA_NODE_ID
 DEFINE_PER_CPU(int, numa_node);
 EXPORT_PER_CPU_SYMBOL(numa_node);
@@ -151,13 +235,7 @@ DEFINE_PER_CPU(int, _numa_mem_);           /* Kernel "local memory" node */
 EXPORT_PER_CPU_SYMBOL(_numa_mem_);
 #endif
 
-/* work_structs for global per-cpu drains */
-struct pcpu_drain {
-       struct zone *zone;
-       struct work_struct work;
-};
 static DEFINE_MUTEX(pcpu_drain_mutex);
-static DEFINE_PER_CPU(struct pcpu_drain, pcpu_drain);
 
 #ifdef CONFIG_GCC_PLUGIN_LATENT_ENTROPY
 volatile unsigned long latent_entropy __latent_entropy;
@@ -524,7 +602,7 @@ void set_pfnblock_flags_mask(struct page *page, unsigned long flags,
 {
        unsigned long *bitmap;
        unsigned long bitidx, word_bitidx;
-       unsigned long old_word, word;
+       unsigned long word;
 
        BUILD_BUG_ON(NR_PAGEBLOCK_BITS != 4);
        BUILD_BUG_ON(MIGRATE_TYPES > (1 << PB_migratetype_bits));
@@ -540,12 +618,8 @@ void set_pfnblock_flags_mask(struct page *page, unsigned long flags,
        flags <<= bitidx;
 
        word = READ_ONCE(bitmap[word_bitidx]);
-       for (;;) {
-               old_word = cmpxchg(&bitmap[word_bitidx], word, (word & ~mask) | flags);
-               if (word == old_word)
-                       break;
-               word = old_word;
-       }
+       do {
+       } while (!try_cmpxchg(&bitmap[word_bitidx], &word, (word & ~mask) | flags));
 }
 
 void set_pageblock_migratetype(struct page *page, int migratetype)
@@ -653,7 +727,7 @@ static inline unsigned int order_to_pindex(int migratetype, int order)
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
        if (order > PAGE_ALLOC_COSTLY_ORDER) {
                VM_BUG_ON(order != pageblock_order);
-               base = PAGE_ALLOC_COSTLY_ORDER + 1;
+               return NR_LOWORDER_PCP_LISTS;
        }
 #else
        VM_BUG_ON(order > PAGE_ALLOC_COSTLY_ORDER);
@@ -667,7 +741,7 @@ static inline int pindex_to_order(unsigned int pindex)
        int order = pindex / MIGRATE_PCPTYPES;
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
-       if (order > PAGE_ALLOC_COSTLY_ORDER)
+       if (pindex == NR_LOWORDER_PCP_LISTS)
                order = pageblock_order;
 #else
        VM_BUG_ON(order > PAGE_ALLOC_COSTLY_ORDER);
@@ -744,6 +818,14 @@ void prep_compound_page(struct page *page, unsigned int order)
        prep_compound_head(page, order);
 }
 
+void destroy_large_folio(struct folio *folio)
+{
+       enum compound_dtor_id dtor = folio_page(folio, 1)->compound_dtor;
+
+       VM_BUG_ON_FOLIO(dtor >= NR_COMPOUND_DTORS, folio);
+       compound_page_dtors[dtor](&folio->page);
+}
+
 #ifdef CONFIG_DEBUG_PAGEALLOC
 unsigned int _debug_guardpage_minorder;
 
@@ -785,7 +867,7 @@ static inline bool set_page_guard(struct zone *zone, struct page *page,
                return false;
 
        __SetPageGuard(page);
-       INIT_LIST_HEAD(&page->lru);
+       INIT_LIST_HEAD(&page->buddy_list);
        set_page_private(page, order);
        /* Guard pages are not available for any usage */
        __mod_zone_freepage_state(zone, -(1 << order), migratetype);
@@ -928,7 +1010,7 @@ static inline void add_to_free_list(struct page *page, struct zone *zone,
 {
        struct free_area *area = &zone->free_area[order];
 
-       list_add(&page->lru, &area->free_list[migratetype]);
+       list_add(&page->buddy_list, &area->free_list[migratetype]);
        area->nr_free++;
 }
 
@@ -938,7 +1020,7 @@ static inline void add_to_free_list_tail(struct page *page, struct zone *zone,
 {
        struct free_area *area = &zone->free_area[order];
 
-       list_add_tail(&page->lru, &area->free_list[migratetype]);
+       list_add_tail(&page->buddy_list, &area->free_list[migratetype]);
        area->nr_free++;
 }
 
@@ -952,7 +1034,7 @@ static inline void move_to_free_list(struct page *page, struct zone *zone,
 {
        struct free_area *area = &zone->free_area[order];
 
-       list_move_tail(&page->lru, &area->free_list[migratetype]);
+       list_move_tail(&page->buddy_list, &area->free_list[migratetype]);
 }
 
 static inline void del_page_from_free_list(struct page *page, struct zone *zone,
@@ -962,7 +1044,7 @@ static inline void del_page_from_free_list(struct page *page, struct zone *zone,
        if (page_reported(page))
                __ClearPageReported(page);
 
-       list_del(&page->lru);
+       list_del(&page->buddy_list);
        __ClearPageBuddy(page);
        set_page_private(page, 0);
        zone->free_area[order].nr_free--;
@@ -1296,18 +1378,14 @@ static inline bool should_skip_kasan_poison(struct page *page, fpi_t fpi_flags)
               PageSkipKASanPoison(page);
 }
 
-static void kernel_init_free_pages(struct page *page, int numpages)
+static void kernel_init_pages(struct page *page, int numpages)
 {
        int i;
 
        /* s390's use of memset() could override KASAN redzones. */
        kasan_disable_current();
-       for (i = 0; i < numpages; i++) {
-               u8 tag = page_kasan_tag(page + i);
-               page_kasan_tag_reset(page + i);
-               clear_highpage(page + i);
-               page_kasan_tag_set(page + i, tag);
-       }
+       for (i = 0; i < numpages; i++)
+               clear_highpage_kasan_tagged(page + i);
        kasan_enable_current();
 }
 
@@ -1396,7 +1474,7 @@ static __always_inline bool free_pages_prepare(struct page *page,
                        init = false;
        }
        if (init)
-               kernel_init_free_pages(page, 1 << order);
+               kernel_init_pages(page, 1 << order);
 
        /*
         * arch_free_page() can make the page's contents inaccessible.  s390
@@ -1473,10 +1551,7 @@ static void free_pcppages_bulk(struct zone *zone, int count,
        /* Ensure requested pindex is drained first. */
        pindex = pindex - 1;
 
-       /*
-        * local_lock_irq held so equivalent to spin_lock_irqsave for
-        * both PREEMPT_RT and non-PREEMPT_RT configurations.
-        */
+       /* Caller must hold IRQ-safe pcp->lock so IRQs are disabled. */
        spin_lock(&zone->lock);
        isolated_pageblocks = has_isolate_pageblock(zone);
 
@@ -1504,11 +1579,11 @@ static void free_pcppages_bulk(struct zone *zone, int count,
                do {
                        int mt;
 
-                       page = list_last_entry(list, struct page, lru);
+                       page = list_last_entry(list, struct page, pcp_list);
                        mt = get_pcppage_migratetype(page);
 
                        /* must delete to avoid corrupting pcp list */
-                       list_del(&page->lru);
+                       list_del(&page->pcp_list);
                        count -= nr_pages;
                        pcp->count -= nr_pages;
 
@@ -2442,7 +2517,7 @@ inline void post_alloc_hook(struct page *page, unsigned int order,
        }
        /* If memory is still not initialized, do it now. */
        if (init)
-               kernel_init_free_pages(page, 1 << order);
+               kernel_init_pages(page, 1 << order);
        /* Propagate __GFP_SKIP_KASAN_POISON to page flags. */
        if (kasan_hw_tags_enabled() && (gfp_flags & __GFP_SKIP_KASAN_POISON))
                SetPageSkipKASanPoison(page);
@@ -3045,10 +3120,7 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
 {
        int i, allocated = 0;
 
-       /*
-        * local_lock_irq held so equivalent to spin_lock_irqsave for
-        * both PREEMPT_RT and non-PREEMPT_RT configurations.
-        */
+       /* Caller must hold IRQ-safe pcp->lock so IRQs are disabled. */
        spin_lock(&zone->lock);
        for (i = 0; i < count; ++i) {
                struct page *page = __rmqueue(zone, order, migratetype,
@@ -3069,7 +3141,7 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
                 * for IO devices that can merge IO requests if the physical
                 * pages are ordered properly.
                 */
-               list_add_tail(&page->lru, list);
+               list_add_tail(&page->pcp_list, list);
                allocated++;
                if (is_migrate_cma(get_pcppage_migratetype(page)))
                        __mod_zone_page_state(zone, NR_FREE_CMA_PAGES,
@@ -3092,51 +3164,48 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
  * Called from the vmstat counter updater to drain pagesets of this
  * currently executing processor on remote nodes after they have
  * expired.
- *
- * Note that this function must be called with the thread pinned to
- * a single processor.
  */
 void drain_zone_pages(struct zone *zone, struct per_cpu_pages *pcp)
 {
-       unsigned long flags;
        int to_drain, batch;
 
-       local_lock_irqsave(&pagesets.lock, flags);
        batch = READ_ONCE(pcp->batch);
        to_drain = min(pcp->count, batch);
-       if (to_drain > 0)
+       if (to_drain > 0) {
+               unsigned long flags;
+
+               /*
+                * free_pcppages_bulk expects IRQs disabled for zone->lock
+                * so even though pcp->lock is not intended to be IRQ-safe,
+                * it's needed in this context.
+                */
+               spin_lock_irqsave(&pcp->lock, flags);
                free_pcppages_bulk(zone, to_drain, pcp, 0);
-       local_unlock_irqrestore(&pagesets.lock, flags);
+               spin_unlock_irqrestore(&pcp->lock, flags);
+       }
 }
 #endif
 
 /*
  * Drain pcplists of the indicated processor and zone.
- *
- * The processor must either be the current processor and the
- * thread pinned to the current processor or a processor that
- * is not online.
  */
 static void drain_pages_zone(unsigned int cpu, struct zone *zone)
 {
-       unsigned long flags;
        struct per_cpu_pages *pcp;
 
-       local_lock_irqsave(&pagesets.lock, flags);
-
        pcp = per_cpu_ptr(zone->per_cpu_pageset, cpu);
-       if (pcp->count)
-               free_pcppages_bulk(zone, pcp->count, pcp, 0);
+       if (pcp->count) {
+               unsigned long flags;
 
-       local_unlock_irqrestore(&pagesets.lock, flags);
+               /* See drain_zone_pages on why this is disabling IRQs */
+               spin_lock_irqsave(&pcp->lock, flags);
+               free_pcppages_bulk(zone, pcp->count, pcp, 0);
+               spin_unlock_irqrestore(&pcp->lock, flags);
+       }
 }
 
 /*
  * Drain pcplists of all zones on the indicated processor.
- *
- * The processor must either be the current processor and the
- * thread pinned to the current processor or a processor that
- * is not online.
  */
 static void drain_pages(unsigned int cpu)
 {
@@ -3149,9 +3218,6 @@ static void drain_pages(unsigned int cpu)
 
 /*
  * Spill all of this CPU's per-cpu pages back into the buddy allocator.
- *
- * The CPU has to be pinned. When zone parameter is non-NULL, spill just
- * the single zone's pages.
  */
 void drain_local_pages(struct zone *zone)
 {
@@ -3163,24 +3229,6 @@ void drain_local_pages(struct zone *zone)
                drain_pages(cpu);
 }
 
-static void drain_local_pages_wq(struct work_struct *work)
-{
-       struct pcpu_drain *drain;
-
-       drain = container_of(work, struct pcpu_drain, work);
-
-       /*
-        * drain_all_pages doesn't use proper cpu hotplug protection so
-        * we can race with cpu offline when the WQ can move this from
-        * a cpu pinned worker to an unbound one. We can operate on a different
-        * cpu which is alright but we also have to make sure to not move to
-        * a different one.
-        */
-       migrate_disable();
-       drain_local_pages(drain->zone);
-       migrate_enable();
-}
-
 /*
  * The implementation of drain_all_pages(), exposing an extra parameter to
  * drain on all cpus.
@@ -3201,13 +3249,6 @@ static void __drain_all_pages(struct zone *zone, bool force_all_cpus)
         */
        static cpumask_t cpus_with_pcps;
 
-       /*
-        * Make sure nobody triggers this path before mm_percpu_wq is fully
-        * initialized.
-        */
-       if (WARN_ON_ONCE(!mm_percpu_wq))
-               return;
-
        /*
         * Do not drain if one is already in progress unless it's specific to
         * a zone. Such callers are primarily CMA and memory hotplug and need
@@ -3257,14 +3298,11 @@ static void __drain_all_pages(struct zone *zone, bool force_all_cpus)
        }
 
        for_each_cpu(cpu, &cpus_with_pcps) {
-               struct pcpu_drain *drain = per_cpu_ptr(&pcpu_drain, cpu);
-
-               drain->zone = zone;
-               INIT_WORK(&drain->work, drain_local_pages_wq);
-               queue_work_on(cpu, mm_percpu_wq, &drain->work);
+               if (zone)
+                       drain_pages_zone(cpu, zone);
+               else
+                       drain_pages(cpu);
        }
-       for_each_cpu(cpu, &cpus_with_pcps)
-               flush_work(&per_cpu_ptr(&pcpu_drain, cpu)->work);
 
        mutex_unlock(&pcpu_drain_mutex);
 }
@@ -3273,8 +3311,6 @@ static void __drain_all_pages(struct zone *zone, bool force_all_cpus)
  * Spill all the per-cpu pages from all CPUs back into the buddy allocator.
  *
  * When zone parameter is non-NULL, spill just the single zone's pages.
- *
- * Note that this can be extremely slow as the draining happens in a workqueue.
  */
 void drain_all_pages(struct zone *zone)
 {
@@ -3319,7 +3355,7 @@ void mark_free_pages(struct zone *zone)
 
        for_each_migratetype_order(order, t) {
                list_for_each_entry(page,
-                               &zone->free_area[order].free_list[t], lru) {
+                               &zone->free_area[order].free_list[t], buddy_list) {
                        unsigned long i;
 
                        pfn = page_to_pfn(page);
@@ -3396,19 +3432,17 @@ static int nr_pcp_high(struct per_cpu_pages *pcp, struct zone *zone,
        return min(READ_ONCE(pcp->batch) << 2, high);
 }
 
-static void free_unref_page_commit(struct page *page, int migratetype,
+static void free_unref_page_commit(struct zone *zone, struct per_cpu_pages *pcp,
+                                  struct page *page, int migratetype,
                                   unsigned int order)
 {
-       struct zone *zone = page_zone(page);
-       struct per_cpu_pages *pcp;
        int high;
        int pindex;
        bool free_high;
 
        __count_vm_event(PGFREE);
-       pcp = this_cpu_ptr(zone->per_cpu_pageset);
        pindex = order_to_pindex(migratetype, order);
-       list_add(&page->lru, &pcp->lists[pindex]);
+       list_add(&page->pcp_list, &pcp->lists[pindex]);
        pcp->count += 1 << order;
 
        /*
@@ -3433,6 +3467,9 @@ static void free_unref_page_commit(struct page *page, int migratetype,
 void free_unref_page(struct page *page, unsigned int order)
 {
        unsigned long flags;
+       unsigned long __maybe_unused UP_flags;
+       struct per_cpu_pages *pcp;
+       struct zone *zone;
        unsigned long pfn = page_to_pfn(page);
        int migratetype;
 
@@ -3455,9 +3492,16 @@ void free_unref_page(struct page *page, unsigned int order)
                migratetype = MIGRATE_MOVABLE;
        }
 
-       local_lock_irqsave(&pagesets.lock, flags);
-       free_unref_page_commit(page, migratetype, order);
-       local_unlock_irqrestore(&pagesets.lock, flags);
+       zone = page_zone(page);
+       pcp_trylock_prepare(UP_flags);
+       pcp = pcp_spin_trylock_irqsave(zone->per_cpu_pageset, flags);
+       if (pcp) {
+               free_unref_page_commit(zone, pcp, page, migratetype, order);
+               pcp_spin_unlock_irqrestore(pcp, flags);
+       } else {
+               free_one_page(zone, page, pfn, order, migratetype, FPI_NONE);
+       }
+       pcp_trylock_finish(UP_flags);
 }
 
 /*
@@ -3466,6 +3510,8 @@ void free_unref_page(struct page *page, unsigned int order)
 void free_unref_page_list(struct list_head *list)
 {
        struct page *page, *next;
+       struct per_cpu_pages *pcp = NULL;
+       struct zone *locked_zone = NULL;
        unsigned long flags;
        int batch_count = 0;
        int migratetype;
@@ -3490,8 +3536,18 @@ void free_unref_page_list(struct list_head *list)
                }
        }
 
-       local_lock_irqsave(&pagesets.lock, flags);
        list_for_each_entry_safe(page, next, list, lru) {
+               struct zone *zone = page_zone(page);
+
+               /* Different zone, different pcp lock. */
+               if (zone != locked_zone) {
+                       if (pcp)
+                               pcp_spin_unlock_irqrestore(pcp, flags);
+
+                       locked_zone = zone;
+                       pcp = pcp_spin_lock_irqsave(locked_zone->per_cpu_pageset, flags);
+               }
+
                /*
                 * Non-isolated types over MIGRATE_PCPTYPES get added
                 * to the MIGRATE_MOVABLE pcp list.
@@ -3501,19 +3557,21 @@ void free_unref_page_list(struct list_head *list)
                        migratetype = MIGRATE_MOVABLE;
 
                trace_mm_page_free_batched(page);
-               free_unref_page_commit(page, migratetype, 0);
+               free_unref_page_commit(zone, pcp, page, migratetype, 0);
 
                /*
                 * Guard against excessive IRQ disabled times when we get
                 * a large list of pages to free.
                 */
                if (++batch_count == SWAP_CLUSTER_MAX) {
-                       local_unlock_irqrestore(&pagesets.lock, flags);
+                       pcp_spin_unlock_irqrestore(pcp, flags);
                        batch_count = 0;
-                       local_lock_irqsave(&pagesets.lock, flags);
+                       pcp = pcp_spin_lock_irqsave(locked_zone->per_cpu_pageset, flags);
                }
        }
-       local_unlock_irqrestore(&pagesets.lock, flags);
+
+       if (pcp)
+               pcp_spin_unlock_irqrestore(pcp, flags);
 }
 
 /*
@@ -3638,6 +3696,43 @@ static inline void zone_statistics(struct zone *preferred_zone, struct zone *z,
 #endif
 }
 
+static __always_inline
+struct page *rmqueue_buddy(struct zone *preferred_zone, struct zone *zone,
+                          unsigned int order, unsigned int alloc_flags,
+                          int migratetype)
+{
+       struct page *page;
+       unsigned long flags;
+
+       do {
+               page = NULL;
+               spin_lock_irqsave(&zone->lock, flags);
+               /*
+                * order-0 request can reach here when the pcplist is skipped
+                * due to non-CMA allocation context. HIGHATOMIC area is
+                * reserved for high-order atomic allocation, so order-0
+                * request should skip it.
+                */
+               if (order > 0 && alloc_flags & ALLOC_HARDER)
+                       page = __rmqueue_smallest(zone, order, MIGRATE_HIGHATOMIC);
+               if (!page) {
+                       page = __rmqueue(zone, order, migratetype, alloc_flags);
+                       if (!page) {
+                               spin_unlock_irqrestore(&zone->lock, flags);
+                               return NULL;
+                       }
+               }
+               __mod_zone_freepage_state(zone, -(1 << order),
+                                         get_pcppage_migratetype(page));
+               spin_unlock_irqrestore(&zone->lock, flags);
+       } while (check_new_pages(page, order));
+
+       __count_zid_vm_events(PGALLOC, page_zonenum(page), 1 << order);
+       zone_statistics(preferred_zone, zone, 1);
+
+       return page;
+}
+
 /* Remove page from the per-cpu list, caller must protect the list */
 static inline
 struct page *__rmqueue_pcplist(struct zone *zone, unsigned int order,
@@ -3671,8 +3766,8 @@ struct page *__rmqueue_pcplist(struct zone *zone, unsigned int order,
                                return NULL;
                }
 
-               page = list_first_entry(list, struct page, lru);
-               list_del(&page->lru);
+               page = list_first_entry(list, struct page, pcp_list);
+               list_del(&page->pcp_list);
                pcp->count -= 1 << order;
        } while (check_new_pcp(page, order));
 
@@ -3689,19 +3784,29 @@ static struct page *rmqueue_pcplist(struct zone *preferred_zone,
        struct list_head *list;
        struct page *page;
        unsigned long flags;
+       unsigned long __maybe_unused UP_flags;
 
-       local_lock_irqsave(&pagesets.lock, flags);
+       /*
+        * spin_trylock may fail due to a parallel drain. In the future, the
+        * trylock will also protect against IRQ reentrancy.
+        */
+       pcp_trylock_prepare(UP_flags);
+       pcp = pcp_spin_trylock_irqsave(zone->per_cpu_pageset, flags);
+       if (!pcp) {
+               pcp_trylock_finish(UP_flags);
+               return NULL;
+       }
 
        /*
         * On allocation, reduce the number of pages that are batch freed.
         * See nr_pcp_free() where free_factor is increased for subsequent
         * frees.
         */
-       pcp = this_cpu_ptr(zone->per_cpu_pageset);
        pcp->free_factor >>= 1;
        list = &pcp->lists[order_to_pindex(migratetype, order)];
        page = __rmqueue_pcplist(zone, order, migratetype, alloc_flags, pcp, list);
-       local_unlock_irqrestore(&pagesets.lock, flags);
+       pcp_spin_unlock_irqrestore(pcp, flags);
+       pcp_trylock_finish(UP_flags);
        if (page) {
                __count_zid_vm_events(PGALLOC, page_zonenum(page), 1);
                zone_statistics(preferred_zone, zone, 1);
@@ -3718,9 +3823,14 @@ struct page *rmqueue(struct zone *preferred_zone,
                        gfp_t gfp_flags, unsigned int alloc_flags,
                        int migratetype)
 {
-       unsigned long flags;
        struct page *page;
 
+       /*
+        * We most definitely don't want callers attempting to
+        * allocate greater than order-1 page units with __GFP_NOFAIL.
+        */
+       WARN_ON_ONCE((gfp_flags & __GFP_NOFAIL) && (order > 1));
+
        if (likely(pcp_allowed_order(order))) {
                /*
                 * MIGRATE_MOVABLE pcplist could have the pages on CMA area and
@@ -3730,53 +3840,23 @@ struct page *rmqueue(struct zone *preferred_zone,
                                migratetype != MIGRATE_MOVABLE) {
                        page = rmqueue_pcplist(preferred_zone, zone, order,
                                        gfp_flags, migratetype, alloc_flags);
-                       goto out;
+                       if (likely(page))
+                               goto out;
                }
        }
 
-       /*
-        * We most definitely don't want callers attempting to
-        * allocate greater than order-1 page units with __GFP_NOFAIL.
-        */
-       WARN_ON_ONCE((gfp_flags & __GFP_NOFAIL) && (order > 1));
-
-       do {
-               page = NULL;
-               spin_lock_irqsave(&zone->lock, flags);
-               /*
-                * order-0 request can reach here when the pcplist is skipped
-                * due to non-CMA allocation context. HIGHATOMIC area is
-                * reserved for high-order atomic allocation, so order-0
-                * request should skip it.
-                */
-               if (order > 0 && alloc_flags & ALLOC_HARDER)
-                       page = __rmqueue_smallest(zone, order, MIGRATE_HIGHATOMIC);
-               if (!page) {
-                       page = __rmqueue(zone, order, migratetype, alloc_flags);
-                       if (!page)
-                               goto failed;
-               }
-               __mod_zone_freepage_state(zone, -(1 << order),
-                                         get_pcppage_migratetype(page));
-               spin_unlock_irqrestore(&zone->lock, flags);
-       } while (check_new_pages(page, order));
-
-       __count_zid_vm_events(PGALLOC, page_zonenum(page), 1 << order);
-       zone_statistics(preferred_zone, zone, 1);
+       page = rmqueue_buddy(preferred_zone, zone, order, alloc_flags,
+                                                       migratetype);
 
 out:
        /* Separate test+clear to avoid unnecessary atomics */
-       if (test_bit(ZONE_BOOSTED_WATERMARK, &zone->flags)) {
+       if (unlikely(test_bit(ZONE_BOOSTED_WATERMARK, &zone->flags))) {
                clear_bit(ZONE_BOOSTED_WATERMARK, &zone->flags);
                wakeup_kswapd(zone, 0, 0, zone_idx(zone));
        }
 
        VM_BUG_ON_PAGE(page && bad_range(zone, page), page);
        return page;
-
-failed:
-       spin_unlock_irqrestore(&zone->lock, flags);
-       return NULL;
 }
 
 #ifdef CONFIG_FAIL_PAGE_ALLOC
@@ -4095,7 +4175,7 @@ get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,
 retry:
        /*
         * Scan zonelist, looking for a zone with enough free.
-        * See also __cpuset_node_allowed() comment in kernel/cpuset.c.
+        * See also __cpuset_node_allowed() comment in kernel/cgroup/cpuset.c.
         */
        no_fallback = alloc_flags & ALLOC_NOFRAGMENT;
        z = ac->preferred_zoneref;
@@ -5202,10 +5282,7 @@ static inline bool prepare_alloc_pages(gfp_t gfp_mask, unsigned int order,
                        *alloc_flags |= ALLOC_CPUSET;
        }
 
-       fs_reclaim_acquire(gfp_mask);
-       fs_reclaim_release(gfp_mask);
-
-       might_sleep_if(gfp_mask & __GFP_DIRECT_RECLAIM);
+       might_alloc(gfp_mask);
 
        if (should_fail_alloc_page(gfp_mask, order))
                return false;
@@ -5253,6 +5330,7 @@ unsigned long __alloc_pages_bulk(gfp_t gfp, int preferred_nid,
 {
        struct page *page;
        unsigned long flags;
+       unsigned long __maybe_unused UP_flags;
        struct zone *zone;
        struct zoneref *z;
        struct per_cpu_pages *pcp;
@@ -5333,11 +5411,14 @@ unsigned long __alloc_pages_bulk(gfp_t gfp, int preferred_nid,
        if (unlikely(!zone))
                goto failed;
 
+       /* Is a parallel drain in progress? */
+       pcp_trylock_prepare(UP_flags);
+       pcp = pcp_spin_trylock_irqsave(zone->per_cpu_pageset, flags);
+       if (!pcp)
+               goto failed_irq;
+
        /* Attempt the batch allocation */
-       local_lock_irqsave(&pagesets.lock, flags);
-       pcp = this_cpu_ptr(zone->per_cpu_pageset);
        pcp_list = &pcp->lists[order_to_pindex(ac.migratetype, 0)];
-
        while (nr_populated < nr_pages) {
 
                /* Skip existing pages */
@@ -5350,8 +5431,10 @@ unsigned long __alloc_pages_bulk(gfp_t gfp, int preferred_nid,
                                                                pcp, pcp_list);
                if (unlikely(!page)) {
                        /* Try and allocate at least one page */
-                       if (!nr_account)
+                       if (!nr_account) {
+                               pcp_spin_unlock_irqrestore(pcp, flags);
                                goto failed_irq;
+                       }
                        break;
                }
                nr_account++;
@@ -5364,7 +5447,8 @@ unsigned long __alloc_pages_bulk(gfp_t gfp, int preferred_nid,
                nr_populated++;
        }
 
-       local_unlock_irqrestore(&pagesets.lock, flags);
+       pcp_spin_unlock_irqrestore(pcp, flags);
+       pcp_trylock_finish(UP_flags);
 
        __count_zid_vm_events(PGALLOC, zone_idx(zone), nr_account);
        zone_statistics(ac.preferred_zoneref->zone, zone, nr_account);
@@ -5373,7 +5457,7 @@ out:
        return nr_populated;
 
 failed_irq:
-       local_unlock_irqrestore(&pagesets.lock, flags);
+       pcp_trylock_finish(UP_flags);
 
 failed:
        page = __alloc_pages(gfp, 0, preferred_nid, nodemask);
@@ -5804,14 +5888,14 @@ long si_mem_available(void)
 
        /*
         * Estimate the amount of memory available for userspace allocations,
-        * without causing swapping.
+        * without causing swapping or OOM.
         */
        available = global_zone_page_state(NR_FREE_PAGES) - totalreserve_pages;
 
        /*
         * Not all the page cache can be freed, otherwise the system will
-        * start swapping. Assume at least half of the page cache, or the
-        * low watermark worth of cache, needs to stay.
+        * start swapping or thrashing. Assume at least half of the page
+        * cache, or the low watermark worth of cache, needs to stay.
         */
        pagecache = pages[LRU_ACTIVE_FILE] + pages[LRU_INACTIVE_FILE];
        pagecache -= min(pagecache / 2, wmark_low);
@@ -5939,7 +6023,7 @@ static void show_migration_types(unsigned char type)
 void show_free_areas(unsigned int filter, nodemask_t *nodemask)
 {
        unsigned long free_pcp = 0;
-       int cpu;
+       int cpu, nid;
        struct zone *zone;
        pg_data_t *pgdat;
 
@@ -6127,7 +6211,11 @@ void show_free_areas(unsigned int filter, nodemask_t *nodemask)
                printk(KERN_CONT "= %lukB\n", K(total));
        }
 
-       hugetlb_show_meminfo();
+       for_each_online_node(nid) {
+               if (show_mem_node_skip(filter, nid, nodemask))
+                       continue;
+               hugetlb_show_meminfo_node(nid);
+       }
 
        printk("%ld total pagecache pages\n", global_node_page_state(NR_FILE_PAGES));
 
@@ -7013,6 +7101,7 @@ static void per_cpu_pages_init(struct per_cpu_pages *pcp, struct per_cpu_zonesta
        memset(pcp, 0, sizeof(*pcp));
        memset(pzstats, 0, sizeof(*pzstats));
 
+       spin_lock_init(&pcp->lock);
        for (pindex = 0; pindex < NR_PCP_LISTS; pindex++)
                INIT_LIST_HEAD(&pcp->lists[pindex]);
 
index c10f839..8e9e574 100644 (file)
@@ -174,8 +174,7 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw)
                if (!pvmw->pte)
                        return false;
 
-               pvmw->ptl = huge_pte_lockptr(hstate, mm, pvmw->pte);
-               spin_lock(pvmw->ptl);
+               pvmw->ptl = huge_pte_lock(hstate, mm, pvmw->pte);
                if (!check_pte(pvmw))
                        return not_found(pvmw);
                return true;
@@ -243,7 +242,7 @@ restart:
                         * cleared *pmd but not decremented compound_mapcount().
                         */
                        if ((pvmw->flags & PVMW_SYNC) &&
-                           transparent_hugepage_active(vma) &&
+                           transhuge_vma_suitable(vma, pvmw->address) &&
                            (pvmw->nr_pages >= HPAGE_PMD_NR)) {
                                spinlock_t *ptl = pmd_lock(mm, pvmw->pmd);
 
index 3633eee..27697b2 100644 (file)
@@ -3104,7 +3104,7 @@ int __init pcpu_embed_first_chunk(size_t reserved_size, size_t dyn_size,
                        goto out_free_areas;
                }
                /* kmemleak tracks the percpu allocations separately */
-               kmemleak_free(ptr);
+               kmemleak_ignore_phys(__pa(ptr));
                areas[group] = ptr;
 
                base = min(ptr, base);
@@ -3304,7 +3304,7 @@ int __init pcpu_page_first_chunk(size_t reserved_size, pcpu_fc_cpu_to_node_fn_t
                                goto enomem;
                        }
                        /* kmemleak tracks the percpu allocations separately */
-                       kmemleak_free(ptr);
+                       kmemleak_ignore_phys(__pa(ptr));
                        pages[j++] = virt_to_page(ptr);
                }
        }
@@ -3417,7 +3417,7 @@ void __init setup_per_cpu_areas(void)
        if (!ai || !fc)
                panic("Failed to allocate memory for percpu areas.");
        /* kmemleak tracks the percpu allocations separately */
-       kmemleak_free(fc);
+       kmemleak_ignore_phys(__pa(fc));
 
        ai->dyn_size = unit_size;
        ai->unit_size = unit_size;
index 746c05a..edc06c5 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -999,7 +999,7 @@ static int page_vma_mkclean_one(struct page_vma_mapped_walk *pvmw)
                 * downgrading page table protection not changing it to point
                 * to a new page.
                 *
-                * See Documentation/vm/mmu_notifier.rst
+                * See Documentation/mm/mmu_notifier.rst
                 */
                if (ret)
                        cleaned++;
@@ -1537,6 +1537,8 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma,
                                 PageAnonExclusive(subpage);
 
                if (folio_test_hugetlb(folio)) {
+                       bool anon = folio_test_anon(folio);
+
                        /*
                         * The try_to_unmap() is only passed a hugetlb page
                         * in the case where the hugetlb page is poisoned.
@@ -1551,31 +1553,28 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma,
                         */
                        flush_cache_range(vma, range.start, range.end);
 
-                       if (!folio_test_anon(folio)) {
+                       /*
+                        * To call huge_pmd_unshare, i_mmap_rwsem must be
+                        * held in write mode.  Caller needs to explicitly
+                        * do this outside rmap routines.
+                        */
+                       VM_BUG_ON(!anon && !(flags & TTU_RMAP_LOCKED));
+                       if (!anon && huge_pmd_unshare(mm, vma, address, pvmw.pte)) {
+                               flush_tlb_range(vma, range.start, range.end);
+                               mmu_notifier_invalidate_range(mm, range.start,
+                                                             range.end);
+
                                /*
-                                * To call huge_pmd_unshare, i_mmap_rwsem must be
-                                * held in write mode.  Caller needs to explicitly
-                                * do this outside rmap routines.
+                                * The ref count of the PMD page was dropped
+                                * which is part of the way map counting
+                                * is done for shared PMDs.  Return 'true'
+                                * here.  When there is no other sharing,
+                                * huge_pmd_unshare returns false and we will
+                                * unmap the actual page and drop map count
+                                * to zero.
                                 */
-                               VM_BUG_ON(!(flags & TTU_RMAP_LOCKED));
-
-                               if (huge_pmd_unshare(mm, vma, &address, pvmw.pte)) {
-                                       flush_tlb_range(vma, range.start, range.end);
-                                       mmu_notifier_invalidate_range(mm, range.start,
-                                                                     range.end);
-
-                                       /*
-                                        * The ref count of the PMD page was dropped
-                                        * which is part of the way map counting
-                                        * is done for shared PMDs.  Return 'true'
-                                        * here.  When there is no other sharing,
-                                        * huge_pmd_unshare returns false and we will
-                                        * unmap the actual page and drop map count
-                                        * to zero.
-                                        */
-                                       page_vma_mapped_walk_done(&pvmw);
-                                       break;
-                               }
+                               page_vma_mapped_walk_done(&pvmw);
+                               break;
                        }
                        pteval = huge_ptep_clear_flush(vma, address, pvmw.pte);
                } else {
@@ -1619,9 +1618,7 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma,
                        pteval = swp_entry_to_pte(make_hwpoison_entry(subpage));
                        if (folio_test_hugetlb(folio)) {
                                hugetlb_count_sub(folio_nr_pages(folio), mm);
-                               set_huge_swap_pte_at(mm, address,
-                                                    pvmw.pte, pteval,
-                                                    vma_mmu_pagesize(vma));
+                               set_huge_pte_at(mm, address, pvmw.pte, pteval);
                        } else {
                                dec_mm_counter(mm, mm_counter(&folio->page));
                                set_pte_at(mm, address, pvmw.pte, pteval);
@@ -1765,7 +1762,7 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma,
                         * to point at a new folio while a device is
                         * still using this folio.
                         *
-                        * See Documentation/vm/mmu_notifier.rst
+                        * See Documentation/mm/mmu_notifier.rst
                         */
                        dec_mm_counter(mm, mm_counter_file(&folio->page));
                }
@@ -1775,7 +1772,7 @@ discard:
                 * done above for all cases requiring it to happen under page
                 * table lock before mmu_notifier_invalidate_range_end()
                 *
-                * See Documentation/vm/mmu_notifier.rst
+                * See Documentation/mm/mmu_notifier.rst
                 */
                page_remove_rmap(subpage, vma, folio_test_hugetlb(folio));
                if (vma->vm_flags & VM_LOCKED)
@@ -1921,6 +1918,8 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,
                                 PageAnonExclusive(subpage);
 
                if (folio_test_hugetlb(folio)) {
+                       bool anon = folio_test_anon(folio);
+
                        /*
                         * huge_pmd_unshare may unmap an entire PMD page.
                         * There is no way of knowing exactly which PMDs may
@@ -1930,31 +1929,28 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,
                         */
                        flush_cache_range(vma, range.start, range.end);
 
-                       if (!folio_test_anon(folio)) {
+                       /*
+                        * To call huge_pmd_unshare, i_mmap_rwsem must be
+                        * held in write mode.  Caller needs to explicitly
+                        * do this outside rmap routines.
+                        */
+                       VM_BUG_ON(!anon && !(flags & TTU_RMAP_LOCKED));
+                       if (!anon && huge_pmd_unshare(mm, vma, address, pvmw.pte)) {
+                               flush_tlb_range(vma, range.start, range.end);
+                               mmu_notifier_invalidate_range(mm, range.start,
+                                                             range.end);
+
                                /*
-                                * To call huge_pmd_unshare, i_mmap_rwsem must be
-                                * held in write mode.  Caller needs to explicitly
-                                * do this outside rmap routines.
+                                * The ref count of the PMD page was dropped
+                                * which is part of the way map counting
+                                * is done for shared PMDs.  Return 'true'
+                                * here.  When there is no other sharing,
+                                * huge_pmd_unshare returns false and we will
+                                * unmap the actual page and drop map count
+                                * to zero.
                                 */
-                               VM_BUG_ON(!(flags & TTU_RMAP_LOCKED));
-
-                               if (huge_pmd_unshare(mm, vma, &address, pvmw.pte)) {
-                                       flush_tlb_range(vma, range.start, range.end);
-                                       mmu_notifier_invalidate_range(mm, range.start,
-                                                                     range.end);
-
-                                       /*
-                                        * The ref count of the PMD page was dropped
-                                        * which is part of the way map counting
-                                        * is done for shared PMDs.  Return 'true'
-                                        * here.  When there is no other sharing,
-                                        * huge_pmd_unshare returns false and we will
-                                        * unmap the actual page and drop map count
-                                        * to zero.
-                                        */
-                                       page_vma_mapped_walk_done(&pvmw);
-                                       break;
-                               }
+                               page_vma_mapped_walk_done(&pvmw);
+                               break;
                        }
 
                        /* Nuke the hugetlb page table entry */
@@ -1972,7 +1968,7 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,
                /* Update high watermark before we lower rss */
                update_hiwater_rss(mm);
 
-               if (folio_is_zone_device(folio)) {
+               if (folio_is_device_private(folio)) {
                        unsigned long pfn = folio_pfn(folio);
                        swp_entry_t entry;
                        pte_t swp_pte;
@@ -2013,9 +2009,7 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,
                        pteval = swp_entry_to_pte(make_hwpoison_entry(subpage));
                        if (folio_test_hugetlb(folio)) {
                                hugetlb_count_sub(folio_nr_pages(folio), mm);
-                               set_huge_swap_pte_at(mm, address,
-                                                    pvmw.pte, pteval,
-                                                    vma_mmu_pagesize(vma));
+                               set_huge_pte_at(mm, address, pvmw.pte, pteval);
                        } else {
                                dec_mm_counter(mm, mm_counter(&folio->page));
                                set_pte_at(mm, address, pvmw.pte, pteval);
@@ -2083,8 +2077,7 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,
                        if (pte_uffd_wp(pteval))
                                swp_pte = pte_swp_mkuffd_wp(swp_pte);
                        if (folio_test_hugetlb(folio))
-                               set_huge_swap_pte_at(mm, address, pvmw.pte,
-                                                    swp_pte, vma_mmu_pagesize(vma));
+                               set_huge_pte_at(mm, address, pvmw.pte, swp_pte);
                        else
                                set_pte_at(mm, address, pvmw.pte, swp_pte);
                        trace_set_migration_pte(address, pte_val(swp_pte),
@@ -2100,7 +2093,7 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,
                 * done above for all cases requiring it to happen under page
                 * table lock before mmu_notifier_invalidate_range_end()
                 *
-                * See Documentation/vm/mmu_notifier.rst
+                * See Documentation/mm/mmu_notifier.rst
                 */
                page_remove_rmap(subpage, vma, folio_test_hugetlb(folio));
                if (vma->vm_flags & VM_LOCKED)
@@ -2138,7 +2131,8 @@ void try_to_migrate(struct folio *folio, enum ttu_flags flags)
                                        TTU_SYNC)))
                return;
 
-       if (folio_is_zone_device(folio) && !folio_is_device_private(folio))
+       if (folio_is_zone_device(folio) &&
+           (!folio_is_device_private(folio) && !folio_is_device_coherent(folio)))
                return;
 
        /*
index e5e43b9..e975fcd 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/ramfs.h>
 #include <linux/pagemap.h>
 #include <linux/file.h>
+#include <linux/fileattr.h>
 #include <linux/mm.h>
 #include <linux/random.h>
 #include <linux/sched/signal.h>
@@ -1057,6 +1058,15 @@ static int shmem_getattr(struct user_namespace *mnt_userns,
                shmem_recalc_inode(inode);
                spin_unlock_irq(&info->lock);
        }
+       if (info->fsflags & FS_APPEND_FL)
+               stat->attributes |= STATX_ATTR_APPEND;
+       if (info->fsflags & FS_IMMUTABLE_FL)
+               stat->attributes |= STATX_ATTR_IMMUTABLE;
+       if (info->fsflags & FS_NODUMP_FL)
+               stat->attributes |= STATX_ATTR_NODUMP;
+       stat->attributes_mask |= (STATX_ATTR_APPEND |
+                       STATX_ATTR_IMMUTABLE |
+                       STATX_ATTR_NODUMP);
        generic_fillattr(&init_user_ns, inode, stat);
 
        if (shmem_is_huge(NULL, inode, 0))
@@ -1690,7 +1700,7 @@ static void shmem_set_folio_swapin_error(struct inode *inode, pgoff_t index,
                return;
 
        folio_wait_writeback(folio);
-       delete_from_swap_cache(&folio->page);
+       delete_from_swap_cache(folio);
        spin_lock_irq(&info->lock);
        /*
         * Don't treat swapin error folio as alloced. Otherwise inode->i_blocks won't
@@ -1705,10 +1715,10 @@ static void shmem_set_folio_swapin_error(struct inode *inode, pgoff_t index,
 }
 
 /*
- * Swap in the page pointed to by *pagep.
- * Caller has to make sure that *pagep contains a valid swapped page.
- * Returns 0 and the page in pagep if success. On failure, returns the
- * error code and NULL in *pagep.
+ * Swap in the folio pointed to by *foliop.
+ * Caller has to make sure that *foliop contains a valid swapped folio.
+ * Returns 0 and the folio in foliop if success. On failure, returns the
+ * error code and NULL in *foliop.
  */
 static int shmem_swapin_folio(struct inode *inode, pgoff_t index,
                             struct folio **foliop, enum sgp_type sgp,
@@ -1748,7 +1758,7 @@ static int shmem_swapin_folio(struct inode *inode, pgoff_t index,
        }
        folio = page_folio(page);
 
-       /* We have to do this with page locked to prevent races */
+       /* We have to do this with folio locked to prevent races */
        folio_lock(folio);
        if (!folio_test_swapcache(folio) ||
            folio_swap_entry(folio).val != swap.val ||
@@ -1788,7 +1798,7 @@ static int shmem_swapin_folio(struct inode *inode, pgoff_t index,
        if (sgp == SGP_WRITE)
                folio_mark_accessed(folio);
 
-       delete_from_swap_cache(&folio->page);
+       delete_from_swap_cache(folio);
        folio_mark_dirty(folio);
        swap_free(swap);
 
@@ -2271,7 +2281,18 @@ static int shmem_mmap(struct file *file, struct vm_area_struct *vma)
        return 0;
 }
 
-static struct inode *shmem_get_inode(struct super_block *sb, const struct inode *dir,
+/* Mask out flags that are inappropriate for the given type of inode. */
+static unsigned shmem_mask_flags(umode_t mode, __u32 flags)
+{
+       if (S_ISDIR(mode))
+               return flags;
+       else if (S_ISREG(mode))
+               return flags & SHMEM_REG_FLMASK;
+       else
+               return flags & SHMEM_OTHER_FLMASK;
+}
+
+static struct inode *shmem_get_inode(struct super_block *sb, struct inode *dir,
                                     umode_t mode, dev_t dev, unsigned long flags)
 {
        struct inode *inode;
@@ -2296,6 +2317,9 @@ static struct inode *shmem_get_inode(struct super_block *sb, const struct inode
                info->seals = F_SEAL_SEAL;
                info->flags = flags & VM_NORESERVE;
                info->i_crtime = inode->i_mtime;
+               info->fsflags = (dir == NULL) ? 0 :
+                       SHMEM_I(dir)->fsflags & SHMEM_FL_INHERITED;
+               info->fsflags = shmem_mask_flags(mode, info->fsflags);
                INIT_LIST_HEAD(&info->shrinklist);
                INIT_LIST_HEAD(&info->swaplist);
                simple_xattrs_init(&info->xattrs);
@@ -3137,6 +3161,40 @@ static const char *shmem_get_link(struct dentry *dentry,
 }
 
 #ifdef CONFIG_TMPFS_XATTR
+
+static int shmem_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+{
+       struct shmem_inode_info *info = SHMEM_I(d_inode(dentry));
+
+       fileattr_fill_flags(fa, info->fsflags & SHMEM_FL_USER_VISIBLE);
+
+       return 0;
+}
+
+static int shmem_fileattr_set(struct user_namespace *mnt_userns,
+                             struct dentry *dentry, struct fileattr *fa)
+{
+       struct inode *inode = d_inode(dentry);
+       struct shmem_inode_info *info = SHMEM_I(inode);
+
+       if (fileattr_has_fsx(fa))
+               return -EOPNOTSUPP;
+
+       info->fsflags = (info->fsflags & ~SHMEM_FL_USER_MODIFIABLE) |
+               (fa->flags & SHMEM_FL_USER_MODIFIABLE);
+
+       inode->i_flags &= ~(S_APPEND | S_IMMUTABLE | S_NOATIME);
+       if (info->fsflags & FS_APPEND_FL)
+               inode->i_flags |= S_APPEND;
+       if (info->fsflags & FS_IMMUTABLE_FL)
+               inode->i_flags |= S_IMMUTABLE;
+       if (info->fsflags & FS_NOATIME_FL)
+               inode->i_flags |= S_NOATIME;
+
+       inode->i_ctime = current_time(inode);
+       return 0;
+}
+
 /*
  * Superblocks without xattr inode operations may get some security.* xattr
  * support from the LSM "for free". As soon as we have any other xattrs
@@ -3824,6 +3882,8 @@ static const struct inode_operations shmem_inode_operations = {
 #ifdef CONFIG_TMPFS_XATTR
        .listxattr      = shmem_listxattr,
        .set_acl        = simple_set_acl,
+       .fileattr_get   = shmem_fileattr_get,
+       .fileattr_set   = shmem_fileattr_set,
 #endif
 };
 
@@ -3843,6 +3903,8 @@ static const struct inode_operations shmem_dir_inode_operations = {
 #endif
 #ifdef CONFIG_TMPFS_XATTR
        .listxattr      = shmem_listxattr,
+       .fileattr_get   = shmem_fileattr_get,
+       .fileattr_set   = shmem_fileattr_set,
 #endif
 #ifdef CONFIG_TMPFS_POSIX_ACL
        .setattr        = shmem_setattr,
diff --git a/mm/shrinker_debug.c b/mm/shrinker_debug.c
new file mode 100644 (file)
index 0000000..b05295b
--- /dev/null
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/idr.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/shrinker.h>
+#include <linux/memcontrol.h>
+
+/* defined in vmscan.c */
+extern struct rw_semaphore shrinker_rwsem;
+extern struct list_head shrinker_list;
+
+static DEFINE_IDA(shrinker_debugfs_ida);
+static struct dentry *shrinker_debugfs_root;
+
+static unsigned long shrinker_count_objects(struct shrinker *shrinker,
+                                           struct mem_cgroup *memcg,
+                                           unsigned long *count_per_node)
+{
+       unsigned long nr, total = 0;
+       int nid;
+
+       for_each_node(nid) {
+               if (nid == 0 || (shrinker->flags & SHRINKER_NUMA_AWARE)) {
+                       struct shrink_control sc = {
+                               .gfp_mask = GFP_KERNEL,
+                               .nid = nid,
+                               .memcg = memcg,
+                       };
+
+                       nr = shrinker->count_objects(shrinker, &sc);
+                       if (nr == SHRINK_EMPTY)
+                               nr = 0;
+               } else {
+                       nr = 0;
+               }
+
+               count_per_node[nid] = nr;
+               total += nr;
+       }
+
+       return total;
+}
+
+static int shrinker_debugfs_count_show(struct seq_file *m, void *v)
+{
+       struct shrinker *shrinker = m->private;
+       unsigned long *count_per_node;
+       struct mem_cgroup *memcg;
+       unsigned long total;
+       bool memcg_aware;
+       int ret, nid;
+
+       count_per_node = kcalloc(nr_node_ids, sizeof(unsigned long), GFP_KERNEL);
+       if (!count_per_node)
+               return -ENOMEM;
+
+       ret = down_read_killable(&shrinker_rwsem);
+       if (ret) {
+               kfree(count_per_node);
+               return ret;
+       }
+       rcu_read_lock();
+
+       memcg_aware = shrinker->flags & SHRINKER_MEMCG_AWARE;
+
+       memcg = mem_cgroup_iter(NULL, NULL, NULL);
+       do {
+               if (memcg && !mem_cgroup_online(memcg))
+                       continue;
+
+               total = shrinker_count_objects(shrinker,
+                                              memcg_aware ? memcg : NULL,
+                                              count_per_node);
+               if (total) {
+                       seq_printf(m, "%lu", mem_cgroup_ino(memcg));
+                       for_each_node(nid)
+                               seq_printf(m, " %lu", count_per_node[nid]);
+                       seq_putc(m, '\n');
+               }
+
+               if (!memcg_aware) {
+                       mem_cgroup_iter_break(NULL, memcg);
+                       break;
+               }
+
+               if (signal_pending(current)) {
+                       mem_cgroup_iter_break(NULL, memcg);
+                       ret = -EINTR;
+                       break;
+               }
+       } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)) != NULL);
+
+       rcu_read_unlock();
+       up_read(&shrinker_rwsem);
+
+       kfree(count_per_node);
+       return ret;
+}
+DEFINE_SHOW_ATTRIBUTE(shrinker_debugfs_count);
+
+static int shrinker_debugfs_scan_open(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return nonseekable_open(inode, file);
+}
+
+static ssize_t shrinker_debugfs_scan_write(struct file *file,
+                                          const char __user *buf,
+                                          size_t size, loff_t *pos)
+{
+       struct shrinker *shrinker = file->private_data;
+       unsigned long nr_to_scan = 0, ino, read_len;
+       struct shrink_control sc = {
+               .gfp_mask = GFP_KERNEL,
+       };
+       struct mem_cgroup *memcg = NULL;
+       int nid;
+       char kbuf[72];
+       ssize_t ret;
+
+       read_len = size < (sizeof(kbuf) - 1) ? size : (sizeof(kbuf) - 1);
+       if (copy_from_user(kbuf, buf, read_len))
+               return -EFAULT;
+       kbuf[read_len] = '\0';
+
+       if (sscanf(kbuf, "%lu %d %lu", &ino, &nid, &nr_to_scan) != 3)
+               return -EINVAL;
+
+       if (nid < 0 || nid >= nr_node_ids)
+               return -EINVAL;
+
+       if (nr_to_scan == 0)
+               return size;
+
+       if (shrinker->flags & SHRINKER_MEMCG_AWARE) {
+               memcg = mem_cgroup_get_from_ino(ino);
+               if (!memcg || IS_ERR(memcg))
+                       return -ENOENT;
+
+               if (!mem_cgroup_online(memcg)) {
+                       mem_cgroup_put(memcg);
+                       return -ENOENT;
+               }
+       } else if (ino != 0) {
+               return -EINVAL;
+       }
+
+       ret = down_read_killable(&shrinker_rwsem);
+       if (ret) {
+               mem_cgroup_put(memcg);
+               return ret;
+       }
+
+       sc.nid = nid;
+       sc.memcg = memcg;
+       sc.nr_to_scan = nr_to_scan;
+       sc.nr_scanned = nr_to_scan;
+
+       shrinker->scan_objects(shrinker, &sc);
+
+       up_read(&shrinker_rwsem);
+       mem_cgroup_put(memcg);
+
+       return size;
+}
+
+static const struct file_operations shrinker_debugfs_scan_fops = {
+       .owner   = THIS_MODULE,
+       .open    = shrinker_debugfs_scan_open,
+       .write   = shrinker_debugfs_scan_write,
+};
+
+int shrinker_debugfs_add(struct shrinker *shrinker)
+{
+       struct dentry *entry;
+       char buf[128];
+       int id;
+
+       lockdep_assert_held(&shrinker_rwsem);
+
+       /* debugfs isn't initialized yet, add debugfs entries later. */
+       if (!shrinker_debugfs_root)
+               return 0;
+
+       id = ida_alloc(&shrinker_debugfs_ida, GFP_KERNEL);
+       if (id < 0)
+               return id;
+       shrinker->debugfs_id = id;
+
+       snprintf(buf, sizeof(buf), "%s-%d", shrinker->name, id);
+
+       /* create debugfs entry */
+       entry = debugfs_create_dir(buf, shrinker_debugfs_root);
+       if (IS_ERR(entry)) {
+               ida_free(&shrinker_debugfs_ida, id);
+               return PTR_ERR(entry);
+       }
+       shrinker->debugfs_entry = entry;
+
+       debugfs_create_file("count", 0220, entry, shrinker,
+                           &shrinker_debugfs_count_fops);
+       debugfs_create_file("scan", 0440, entry, shrinker,
+                           &shrinker_debugfs_scan_fops);
+       return 0;
+}
+
+int shrinker_debugfs_rename(struct shrinker *shrinker, const char *fmt, ...)
+{
+       struct dentry *entry;
+       char buf[128];
+       const char *new, *old;
+       va_list ap;
+       int ret = 0;
+
+       va_start(ap, fmt);
+       new = kvasprintf_const(GFP_KERNEL, fmt, ap);
+       va_end(ap);
+
+       if (!new)
+               return -ENOMEM;
+
+       down_write(&shrinker_rwsem);
+
+       old = shrinker->name;
+       shrinker->name = new;
+
+       if (shrinker->debugfs_entry) {
+               snprintf(buf, sizeof(buf), "%s-%d", shrinker->name,
+                        shrinker->debugfs_id);
+
+               entry = debugfs_rename(shrinker_debugfs_root,
+                                      shrinker->debugfs_entry,
+                                      shrinker_debugfs_root, buf);
+               if (IS_ERR(entry))
+                       ret = PTR_ERR(entry);
+               else
+                       shrinker->debugfs_entry = entry;
+       }
+
+       up_write(&shrinker_rwsem);
+
+       kfree_const(old);
+
+       return ret;
+}
+EXPORT_SYMBOL(shrinker_debugfs_rename);
+
+void shrinker_debugfs_remove(struct shrinker *shrinker)
+{
+       lockdep_assert_held(&shrinker_rwsem);
+
+       kfree_const(shrinker->name);
+       shrinker->name = NULL;
+
+       if (!shrinker->debugfs_entry)
+               return;
+
+       debugfs_remove_recursive(shrinker->debugfs_entry);
+       ida_free(&shrinker_debugfs_ida, shrinker->debugfs_id);
+}
+
+static int __init shrinker_debugfs_init(void)
+{
+       struct shrinker *shrinker;
+       struct dentry *dentry;
+       int ret = 0;
+
+       dentry = debugfs_create_dir("shrinker", NULL);
+       if (IS_ERR(dentry))
+               return PTR_ERR(dentry);
+       shrinker_debugfs_root = dentry;
+
+       /* Create debugfs entries for shrinkers registered at boot */
+       down_write(&shrinker_rwsem);
+       list_for_each_entry(shrinker, &shrinker_list, list)
+               if (!shrinker->debugfs_entry) {
+                       ret = shrinker_debugfs_add(shrinker);
+                       if (ret)
+                               break;
+               }
+       up_write(&shrinker_rwsem);
+
+       return ret;
+}
+late_initcall(shrinker_debugfs_init);
index 5e73e2d..10e9613 100644 (file)
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -2958,12 +2958,6 @@ direct_grow:
        return ac->entry[--ac->avail];
 }
 
-static inline void cache_alloc_debugcheck_before(struct kmem_cache *cachep,
-                                               gfp_t flags)
-{
-       might_sleep_if(gfpflags_allow_blocking(flags));
-}
-
 #if DEBUG
 static void *cache_alloc_debugcheck_after(struct kmem_cache *cachep,
                                gfp_t flags, void *objp, unsigned long caller)
@@ -3205,7 +3199,6 @@ slab_alloc_node(struct kmem_cache *cachep, gfp_t flags, int nodeid, size_t orig_
        if (unlikely(ptr))
                goto out_hooks;
 
-       cache_alloc_debugcheck_before(cachep, flags);
        local_irq_save(save_flags);
 
        if (nodeid == NUMA_NO_NODE)
@@ -3290,7 +3283,6 @@ slab_alloc(struct kmem_cache *cachep, struct list_lru *lru, gfp_t flags,
        if (unlikely(objp))
                goto out;
 
-       cache_alloc_debugcheck_before(cachep, flags);
        local_irq_save(save_flags);
        objp = __do_cache_alloc(cachep, flags);
        local_irq_restore(save_flags);
@@ -3527,8 +3519,6 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
        if (!s)
                return 0;
 
-       cache_alloc_debugcheck_before(s, flags);
-
        local_irq_disable();
        for (i = 0; i < size; i++) {
                void *objp = kfence_alloc(s, s->object_size, flags) ?: __do_cache_alloc(s, flags);
index 3f7e4bd..5f0ed47 100644 (file)
@@ -208,8 +208,8 @@ static int vmemmap_remap_range(unsigned long start, unsigned long end,
        unsigned long next;
        pgd_t *pgd;
 
-       VM_BUG_ON(!IS_ALIGNED(start, PAGE_SIZE));
-       VM_BUG_ON(!IS_ALIGNED(end, PAGE_SIZE));
+       VM_BUG_ON(!PAGE_ALIGNED(start));
+       VM_BUG_ON(!PAGE_ALIGNED(end));
 
        pgd = pgd_offset_k(addr);
        do {
@@ -556,7 +556,7 @@ pte_t * __meminit vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, int node,
                } else {
                        /*
                         * When a PTE/PMD entry is freed from the init_mm
-                        * there's a free_pages() call to this page allocated
+                        * there's a free_pages() call to this page allocated
                         * above. Thus this get_page() is paired with the
                         * put_page_testzero() on the freeing path.
                         * This can only called by certain ZONE_DEVICE path,
@@ -745,7 +745,7 @@ static int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn,
 
        size = min(end - start, pgmap_vmemmap_nr(pgmap) * sizeof(struct page));
        for (addr = start; addr < end; addr += size) {
-               unsigned long next = addr, last = addr + size;
+               unsigned long next, last = addr + size;
 
                /* Populate the head page vmemmap page */
                pte = vmemmap_populate_address(addr, node, NULL, NULL);
@@ -760,7 +760,7 @@ static int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn,
 
                /*
                 * Reuse the previous page for the rest of tail pages
-                * See layout diagram in Documentation/vm/vmemmap_dedup.rst
+                * See layout diagram in Documentation/mm/vmemmap_dedup.rst
                 */
                next += PAGE_SIZE;
                rc = vmemmap_populate_range(next, last, node, NULL,
index cb3bfae..e5a8a3a 100644 (file)
@@ -281,7 +281,7 @@ static unsigned long sparse_encode_mem_map(struct page *mem_map, unsigned long p
 {
        unsigned long coded_mem_map =
                (unsigned long)(mem_map - (section_nr_to_pfn(pnum)));
-       BUILD_BUG_ON(SECTION_MAP_LAST_BIT > (1UL<<PFN_SECTION_SHIFT));
+       BUILD_BUG_ON(SECTION_MAP_LAST_BIT > PFN_SECTION_SHIFT);
        BUG_ON(coded_mem_map & ~SECTION_MAP_MASK);
        return coded_mem_map;
 }
index 275a4ea..9cee7f6 100644 (file)
--- a/mm/swap.c
+++ b/mm/swap.c
 /* How many pages do we try to swap or page in/out together? */
 int page_cluster;
 
-/* Protecting only lru_rotate.pvec which requires disabling interrupts */
+/* Protecting only lru_rotate.fbatch which requires disabling interrupts */
 struct lru_rotate {
        local_lock_t lock;
-       struct pagevec pvec;
+       struct folio_batch fbatch;
 };
 static DEFINE_PER_CPU(struct lru_rotate, lru_rotate) = {
        .lock = INIT_LOCAL_LOCK(lock),
 };
 
 /*
- * The following struct pagevec are grouped together because they are protected
+ * The following folio batches are grouped together because they are protected
  * by disabling preemption (and interrupts remain enabled).
  */
-struct lru_pvecs {
+struct cpu_fbatches {
        local_lock_t lock;
-       struct pagevec lru_add;
-       struct pagevec lru_deactivate_file;
-       struct pagevec lru_deactivate;
-       struct pagevec lru_lazyfree;
+       struct folio_batch lru_add;
+       struct folio_batch lru_deactivate_file;
+       struct folio_batch lru_deactivate;
+       struct folio_batch lru_lazyfree;
 #ifdef CONFIG_SMP
-       struct pagevec activate_page;
+       struct folio_batch activate;
 #endif
 };
-static DEFINE_PER_CPU(struct lru_pvecs, lru_pvecs) = {
+static DEFINE_PER_CPU(struct cpu_fbatches, cpu_fbatches) = {
        .lock = INIT_LOCAL_LOCK(lock),
 };
 
@@ -77,36 +77,35 @@ static DEFINE_PER_CPU(struct lru_pvecs, lru_pvecs) = {
  * This path almost never happens for VM activity - pages are normally freed
  * via pagevecs.  But it gets used by networking - and for compound pages.
  */
-static void __page_cache_release(struct page *page)
+static void __page_cache_release(struct folio *folio)
 {
-       if (PageLRU(page)) {
-               struct folio *folio = page_folio(page);
+       if (folio_test_lru(folio)) {
                struct lruvec *lruvec;
                unsigned long flags;
 
                lruvec = folio_lruvec_lock_irqsave(folio, &flags);
-               del_page_from_lru_list(page, lruvec);
-               __clear_page_lru_flags(page);
+               lruvec_del_folio(lruvec, folio);
+               __folio_clear_lru_flags(folio);
                unlock_page_lruvec_irqrestore(lruvec, flags);
        }
-       /* See comment on PageMlocked in release_pages() */
-       if (unlikely(PageMlocked(page))) {
-               int nr_pages = thp_nr_pages(page);
+       /* See comment on folio_test_mlocked in release_pages() */
+       if (unlikely(folio_test_mlocked(folio))) {
+               long nr_pages = folio_nr_pages(folio);
 
-               __ClearPageMlocked(page);
-               mod_zone_page_state(page_zone(page), NR_MLOCK, -nr_pages);
+               __folio_clear_mlocked(folio);
+               zone_stat_mod_folio(folio, NR_MLOCK, -nr_pages);
                count_vm_events(UNEVICTABLE_PGCLEARED, nr_pages);
        }
 }
 
-static void __put_single_page(struct page *page)
+static void __folio_put_small(struct folio *folio)
 {
-       __page_cache_release(page);
-       mem_cgroup_uncharge(page_folio(page));
-       free_unref_page(page, 0);
+       __page_cache_release(folio);
+       mem_cgroup_uncharge(folio);
+       free_unref_page(&folio->page, 0);
 }
 
-static void __put_compound_page(struct page *page)
+static void __folio_put_large(struct folio *folio)
 {
        /*
         * __page_cache_release() is supposed to be called for thp, not for
@@ -114,21 +113,21 @@ static void __put_compound_page(struct page *page)
         * (it's never listed to any LRU lists) and no memcg routines should
         * be called for hugetlb (it has a separate hugetlb_cgroup.)
         */
-       if (!PageHuge(page))
-               __page_cache_release(page);
-       destroy_compound_page(page);
+       if (!folio_test_hugetlb(folio))
+               __page_cache_release(folio);
+       destroy_large_folio(folio);
 }
 
-void __put_page(struct page *page)
+void __folio_put(struct folio *folio)
 {
-       if (unlikely(is_zone_device_page(page)))
-               free_zone_device_page(page);
-       else if (unlikely(PageCompound(page)))
-               __put_compound_page(page);
+       if (unlikely(folio_is_zone_device(folio)))
+               free_zone_device_page(&folio->page);
+       else if (unlikely(folio_test_large(folio)))
+               __folio_put_large(folio);
        else
-               __put_single_page(page);
+               __folio_put_small(folio);
 }
-EXPORT_SYMBOL(__put_page);
+EXPORT_SYMBOL(__folio_put);
 
 /**
  * put_pages_list() - release a list of pages
@@ -138,19 +137,19 @@ EXPORT_SYMBOL(__put_page);
  */
 void put_pages_list(struct list_head *pages)
 {
-       struct page *page, *next;
+       struct folio *folio, *next;
 
-       list_for_each_entry_safe(page, next, pages, lru) {
-               if (!put_page_testzero(page)) {
-                       list_del(&page->lru);
+       list_for_each_entry_safe(folio, next, pages, lru) {
+               if (!folio_put_testzero(folio)) {
+                       list_del(&folio->lru);
                        continue;
                }
-               if (PageHead(page)) {
-                       list_del(&page->lru);
-                       __put_compound_page(page);
+               if (folio_test_large(folio)) {
+                       list_del(&folio->lru);
+                       __folio_put_large(folio);
                        continue;
                }
-               /* Cannot be PageLRU because it's passed to us using the lru */
+               /* LRU flag must be clear because it's passed using the lru */
        }
 
        free_unref_page_list(pages);
@@ -188,36 +187,84 @@ int get_kernel_pages(const struct kvec *kiov, int nr_segs, int write,
 }
 EXPORT_SYMBOL_GPL(get_kernel_pages);
 
-static void pagevec_lru_move_fn(struct pagevec *pvec,
-       void (*move_fn)(struct page *page, struct lruvec *lruvec))
+typedef void (*move_fn_t)(struct lruvec *lruvec, struct folio *folio);
+
+static void lru_add_fn(struct lruvec *lruvec, struct folio *folio)
+{
+       int was_unevictable = folio_test_clear_unevictable(folio);
+       long nr_pages = folio_nr_pages(folio);
+
+       VM_BUG_ON_FOLIO(folio_test_lru(folio), folio);
+
+       /*
+        * Is an smp_mb__after_atomic() still required here, before
+        * folio_evictable() tests the mlocked flag, to rule out the possibility
+        * of stranding an evictable folio on an unevictable LRU?  I think
+        * not, because __munlock_page() only clears the mlocked flag
+        * while the LRU lock is held.
+        *
+        * (That is not true of __page_cache_release(), and not necessarily
+        * true of release_pages(): but those only clear the mlocked flag after
+        * folio_put_testzero() has excluded any other users of the folio.)
+        */
+       if (folio_evictable(folio)) {
+               if (was_unevictable)
+                       __count_vm_events(UNEVICTABLE_PGRESCUED, nr_pages);
+       } else {
+               folio_clear_active(folio);
+               folio_set_unevictable(folio);
+               /*
+                * folio->mlock_count = !!folio_test_mlocked(folio)?
+                * But that leaves __mlock_page() in doubt whether another
+                * actor has already counted the mlock or not.  Err on the
+                * safe side, underestimate, let page reclaim fix it, rather
+                * than leaving a page on the unevictable LRU indefinitely.
+                */
+               folio->mlock_count = 0;
+               if (!was_unevictable)
+                       __count_vm_events(UNEVICTABLE_PGCULLED, nr_pages);
+       }
+
+       lruvec_add_folio(lruvec, folio);
+       trace_mm_lru_insertion(folio);
+}
+
+static void folio_batch_move_lru(struct folio_batch *fbatch, move_fn_t move_fn)
 {
        int i;
        struct lruvec *lruvec = NULL;
        unsigned long flags = 0;
 
-       for (i = 0; i < pagevec_count(pvec); i++) {
-               struct page *page = pvec->pages[i];
-               struct folio *folio = page_folio(page);
+       for (i = 0; i < folio_batch_count(fbatch); i++) {
+               struct folio *folio = fbatch->folios[i];
 
-               /* block memcg migration during page moving between lru */
-               if (!TestClearPageLRU(page))
+               /* block memcg migration while the folio moves between lru */
+               if (move_fn != lru_add_fn && !folio_test_clear_lru(folio))
                        continue;
 
                lruvec = folio_lruvec_relock_irqsave(folio, lruvec, &flags);
-               (*move_fn)(page, lruvec);
+               move_fn(lruvec, folio);
 
-               SetPageLRU(page);
+               folio_set_lru(folio);
        }
+
        if (lruvec)
                unlock_page_lruvec_irqrestore(lruvec, flags);
-       release_pages(pvec->pages, pvec->nr);
-       pagevec_reinit(pvec);
+       folios_put(fbatch->folios, folio_batch_count(fbatch));
+       folio_batch_init(fbatch);
 }
 
-static void pagevec_move_tail_fn(struct page *page, struct lruvec *lruvec)
+static void folio_batch_add_and_move(struct folio_batch *fbatch,
+               struct folio *folio, move_fn_t move_fn)
 {
-       struct folio *folio = page_folio(page);
+       if (folio_batch_add(fbatch, folio) && !folio_test_large(folio) &&
+           !lru_cache_disabled())
+               return;
+       folio_batch_move_lru(fbatch, move_fn);
+}
 
+static void lru_move_tail_fn(struct lruvec *lruvec, struct folio *folio)
+{
        if (!folio_test_unevictable(folio)) {
                lruvec_del_folio(lruvec, folio);
                folio_clear_active(folio);
@@ -226,18 +273,6 @@ static void pagevec_move_tail_fn(struct page *page, struct lruvec *lruvec)
        }
 }
 
-/* return true if pagevec needs to drain */
-static bool pagevec_add_and_need_flush(struct pagevec *pvec, struct page *page)
-{
-       bool ret = false;
-
-       if (!pagevec_add(pvec, page) || PageCompound(page) ||
-                       lru_cache_disabled())
-               ret = true;
-
-       return ret;
-}
-
 /*
  * Writeback is about to end against a folio which has been marked for
  * immediate reclaim.  If it still appears to be reclaimable, move it
@@ -249,14 +284,13 @@ void folio_rotate_reclaimable(struct folio *folio)
 {
        if (!folio_test_locked(folio) && !folio_test_dirty(folio) &&
            !folio_test_unevictable(folio) && folio_test_lru(folio)) {
-               struct pagevec *pvec;
+               struct folio_batch *fbatch;
                unsigned long flags;
 
                folio_get(folio);
                local_lock_irqsave(&lru_rotate.lock, flags);
-               pvec = this_cpu_ptr(&lru_rotate.pvec);
-               if (pagevec_add_and_need_flush(pvec, &folio->page))
-                       pagevec_lru_move_fn(pvec, pagevec_move_tail_fn);
+               fbatch = this_cpu_ptr(&lru_rotate.fbatch);
+               folio_batch_add_and_move(fbatch, folio, lru_move_tail_fn);
                local_unlock_irqrestore(&lru_rotate.lock, flags);
        }
 }
@@ -307,7 +341,7 @@ void lru_note_cost_folio(struct folio *folio)
                        folio_nr_pages(folio));
 }
 
-static void __folio_activate(struct folio *folio, struct lruvec *lruvec)
+static void folio_activate_fn(struct lruvec *lruvec, struct folio *folio)
 {
        if (!folio_test_active(folio) && !folio_test_unevictable(folio)) {
                long nr_pages = folio_nr_pages(folio);
@@ -324,41 +358,30 @@ static void __folio_activate(struct folio *folio, struct lruvec *lruvec)
 }
 
 #ifdef CONFIG_SMP
-static void __activate_page(struct page *page, struct lruvec *lruvec)
-{
-       return __folio_activate(page_folio(page), lruvec);
-}
-
-static void activate_page_drain(int cpu)
+static void folio_activate_drain(int cpu)
 {
-       struct pagevec *pvec = &per_cpu(lru_pvecs.activate_page, cpu);
+       struct folio_batch *fbatch = &per_cpu(cpu_fbatches.activate, cpu);
 
-       if (pagevec_count(pvec))
-               pagevec_lru_move_fn(pvec, __activate_page);
-}
-
-static bool need_activate_page_drain(int cpu)
-{
-       return pagevec_count(&per_cpu(lru_pvecs.activate_page, cpu)) != 0;
+       if (folio_batch_count(fbatch))
+               folio_batch_move_lru(fbatch, folio_activate_fn);
 }
 
 static void folio_activate(struct folio *folio)
 {
        if (folio_test_lru(folio) && !folio_test_active(folio) &&
            !folio_test_unevictable(folio)) {
-               struct pagevec *pvec;
+               struct folio_batch *fbatch;
 
                folio_get(folio);
-               local_lock(&lru_pvecs.lock);
-               pvec = this_cpu_ptr(&lru_pvecs.activate_page);
-               if (pagevec_add_and_need_flush(pvec, &folio->page))
-                       pagevec_lru_move_fn(pvec, __activate_page);
-               local_unlock(&lru_pvecs.lock);
+               local_lock(&cpu_fbatches.lock);
+               fbatch = this_cpu_ptr(&cpu_fbatches.activate);
+               folio_batch_add_and_move(fbatch, folio, folio_activate_fn);
+               local_unlock(&cpu_fbatches.lock);
        }
 }
 
 #else
-static inline void activate_page_drain(int cpu)
+static inline void folio_activate_drain(int cpu)
 {
 }
 
@@ -368,7 +391,7 @@ static void folio_activate(struct folio *folio)
 
        if (folio_test_clear_lru(folio)) {
                lruvec = folio_lruvec_lock_irq(folio);
-               __folio_activate(folio, lruvec);
+               folio_activate_fn(lruvec, folio);
                unlock_page_lruvec_irq(lruvec);
                folio_set_lru(folio);
        }
@@ -377,32 +400,32 @@ static void folio_activate(struct folio *folio)
 
 static void __lru_cache_activate_folio(struct folio *folio)
 {
-       struct pagevec *pvec;
+       struct folio_batch *fbatch;
        int i;
 
-       local_lock(&lru_pvecs.lock);
-       pvec = this_cpu_ptr(&lru_pvecs.lru_add);
+       local_lock(&cpu_fbatches.lock);
+       fbatch = this_cpu_ptr(&cpu_fbatches.lru_add);
 
        /*
-        * Search backwards on the optimistic assumption that the page being
-        * activated has just been added to this pagevec. Note that only
-        * the local pagevec is examined as a !PageLRU page could be in the
+        * Search backwards on the optimistic assumption that the folio being
+        * activated has just been added to this batch. Note that only
+        * the local batch is examined as a !LRU folio could be in the
         * process of being released, reclaimed, migrated or on a remote
-        * pagevec that is currently being drained. Furthermore, marking
-        * a remote pagevec's page PageActive potentially hits a race where
-        * a page is marked PageActive just after it is added to the inactive
+        * batch that is currently being drained. Furthermore, marking
+        * a remote batch's folio active potentially hits a race where
+        * a folio is marked active just after it is added to the inactive
         * list causing accounting errors and BUG_ON checks to trigger.
         */
-       for (i = pagevec_count(pvec) - 1; i >= 0; i--) {
-               struct page *pagevec_page = pvec->pages[i];
+       for (i = folio_batch_count(fbatch) - 1; i >= 0; i--) {
+               struct folio *batch_folio = fbatch->folios[i];
 
-               if (pagevec_page == &folio->page) {
+               if (batch_folio == folio) {
                        folio_set_active(folio);
                        break;
                }
        }
 
-       local_unlock(&lru_pvecs.lock);
+       local_unlock(&cpu_fbatches.lock);
 }
 
 /*
@@ -427,9 +450,9 @@ void folio_mark_accessed(struct folio *folio)
                 */
        } else if (!folio_test_active(folio)) {
                /*
-                * If the page is on the LRU, queue it for activation via
-                * lru_pvecs.activate_page. Otherwise, assume the page is on a
-                * pagevec, mark it active and it'll be moved to the active
+                * If the folio is on the LRU, queue it for activation via
+                * cpu_fbatches.activate. Otherwise, assume the folio is in a
+                * folio_batch, mark it active and it'll be moved to the active
                 * LRU on the next drain.
                 */
                if (folio_test_lru(folio))
@@ -450,22 +473,22 @@ EXPORT_SYMBOL(folio_mark_accessed);
  *
  * Queue the folio for addition to the LRU. The decision on whether
  * to add the page to the [in]active [file|anon] list is deferred until the
- * pagevec is drained. This gives a chance for the caller of folio_add_lru()
+ * folio_batch is drained. This gives a chance for the caller of folio_add_lru()
  * have the folio added to the active list using folio_mark_accessed().
  */
 void folio_add_lru(struct folio *folio)
 {
-       struct pagevec *pvec;
+       struct folio_batch *fbatch;
 
-       VM_BUG_ON_FOLIO(folio_test_active(folio) && folio_test_unevictable(folio), folio);
+       VM_BUG_ON_FOLIO(folio_test_active(folio) &&
+                       folio_test_unevictable(folio), folio);
        VM_BUG_ON_FOLIO(folio_test_lru(folio), folio);
 
        folio_get(folio);
-       local_lock(&lru_pvecs.lock);
-       pvec = this_cpu_ptr(&lru_pvecs.lru_add);
-       if (pagevec_add_and_need_flush(pvec, &folio->page))
-               __pagevec_lru_add(pvec);
-       local_unlock(&lru_pvecs.lock);
+       local_lock(&cpu_fbatches.lock);
+       fbatch = this_cpu_ptr(&cpu_fbatches.lru_add);
+       folio_batch_add_and_move(fbatch, folio, lru_add_fn);
+       local_unlock(&cpu_fbatches.lock);
 }
 EXPORT_SYMBOL(folio_add_lru);
 
@@ -489,56 +512,57 @@ void lru_cache_add_inactive_or_unevictable(struct page *page,
 }
 
 /*
- * If the page can not be invalidated, it is moved to the
+ * If the folio cannot be invalidated, it is moved to the
  * inactive list to speed up its reclaim.  It is moved to the
  * head of the list, rather than the tail, to give the flusher
  * threads some time to write it out, as this is much more
  * effective than the single-page writeout from reclaim.
  *
- * If the page isn't page_mapped and dirty/writeback, the page
- * could reclaim asap using PG_reclaim.
+ * If the folio isn't mapped and dirty/writeback, the folio
+ * could be reclaimed asap using the reclaim flag.
  *
- * 1. active, mapped page -> none
- * 2. active, dirty/writeback page -> inactive, head, PG_reclaim
- * 3. inactive, mapped page -> none
- * 4. inactive, dirty/writeback page -> inactive, head, PG_reclaim
+ * 1. active, mapped folio -> none
+ * 2. active, dirty/writeback folio -> inactive, head, reclaim
+ * 3. inactive, mapped folio -> none
+ * 4. inactive, dirty/writeback folio -> inactive, head, reclaim
  * 5. inactive, clean -> inactive, tail
  * 6. Others -> none
  *
- * In 4, why it moves inactive's head, the VM expects the page would
- * be write it out by flusher threads as this is much more effective
+ * In 4, it moves to the head of the inactive list so the folio is
+ * written out by flusher threads as this is much more efficient
  * than the single-page writeout from reclaim.
  */
-static void lru_deactivate_file_fn(struct page *page, struct lruvec *lruvec)
+static void lru_deactivate_file_fn(struct lruvec *lruvec, struct folio *folio)
 {
-       bool active = PageActive(page);
-       int nr_pages = thp_nr_pages(page);
+       bool active = folio_test_active(folio);
+       long nr_pages = folio_nr_pages(folio);
 
-       if (PageUnevictable(page))
+       if (folio_test_unevictable(folio))
                return;
 
-       /* Some processes are using the page */
-       if (page_mapped(page))
+       /* Some processes are using the folio */
+       if (folio_mapped(folio))
                return;
 
-       del_page_from_lru_list(page, lruvec);
-       ClearPageActive(page);
-       ClearPageReferenced(page);
+       lruvec_del_folio(lruvec, folio);
+       folio_clear_active(folio);
+       folio_clear_referenced(folio);
 
-       if (PageWriteback(page) || PageDirty(page)) {
+       if (folio_test_writeback(folio) || folio_test_dirty(folio)) {
                /*
-                * PG_reclaim could be raced with end_page_writeback
-                * It can make readahead confusing.  But race window
-                * is _really_ small and  it's non-critical problem.
+                * Setting the reclaim flag could race with
+                * folio_end_writeback() and confuse readahead.  But the
+                * race window is _really_ small and  it's not a critical
+                * problem.
                 */
-               add_page_to_lru_list(page, lruvec);
-               SetPageReclaim(page);
+               lruvec_add_folio(lruvec, folio);
+               folio_set_reclaim(folio);
        } else {
                /*
-                * The page's writeback ends up during pagevec
-                * We move that page into tail of inactive.
+                * The folio's writeback ended while it was in the batch.
+                * We move that folio to the tail of the inactive list.
                 */
-               add_page_to_lru_list_tail(page, lruvec);
+               lruvec_add_folio_tail(lruvec, folio);
                __count_vm_events(PGROTATED, nr_pages);
        }
 
@@ -549,15 +573,15 @@ static void lru_deactivate_file_fn(struct page *page, struct lruvec *lruvec)
        }
 }
 
-static void lru_deactivate_fn(struct page *page, struct lruvec *lruvec)
+static void lru_deactivate_fn(struct lruvec *lruvec, struct folio *folio)
 {
-       if (PageActive(page) && !PageUnevictable(page)) {
-               int nr_pages = thp_nr_pages(page);
+       if (folio_test_active(folio) && !folio_test_unevictable(folio)) {
+               long nr_pages = folio_nr_pages(folio);
 
-               del_page_from_lru_list(page, lruvec);
-               ClearPageActive(page);
-               ClearPageReferenced(page);
-               add_page_to_lru_list(page, lruvec);
+               lruvec_del_folio(lruvec, folio);
+               folio_clear_active(folio);
+               folio_clear_referenced(folio);
+               lruvec_add_folio(lruvec, folio);
 
                __count_vm_events(PGDEACTIVATE, nr_pages);
                __count_memcg_events(lruvec_memcg(lruvec), PGDEACTIVATE,
@@ -565,22 +589,22 @@ static void lru_deactivate_fn(struct page *page, struct lruvec *lruvec)
        }
 }
 
-static void lru_lazyfree_fn(struct page *page, struct lruvec *lruvec)
+static void lru_lazyfree_fn(struct lruvec *lruvec, struct folio *folio)
 {
-       if (PageAnon(page) && PageSwapBacked(page) &&
-           !PageSwapCache(page) && !PageUnevictable(page)) {
-               int nr_pages = thp_nr_pages(page);
+       if (folio_test_anon(folio) && folio_test_swapbacked(folio) &&
+           !folio_test_swapcache(folio) && !folio_test_unevictable(folio)) {
+               long nr_pages = folio_nr_pages(folio);
 
-               del_page_from_lru_list(page, lruvec);
-               ClearPageActive(page);
-               ClearPageReferenced(page);
+               lruvec_del_folio(lruvec, folio);
+               folio_clear_active(folio);
+               folio_clear_referenced(folio);
                /*
-                * Lazyfree pages are clean anonymous pages.  They have
-                * PG_swapbacked flag cleared, to distinguish them from normal
-                * anonymous pages
+                * Lazyfree folios are clean anonymous folios.  They have
+                * the swapbacked flag cleared, to distinguish them from normal
+                * anonymous folios
                 */
-               ClearPageSwapBacked(page);
-               add_page_to_lru_list(page, lruvec);
+               folio_clear_swapbacked(folio);
+               lruvec_add_folio(lruvec, folio);
 
                __count_vm_events(PGLAZYFREE, nr_pages);
                __count_memcg_events(lruvec_memcg(lruvec), PGLAZYFREE,
@@ -589,71 +613,67 @@ static void lru_lazyfree_fn(struct page *page, struct lruvec *lruvec)
 }
 
 /*
- * Drain pages out of the cpu's pagevecs.
+ * Drain pages out of the cpu's folio_batch.
  * Either "cpu" is the current CPU, and preemption has already been
  * disabled; or "cpu" is being hot-unplugged, and is already dead.
  */
 void lru_add_drain_cpu(int cpu)
 {
-       struct pagevec *pvec = &per_cpu(lru_pvecs.lru_add, cpu);
+       struct cpu_fbatches *fbatches = &per_cpu(cpu_fbatches, cpu);
+       struct folio_batch *fbatch = &fbatches->lru_add;
 
-       if (pagevec_count(pvec))
-               __pagevec_lru_add(pvec);
+       if (folio_batch_count(fbatch))
+               folio_batch_move_lru(fbatch, lru_add_fn);
 
-       pvec = &per_cpu(lru_rotate.pvec, cpu);
+       fbatch = &per_cpu(lru_rotate.fbatch, cpu);
        /* Disabling interrupts below acts as a compiler barrier. */
-       if (data_race(pagevec_count(pvec))) {
+       if (data_race(folio_batch_count(fbatch))) {
                unsigned long flags;
 
                /* No harm done if a racing interrupt already did this */
                local_lock_irqsave(&lru_rotate.lock, flags);
-               pagevec_lru_move_fn(pvec, pagevec_move_tail_fn);
+               folio_batch_move_lru(fbatch, lru_move_tail_fn);
                local_unlock_irqrestore(&lru_rotate.lock, flags);
        }
 
-       pvec = &per_cpu(lru_pvecs.lru_deactivate_file, cpu);
-       if (pagevec_count(pvec))
-               pagevec_lru_move_fn(pvec, lru_deactivate_file_fn);
+       fbatch = &fbatches->lru_deactivate_file;
+       if (folio_batch_count(fbatch))
+               folio_batch_move_lru(fbatch, lru_deactivate_file_fn);
 
-       pvec = &per_cpu(lru_pvecs.lru_deactivate, cpu);
-       if (pagevec_count(pvec))
-               pagevec_lru_move_fn(pvec, lru_deactivate_fn);
+       fbatch = &fbatches->lru_deactivate;
+       if (folio_batch_count(fbatch))
+               folio_batch_move_lru(fbatch, lru_deactivate_fn);
 
-       pvec = &per_cpu(lru_pvecs.lru_lazyfree, cpu);
-       if (pagevec_count(pvec))
-               pagevec_lru_move_fn(pvec, lru_lazyfree_fn);
+       fbatch = &fbatches->lru_lazyfree;
+       if (folio_batch_count(fbatch))
+               folio_batch_move_lru(fbatch, lru_lazyfree_fn);
 
-       activate_page_drain(cpu);
+       folio_activate_drain(cpu);
 }
 
 /**
- * deactivate_file_folio() - Forcefully deactivate a file folio.
+ * deactivate_file_folio() - Deactivate a file folio.
  * @folio: Folio to deactivate.
  *
  * This function hints to the VM that @folio is a good reclaim candidate,
  * for example if its invalidation fails due to the folio being dirty
  * or under writeback.
  *
- * Context: Caller holds a reference on the page.
+ * Context: Caller holds a reference on the folio.
  */
 void deactivate_file_folio(struct folio *folio)
 {
-       struct pagevec *pvec;
+       struct folio_batch *fbatch;
 
-       /*
-        * In a workload with many unevictable pages such as mprotect,
-        * unevictable folio deactivation for accelerating reclaim is pointless.
-        */
+       /* Deactivating an unevictable folio will not accelerate reclaim */
        if (folio_test_unevictable(folio))
                return;
 
        folio_get(folio);
-       local_lock(&lru_pvecs.lock);
-       pvec = this_cpu_ptr(&lru_pvecs.lru_deactivate_file);
-
-       if (pagevec_add_and_need_flush(pvec, &folio->page))
-               pagevec_lru_move_fn(pvec, lru_deactivate_file_fn);
-       local_unlock(&lru_pvecs.lock);
+       local_lock(&cpu_fbatches.lock);
+       fbatch = this_cpu_ptr(&cpu_fbatches.lru_deactivate_file);
+       folio_batch_add_and_move(fbatch, folio, lru_deactivate_file_fn);
+       local_unlock(&cpu_fbatches.lock);
 }
 
 /*
@@ -666,15 +686,17 @@ void deactivate_file_folio(struct folio *folio)
  */
 void deactivate_page(struct page *page)
 {
-       if (PageLRU(page) && PageActive(page) && !PageUnevictable(page)) {
-               struct pagevec *pvec;
-
-               local_lock(&lru_pvecs.lock);
-               pvec = this_cpu_ptr(&lru_pvecs.lru_deactivate);
-               get_page(page);
-               if (pagevec_add_and_need_flush(pvec, page))
-                       pagevec_lru_move_fn(pvec, lru_deactivate_fn);
-               local_unlock(&lru_pvecs.lock);
+       struct folio *folio = page_folio(page);
+
+       if (folio_test_lru(folio) && folio_test_active(folio) &&
+           !folio_test_unevictable(folio)) {
+               struct folio_batch *fbatch;
+
+               folio_get(folio);
+               local_lock(&cpu_fbatches.lock);
+               fbatch = this_cpu_ptr(&cpu_fbatches.lru_deactivate);
+               folio_batch_add_and_move(fbatch, folio, lru_deactivate_fn);
+               local_unlock(&cpu_fbatches.lock);
        }
 }
 
@@ -687,24 +709,26 @@ void deactivate_page(struct page *page)
  */
 void mark_page_lazyfree(struct page *page)
 {
-       if (PageLRU(page) && PageAnon(page) && PageSwapBacked(page) &&
-           !PageSwapCache(page) && !PageUnevictable(page)) {
-               struct pagevec *pvec;
-
-               local_lock(&lru_pvecs.lock);
-               pvec = this_cpu_ptr(&lru_pvecs.lru_lazyfree);
-               get_page(page);
-               if (pagevec_add_and_need_flush(pvec, page))
-                       pagevec_lru_move_fn(pvec, lru_lazyfree_fn);
-               local_unlock(&lru_pvecs.lock);
+       struct folio *folio = page_folio(page);
+
+       if (folio_test_lru(folio) && folio_test_anon(folio) &&
+           folio_test_swapbacked(folio) && !folio_test_swapcache(folio) &&
+           !folio_test_unevictable(folio)) {
+               struct folio_batch *fbatch;
+
+               folio_get(folio);
+               local_lock(&cpu_fbatches.lock);
+               fbatch = this_cpu_ptr(&cpu_fbatches.lru_lazyfree);
+               folio_batch_add_and_move(fbatch, folio, lru_lazyfree_fn);
+               local_unlock(&cpu_fbatches.lock);
        }
 }
 
 void lru_add_drain(void)
 {
-       local_lock(&lru_pvecs.lock);
+       local_lock(&cpu_fbatches.lock);
        lru_add_drain_cpu(smp_processor_id());
-       local_unlock(&lru_pvecs.lock);
+       local_unlock(&cpu_fbatches.lock);
        mlock_page_drain_local();
 }
 
@@ -716,19 +740,19 @@ void lru_add_drain(void)
  */
 static void lru_add_and_bh_lrus_drain(void)
 {
-       local_lock(&lru_pvecs.lock);
+       local_lock(&cpu_fbatches.lock);
        lru_add_drain_cpu(smp_processor_id());
-       local_unlock(&lru_pvecs.lock);
+       local_unlock(&cpu_fbatches.lock);
        invalidate_bh_lrus_cpu();
        mlock_page_drain_local();
 }
 
 void lru_add_drain_cpu_zone(struct zone *zone)
 {
-       local_lock(&lru_pvecs.lock);
+       local_lock(&cpu_fbatches.lock);
        lru_add_drain_cpu(smp_processor_id());
        drain_local_pages(zone);
-       local_unlock(&lru_pvecs.lock);
+       local_unlock(&cpu_fbatches.lock);
        mlock_page_drain_local();
 }
 
@@ -741,6 +765,21 @@ static void lru_add_drain_per_cpu(struct work_struct *dummy)
        lru_add_and_bh_lrus_drain();
 }
 
+static bool cpu_needs_drain(unsigned int cpu)
+{
+       struct cpu_fbatches *fbatches = &per_cpu(cpu_fbatches, cpu);
+
+       /* Check these in order of likelihood that they're not zero */
+       return folio_batch_count(&fbatches->lru_add) ||
+               data_race(folio_batch_count(&per_cpu(lru_rotate.fbatch, cpu))) ||
+               folio_batch_count(&fbatches->lru_deactivate_file) ||
+               folio_batch_count(&fbatches->lru_deactivate) ||
+               folio_batch_count(&fbatches->lru_lazyfree) ||
+               folio_batch_count(&fbatches->activate) ||
+               need_mlock_page_drain(cpu) ||
+               has_bh_in_lru(cpu, NULL);
+}
+
 /*
  * Doesn't need any cpu hotplug locking because we do rely on per-cpu
  * kworkers being shut down before our page_alloc_cpu_dead callback is
@@ -773,8 +812,9 @@ static inline void __lru_add_drain_all(bool force_all_cpus)
                return;
 
        /*
-        * Guarantee pagevec counter stores visible by this CPU are visible to
-        * other CPUs before loading the current drain generation.
+        * Guarantee folio_batch counter stores visible by this CPU
+        * are visible to other CPUs before loading the current drain
+        * generation.
         */
        smp_mb();
 
@@ -800,8 +840,9 @@ static inline void __lru_add_drain_all(bool force_all_cpus)
         * (D) Increment global generation number
         *
         * Pairs with smp_load_acquire() at (B), outside of the critical
-        * section. Use a full memory barrier to guarantee that the new global
-        * drain generation number is stored before loading pagevec counters.
+        * section. Use a full memory barrier to guarantee that the
+        * new global drain generation number is stored before loading
+        * folio_batch counters.
         *
         * This pairing must be done here, before the for_each_online_cpu loop
         * below which drains the page vectors.
@@ -823,14 +864,7 @@ static inline void __lru_add_drain_all(bool force_all_cpus)
        for_each_online_cpu(cpu) {
                struct work_struct *work = &per_cpu(lru_add_drain_work, cpu);
 
-               if (pagevec_count(&per_cpu(lru_pvecs.lru_add, cpu)) ||
-                   data_race(pagevec_count(&per_cpu(lru_rotate.pvec, cpu))) ||
-                   pagevec_count(&per_cpu(lru_pvecs.lru_deactivate_file, cpu)) ||
-                   pagevec_count(&per_cpu(lru_pvecs.lru_deactivate, cpu)) ||
-                   pagevec_count(&per_cpu(lru_pvecs.lru_lazyfree, cpu)) ||
-                   need_activate_page_drain(cpu) ||
-                   need_mlock_page_drain(cpu) ||
-                   has_bh_in_lru(cpu, NULL)) {
+               if (cpu_needs_drain(cpu)) {
                        INIT_WORK(work, lru_add_drain_per_cpu);
                        queue_work_on(cpu, mm_percpu_wq, work);
                        __cpumask_set_cpu(cpu, &has_work);
@@ -906,8 +940,7 @@ void release_pages(struct page **pages, int nr)
        unsigned int lock_batch;
 
        for (i = 0; i < nr; i++) {
-               struct page *page = pages[i];
-               struct folio *folio = page_folio(page);
+               struct folio *folio = page_folio(pages[i]);
 
                /*
                 * Make sure the IRQ-safe lock-holding time does not get
@@ -919,35 +952,34 @@ void release_pages(struct page **pages, int nr)
                        lruvec = NULL;
                }
 
-               page = &folio->page;
-               if (is_huge_zero_page(page))
+               if (is_huge_zero_page(&folio->page))
                        continue;
 
-               if (is_zone_device_page(page)) {
+               if (folio_is_zone_device(folio)) {
                        if (lruvec) {
                                unlock_page_lruvec_irqrestore(lruvec, flags);
                                lruvec = NULL;
                        }
-                       if (put_devmap_managed_page(page))
+                       if (put_devmap_managed_page(&folio->page))
                                continue;
-                       if (put_page_testzero(page))
-                               free_zone_device_page(page);
+                       if (folio_put_testzero(folio))
+                               free_zone_device_page(&folio->page);
                        continue;
                }
 
-               if (!put_page_testzero(page))
+               if (!folio_put_testzero(folio))
                        continue;
 
-               if (PageCompound(page)) {
+               if (folio_test_large(folio)) {
                        if (lruvec) {
                                unlock_page_lruvec_irqrestore(lruvec, flags);
                                lruvec = NULL;
                        }
-                       __put_compound_page(page);
+                       __folio_put_large(folio);
                        continue;
                }
 
-               if (PageLRU(page)) {
+               if (folio_test_lru(folio)) {
                        struct lruvec *prev_lruvec = lruvec;
 
                        lruvec = folio_lruvec_relock_irqsave(folio, lruvec,
@@ -955,8 +987,8 @@ void release_pages(struct page **pages, int nr)
                        if (prev_lruvec != lruvec)
                                lock_batch = 0;
 
-                       del_page_from_lru_list(page, lruvec);
-                       __clear_page_lru_flags(page);
+                       lruvec_del_folio(lruvec, folio);
+                       __folio_clear_lru_flags(folio);
                }
 
                /*
@@ -965,13 +997,13 @@ void release_pages(struct page **pages, int nr)
                 * found set here.  This does not indicate a problem, unless
                 * "unevictable_pgs_cleared" appears worryingly large.
                 */
-               if (unlikely(PageMlocked(page))) {
-                       __ClearPageMlocked(page);
-                       dec_zone_page_state(page, NR_MLOCK);
+               if (unlikely(folio_test_mlocked(folio))) {
+                       __folio_clear_mlocked(folio);
+                       zone_stat_sub_folio(folio, NR_MLOCK);
                        count_vm_event(UNEVICTABLE_PGCLEARED);
                }
 
-               list_add(&page->lru, &pages_to_free);
+               list_add(&folio->lru, &pages_to_free);
        }
        if (lruvec)
                unlock_page_lruvec_irqrestore(lruvec, flags);
@@ -987,8 +1019,8 @@ EXPORT_SYMBOL(release_pages);
  * OK from a correctness point of view but is inefficient - those pages may be
  * cache-warm and we want to give them back to the page allocator ASAP.
  *
- * So __pagevec_release() will drain those queues here.  __pagevec_lru_add()
- * and __pagevec_lru_add_active() call release_pages() directly to avoid
+ * So __pagevec_release() will drain those queues here.
+ * folio_batch_move_lru() calls folios_put() directly to avoid
  * mutual recursion.
  */
 void __pagevec_release(struct pagevec *pvec)
@@ -1002,69 +1034,6 @@ void __pagevec_release(struct pagevec *pvec)
 }
 EXPORT_SYMBOL(__pagevec_release);
 
-static void __pagevec_lru_add_fn(struct folio *folio, struct lruvec *lruvec)
-{
-       int was_unevictable = folio_test_clear_unevictable(folio);
-       long nr_pages = folio_nr_pages(folio);
-
-       VM_BUG_ON_FOLIO(folio_test_lru(folio), folio);
-
-       folio_set_lru(folio);
-       /*
-        * Is an smp_mb__after_atomic() still required here, before
-        * folio_evictable() tests PageMlocked, to rule out the possibility
-        * of stranding an evictable folio on an unevictable LRU?  I think
-        * not, because __munlock_page() only clears PageMlocked while the LRU
-        * lock is held.
-        *
-        * (That is not true of __page_cache_release(), and not necessarily
-        * true of release_pages(): but those only clear PageMlocked after
-        * put_page_testzero() has excluded any other users of the page.)
-        */
-       if (folio_evictable(folio)) {
-               if (was_unevictable)
-                       __count_vm_events(UNEVICTABLE_PGRESCUED, nr_pages);
-       } else {
-               folio_clear_active(folio);
-               folio_set_unevictable(folio);
-               /*
-                * folio->mlock_count = !!folio_test_mlocked(folio)?
-                * But that leaves __mlock_page() in doubt whether another
-                * actor has already counted the mlock or not.  Err on the
-                * safe side, underestimate, let page reclaim fix it, rather
-                * than leaving a page on the unevictable LRU indefinitely.
-                */
-               folio->mlock_count = 0;
-               if (!was_unevictable)
-                       __count_vm_events(UNEVICTABLE_PGCULLED, nr_pages);
-       }
-
-       lruvec_add_folio(lruvec, folio);
-       trace_mm_lru_insertion(folio);
-}
-
-/*
- * Add the passed pages to the LRU, then drop the caller's refcount
- * on them.  Reinitialises the caller's pagevec.
- */
-void __pagevec_lru_add(struct pagevec *pvec)
-{
-       int i;
-       struct lruvec *lruvec = NULL;
-       unsigned long flags = 0;
-
-       for (i = 0; i < pagevec_count(pvec); i++) {
-               struct folio *folio = page_folio(pvec->pages[i]);
-
-               lruvec = folio_lruvec_relock_irqsave(folio, lruvec, &flags);
-               __pagevec_lru_add_fn(folio, lruvec);
-       }
-       if (lruvec)
-               unlock_page_lruvec_irqrestore(lruvec, flags);
-       release_pages(pvec->pages, pvec->nr);
-       pagevec_reinit(pvec);
-}
-
 /**
  * folio_batch_remove_exceptionals() - Prune non-folios from a batch.
  * @fbatch: The batch to prune
index 0193797..17936e0 100644 (file)
--- a/mm/swap.h
+++ b/mm/swap.h
@@ -36,12 +36,11 @@ bool add_to_swap(struct folio *folio);
 void *get_shadow_from_swap_cache(swp_entry_t entry);
 int add_to_swap_cache(struct page *page, swp_entry_t entry,
                      gfp_t gfp, void **shadowp);
-void __delete_from_swap_cache(struct page *page,
+void __delete_from_swap_cache(struct folio *folio,
                              swp_entry_t entry, void *shadow);
-void delete_from_swap_cache(struct page *page);
+void delete_from_swap_cache(struct folio *folio);
 void clear_shadow_from_swap_cache(int type, unsigned long begin,
                                  unsigned long end);
-void free_swap_cache(struct page *page);
 struct page *lookup_swap_cache(swp_entry_t entry,
                               struct vm_area_struct *vma,
                               unsigned long addr);
@@ -61,9 +60,9 @@ struct page *swap_cluster_readahead(swp_entry_t entry, gfp_t flag,
 struct page *swapin_readahead(swp_entry_t entry, gfp_t flag,
                              struct vm_fault *vmf);
 
-static inline unsigned int page_swap_flags(struct page *page)
+static inline unsigned int folio_swap_flags(struct folio *folio)
 {
-       return page_swap_info(page)->flags;
+       return page_swap_info(&folio->page)->flags;
 }
 #else /* CONFIG_SWAP */
 struct swap_iocb;
@@ -81,10 +80,6 @@ static inline struct address_space *swap_address_space(swp_entry_t entry)
        return NULL;
 }
 
-static inline void free_swap_cache(struct page *page)
-{
-}
-
 static inline void show_swap_cache_info(void)
 {
 }
@@ -135,12 +130,12 @@ static inline int add_to_swap_cache(struct page *page, swp_entry_t entry,
        return -1;
 }
 
-static inline void __delete_from_swap_cache(struct page *page,
+static inline void __delete_from_swap_cache(struct folio *folio,
                                        swp_entry_t entry, void *shadow)
 {
 }
 
-static inline void delete_from_swap_cache(struct page *page)
+static inline void delete_from_swap_cache(struct folio *folio)
 {
 }
 
@@ -149,7 +144,7 @@ static inline void clear_shadow_from_swap_cache(int type, unsigned long begin,
 {
 }
 
-static inline unsigned int page_swap_flags(struct page *page)
+static inline unsigned int folio_swap_flags(struct folio *folio)
 {
        return 0;
 }
index 0a2021f..e166051 100644 (file)
@@ -59,24 +59,11 @@ static bool enable_vma_readahead __read_mostly = true;
 #define GET_SWAP_RA_VAL(vma)                                   \
        (atomic_long_read(&(vma)->swap_readahead_info) ? : 4)
 
-#define INC_CACHE_INFO(x)      data_race(swap_cache_info.x++)
-#define ADD_CACHE_INFO(x, nr)  data_race(swap_cache_info.x += (nr))
-
-static struct {
-       unsigned long add_total;
-       unsigned long del_total;
-       unsigned long find_success;
-       unsigned long find_total;
-} swap_cache_info;
-
 static atomic_t swapin_readahead_hits = ATOMIC_INIT(4);
 
 void show_swap_cache_info(void)
 {
        printk("%lu pages in swap cache\n", total_swapcache_pages());
-       printk("Swap cache stats: add %lu, delete %lu, find %lu/%lu\n",
-               swap_cache_info.add_total, swap_cache_info.del_total,
-               swap_cache_info.find_success, swap_cache_info.find_total);
        printk("Free swap  = %ldkB\n",
                get_nr_swap_pages() << (PAGE_SHIFT - 10));
        printk("Total swap = %lukB\n", total_swap_pages << (PAGE_SHIFT - 10));
@@ -133,7 +120,6 @@ int add_to_swap_cache(struct page *page, swp_entry_t entry,
                address_space->nrpages += nr;
                __mod_node_page_state(page_pgdat(page), NR_FILE_PAGES, nr);
                __mod_lruvec_page_state(page, NR_SWAPCACHE, nr);
-               ADD_CACHE_INFO(add_total, nr);
 unlock:
                xas_unlock_irq(&xas);
        } while (xas_nomem(&xas, gfp));
@@ -147,32 +133,32 @@ unlock:
 }
 
 /*
- * This must be called only on pages that have
+ * This must be called only on folios that have
  * been verified to be in the swap cache.
  */
-void __delete_from_swap_cache(struct page *page,
+void __delete_from_swap_cache(struct folio *folio,
                        swp_entry_t entry, void *shadow)
 {
        struct address_space *address_space = swap_address_space(entry);
-       int i, nr = thp_nr_pages(page);
+       int i;
+       long nr = folio_nr_pages(folio);
        pgoff_t idx = swp_offset(entry);
        XA_STATE(xas, &address_space->i_pages, idx);
 
-       VM_BUG_ON_PAGE(!PageLocked(page), page);
-       VM_BUG_ON_PAGE(!PageSwapCache(page), page);
-       VM_BUG_ON_PAGE(PageWriteback(page), page);
+       VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
+       VM_BUG_ON_FOLIO(!folio_test_swapcache(folio), folio);
+       VM_BUG_ON_FOLIO(folio_test_writeback(folio), folio);
 
        for (i = 0; i < nr; i++) {
                void *entry = xas_store(&xas, shadow);
-               VM_BUG_ON_PAGE(entry != page, entry);
-               set_page_private(page + i, 0);
+               VM_BUG_ON_FOLIO(entry != folio, folio);
+               set_page_private(folio_page(folio, i), 0);
                xas_next(&xas);
        }
-       ClearPageSwapCache(page);
+       folio_clear_swapcache(folio);
        address_space->nrpages -= nr;
-       __mod_node_page_state(page_pgdat(page), NR_FILE_PAGES, -nr);
-       __mod_lruvec_page_state(page, NR_SWAPCACHE, -nr);
-       ADD_CACHE_INFO(del_total, nr);
+       __node_stat_mod_folio(folio, NR_FILE_PAGES, -nr);
+       __lruvec_stat_mod_folio(folio, NR_SWAPCACHE, -nr);
 }
 
 /**
@@ -237,22 +223,22 @@ fail:
 }
 
 /*
- * This must be called only on pages that have
+ * This must be called only on folios that have
  * been verified to be in the swap cache and locked.
- * It will never put the page into the free list,
- * the caller has a reference on the page.
+ * It will never put the folio into the free list,
+ * the caller has a reference on the folio.
  */
-void delete_from_swap_cache(struct page *page)
+void delete_from_swap_cache(struct folio *folio)
 {
-       swp_entry_t entry = { .val = page_private(page) };
+       swp_entry_t entry = folio_swap_entry(folio);
        struct address_space *address_space = swap_address_space(entry);
 
        xa_lock_irq(&address_space->i_pages);
-       __delete_from_swap_cache(page, entry, NULL);
+       __delete_from_swap_cache(folio, entry, NULL);
        xa_unlock_irq(&address_space->i_pages);
 
-       put_swap_page(page, entry);
-       page_ref_sub(page, thp_nr_pages(page));
+       put_swap_page(&folio->page, entry);
+       folio_ref_sub(folio, folio_nr_pages(folio));
 }
 
 void clear_shadow_from_swap_cache(int type, unsigned long begin,
@@ -348,12 +334,10 @@ struct page *lookup_swap_cache(swp_entry_t entry, struct vm_area_struct *vma,
        page = find_get_page(swap_address_space(entry), swp_offset(entry));
        put_swap_device(si);
 
-       INC_CACHE_INFO(find_total);
        if (page) {
                bool vma_ra = swap_use_vma_readahead();
                bool readahead;
 
-               INC_CACHE_INFO(find_success);
                /*
                 * At the moment, we don't support PG_readahead for anon THP
                 * so let's bail out rather than confusing the readahead stat.
index a2e66d8..1fdccd2 100644 (file)
@@ -695,7 +695,7 @@ static void swap_range_alloc(struct swap_info_struct *si, unsigned long offset,
                si->lowest_bit += nr_entries;
        if (end == si->highest_bit)
                WRITE_ONCE(si->highest_bit, si->highest_bit - nr_entries);
-       si->inuse_pages += nr_entries;
+       WRITE_ONCE(si->inuse_pages, si->inuse_pages + nr_entries);
        if (si->inuse_pages == si->pages) {
                si->lowest_bit = si->max;
                si->highest_bit = 0;
@@ -732,7 +732,7 @@ static void swap_range_free(struct swap_info_struct *si, unsigned long offset,
                        add_to_avail_list(si);
        }
        atomic_long_add(nr_entries, &nr_swap_pages);
-       si->inuse_pages -= nr_entries;
+       WRITE_ONCE(si->inuse_pages, si->inuse_pages - nr_entries);
        if (si->flags & SWP_BLKDEV)
                swap_slot_free_notify =
                        si->bdev->bd_disk->fops->swap_slot_free_notify;
@@ -1568,16 +1568,15 @@ unlock_out:
        return ret;
 }
 
-static bool page_swapped(struct page *page)
+static bool folio_swapped(struct folio *folio)
 {
        swp_entry_t entry;
        struct swap_info_struct *si;
 
-       if (!IS_ENABLED(CONFIG_THP_SWAP) || likely(!PageTransCompound(page)))
-               return page_swapcount(page) != 0;
+       if (!IS_ENABLED(CONFIG_THP_SWAP) || likely(!folio_test_large(folio)))
+               return page_swapcount(&folio->page) != 0;
 
-       page = compound_head(page);
-       entry.val = page_private(page);
+       entry = folio_swap_entry(folio);
        si = _swap_info_get(entry);
        if (si)
                return swap_page_trans_huge_swapped(si, entry);
@@ -1590,13 +1589,14 @@ static bool page_swapped(struct page *page)
  */
 int try_to_free_swap(struct page *page)
 {
-       VM_BUG_ON_PAGE(!PageLocked(page), page);
+       struct folio *folio = page_folio(page);
+       VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
 
-       if (!PageSwapCache(page))
+       if (!folio_test_swapcache(folio))
                return 0;
-       if (PageWriteback(page))
+       if (folio_test_writeback(folio))
                return 0;
-       if (page_swapped(page))
+       if (folio_swapped(folio))
                return 0;
 
        /*
@@ -1617,9 +1617,8 @@ int try_to_free_swap(struct page *page)
        if (pm_suspended_storage())
                return 0;
 
-       page = compound_head(page);
-       delete_from_swap_cache(page);
-       SetPageDirty(page);
+       delete_from_swap_cache(folio);
+       folio_set_dirty(folio);
        return 1;
 }
 
@@ -2640,7 +2639,7 @@ static int swap_show(struct seq_file *swap, void *v)
        }
 
        bytes = si->pages << (PAGE_SHIFT - 10);
-       inuse = si->inuse_pages << (PAGE_SHIFT - 10);
+       inuse = READ_ONCE(si->inuse_pages) << (PAGE_SHIFT - 10);
 
        file = si->swap_file;
        len = seq_file_path(swap, file, " \t\n\\");
@@ -3259,7 +3258,7 @@ void si_swapinfo(struct sysinfo *val)
                struct swap_info_struct *si = swap_info[type];
 
                if ((si->flags & SWP_USED) && !(si->flags & SWP_WRITEOK))
-                       nr_to_be_unused += si->inuse_pages;
+                       nr_to_be_unused += READ_ONCE(si->inuse_pages);
        }
        val->freeswap = atomic_long_read(&nr_swap_pages) + nr_to_be_unused;
        val->totalswap = total_swap_pages + nr_to_be_unused;
index 53af0e7..c9439c6 100644 (file)
--- a/mm/util.c
+++ b/mm/util.c
@@ -1005,7 +1005,7 @@ EXPORT_SYMBOL_GPL(vm_memory_committed);
  * succeed and -ENOMEM implies there is not.
  *
  * We currently support three overcommit policies, which are set via the
- * vm.overcommit_memory sysctl.  See Documentation/vm/overcommit-accounting.rst
+ * vm.overcommit_memory sysctl.  See Documentation/mm/overcommit-accounting.rst
  *
  * Strict overcommit modes added 2002 Feb 26 by Alan Cox.
  * Additional code 2002 Jul 20 by Robert Love.
index effd1ff..dd6cdb2 100644 (file)
@@ -790,6 +790,7 @@ unsigned long vmalloc_nr_pages(void)
        return atomic_long_read(&nr_vmalloc_pages);
 }
 
+/* Look up the first VA which satisfies addr < va_end, NULL if none. */
 static struct vmap_area *find_vmap_area_exceed_addr(unsigned long addr)
 {
        struct vmap_area *va = NULL;
@@ -814,9 +815,9 @@ static struct vmap_area *find_vmap_area_exceed_addr(unsigned long addr)
        return va;
 }
 
-static struct vmap_area *__find_vmap_area(unsigned long addr)
+static struct vmap_area *__find_vmap_area(unsigned long addr, struct rb_root *root)
 {
-       struct rb_node *n = vmap_area_root.rb_node;
+       struct rb_node *n = root->rb_node;
 
        addr = (unsigned long)kasan_reset_tag((void *)addr);
 
@@ -874,11 +875,9 @@ find_va_links(struct vmap_area *va,
                 * Trigger the BUG() if there are sides(left/right)
                 * or full overlaps.
                 */
-               if (va->va_start < tmp_va->va_end &&
-                               va->va_end <= tmp_va->va_start)
+               if (va->va_end <= tmp_va->va_start)
                        link = &(*link)->rb_left;
-               else if (va->va_end > tmp_va->va_start &&
-                               va->va_start >= tmp_va->va_end)
+               else if (va->va_start >= tmp_va->va_end)
                        link = &(*link)->rb_right;
                else {
                        WARN(1, "vmalloc bug: 0x%lx-0x%lx overlaps with 0x%lx-0x%lx\n",
@@ -911,8 +910,9 @@ get_va_next_sibling(struct rb_node *parent, struct rb_node **link)
 }
 
 static __always_inline void
-link_va(struct vmap_area *va, struct rb_root *root,
-       struct rb_node *parent, struct rb_node **link, struct list_head *head)
+__link_va(struct vmap_area *va, struct rb_root *root,
+       struct rb_node *parent, struct rb_node **link,
+       struct list_head *head, bool augment)
 {
        /*
         * VA is still not in the list, but we can
@@ -926,12 +926,12 @@ link_va(struct vmap_area *va, struct rb_root *root,
 
        /* Insert to the rb-tree */
        rb_link_node(&va->rb_node, parent, link);
-       if (root == &free_vmap_area_root) {
+       if (augment) {
                /*
                 * Some explanation here. Just perform simple insertion
                 * to the tree. We do not set va->subtree_max_size to
                 * its current size before calling rb_insert_augmented().
-                * It is because of we populate the tree from the bottom
+                * It is because we populate the tree from the bottom
                 * to parent levels when the node _is_ in the tree.
                 *
                 * Therefore we set subtree_max_size to zero after insertion,
@@ -950,21 +950,49 @@ link_va(struct vmap_area *va, struct rb_root *root,
 }
 
 static __always_inline void
-unlink_va(struct vmap_area *va, struct rb_root *root)
+link_va(struct vmap_area *va, struct rb_root *root,
+       struct rb_node *parent, struct rb_node **link,
+       struct list_head *head)
+{
+       __link_va(va, root, parent, link, head, false);
+}
+
+static __always_inline void
+link_va_augment(struct vmap_area *va, struct rb_root *root,
+       struct rb_node *parent, struct rb_node **link,
+       struct list_head *head)
+{
+       __link_va(va, root, parent, link, head, true);
+}
+
+static __always_inline void
+__unlink_va(struct vmap_area *va, struct rb_root *root, bool augment)
 {
        if (WARN_ON(RB_EMPTY_NODE(&va->rb_node)))
                return;
 
-       if (root == &free_vmap_area_root)
+       if (augment)
                rb_erase_augmented(&va->rb_node,
                        root, &free_vmap_area_rb_augment_cb);
        else
                rb_erase(&va->rb_node, root);
 
-       list_del(&va->list);
+       list_del_init(&va->list);
        RB_CLEAR_NODE(&va->rb_node);
 }
 
+static __always_inline void
+unlink_va(struct vmap_area *va, struct rb_root *root)
+{
+       __unlink_va(va, root, false);
+}
+
+static __always_inline void
+unlink_va_augment(struct vmap_area *va, struct rb_root *root)
+{
+       __unlink_va(va, root, true);
+}
+
 #if DEBUG_AUGMENT_PROPAGATE_CHECK
 /*
  * Gets called when remove the node and rotate.
@@ -1060,7 +1088,7 @@ insert_vmap_area_augment(struct vmap_area *va,
                link = find_va_links(va, root, NULL, &parent);
 
        if (link) {
-               link_va(va, root, parent, link, head);
+               link_va_augment(va, root, parent, link, head);
                augment_tree_propagate_from(va);
        }
 }
@@ -1077,8 +1105,8 @@ insert_vmap_area_augment(struct vmap_area *va,
  * ongoing.
  */
 static __always_inline struct vmap_area *
-merge_or_add_vmap_area(struct vmap_area *va,
-       struct rb_root *root, struct list_head *head)
+__merge_or_add_vmap_area(struct vmap_area *va,
+       struct rb_root *root, struct list_head *head, bool augment)
 {
        struct vmap_area *sibling;
        struct list_head *next;
@@ -1140,7 +1168,7 @@ merge_or_add_vmap_area(struct vmap_area *va,
                         * "normalized" because of rotation operations.
                         */
                        if (merged)
-                               unlink_va(va, root);
+                               __unlink_va(va, root, augment);
 
                        sibling->va_end = va->va_end;
 
@@ -1155,16 +1183,23 @@ merge_or_add_vmap_area(struct vmap_area *va,
 
 insert:
        if (!merged)
-               link_va(va, root, parent, link, head);
+               __link_va(va, root, parent, link, head, augment);
 
        return va;
 }
 
+static __always_inline struct vmap_area *
+merge_or_add_vmap_area(struct vmap_area *va,
+       struct rb_root *root, struct list_head *head)
+{
+       return __merge_or_add_vmap_area(va, root, head, false);
+}
+
 static __always_inline struct vmap_area *
 merge_or_add_vmap_area_augment(struct vmap_area *va,
        struct rb_root *root, struct list_head *head)
 {
-       va = merge_or_add_vmap_area(va, root, head);
+       va = __merge_or_add_vmap_area(va, root, head, true);
        if (va)
                augment_tree_propagate_from(va);
 
@@ -1198,15 +1233,15 @@ is_within_this_va(struct vmap_area *va, unsigned long size,
  * overhead.
  */
 static __always_inline struct vmap_area *
-find_vmap_lowest_match(unsigned long size, unsigned long align,
-       unsigned long vstart, bool adjust_search_size)
+find_vmap_lowest_match(struct rb_root *root, unsigned long size,
+       unsigned long align, unsigned long vstart, bool adjust_search_size)
 {
        struct vmap_area *va;
        struct rb_node *node;
        unsigned long length;
 
        /* Start from the root. */
-       node = free_vmap_area_root.rb_node;
+       node = root->rb_node;
 
        /* Adjust the search size for alignment overhead. */
        length = adjust_search_size ? size + align - 1 : size;
@@ -1334,11 +1369,12 @@ classify_va_fit_type(struct vmap_area *va,
 }
 
 static __always_inline int
-adjust_va_to_fit_type(struct vmap_area *va,
-       unsigned long nva_start_addr, unsigned long size,
-       enum fit_type type)
+adjust_va_to_fit_type(struct rb_root *root, struct list_head *head,
+                     struct vmap_area *va, unsigned long nva_start_addr,
+                     unsigned long size)
 {
        struct vmap_area *lva = NULL;
+       enum fit_type type = classify_va_fit_type(va, nva_start_addr, size);
 
        if (type == FL_FIT_TYPE) {
                /*
@@ -1348,7 +1384,7 @@ adjust_va_to_fit_type(struct vmap_area *va,
                 * V      NVA      V
                 * |---------------|
                 */
-               unlink_va(va, &free_vmap_area_root);
+               unlink_va_augment(va, root);
                kmem_cache_free(vmap_area_cachep, va);
        } else if (type == LE_FIT_TYPE) {
                /*
@@ -1426,8 +1462,7 @@ adjust_va_to_fit_type(struct vmap_area *va,
                augment_tree_propagate_from(va);
 
                if (lva)        /* type == NE_FIT_TYPE */
-                       insert_vmap_area_augment(lva, &va->rb_node,
-                               &free_vmap_area_root, &free_vmap_area_list);
+                       insert_vmap_area_augment(lva, &va->rb_node, root, head);
        }
 
        return 0;
@@ -1438,13 +1473,13 @@ adjust_va_to_fit_type(struct vmap_area *va,
  * Otherwise a vend is returned that indicates failure.
  */
 static __always_inline unsigned long
-__alloc_vmap_area(unsigned long size, unsigned long align,
+__alloc_vmap_area(struct rb_root *root, struct list_head *head,
+       unsigned long size, unsigned long align,
        unsigned long vstart, unsigned long vend)
 {
        bool adjust_search_size = true;
        unsigned long nva_start_addr;
        struct vmap_area *va;
-       enum fit_type type;
        int ret;
 
        /*
@@ -1459,7 +1494,7 @@ __alloc_vmap_area(unsigned long size, unsigned long align,
        if (align <= PAGE_SIZE || (align > PAGE_SIZE && (vend - vstart) == size))
                adjust_search_size = false;
 
-       va = find_vmap_lowest_match(size, align, vstart, adjust_search_size);
+       va = find_vmap_lowest_match(root, size, align, vstart, adjust_search_size);
        if (unlikely(!va))
                return vend;
 
@@ -1472,14 +1507,9 @@ __alloc_vmap_area(unsigned long size, unsigned long align,
        if (nva_start_addr + size > vend)
                return vend;
 
-       /* Classify what we have found. */
-       type = classify_va_fit_type(va, nva_start_addr, size);
-       if (WARN_ON_ONCE(type == NOTHING_FIT))
-               return vend;
-
        /* Update the free vmap_area. */
-       ret = adjust_va_to_fit_type(va, nva_start_addr, size, type);
-       if (ret)
+       ret = adjust_va_to_fit_type(root, head, va, nva_start_addr, size);
+       if (WARN_ON_ONCE(ret))
                return vend;
 
 #if DEBUG_AUGMENT_LOWEST_MATCH_CHECK
@@ -1569,7 +1599,8 @@ static struct vmap_area *alloc_vmap_area(unsigned long size,
 
 retry:
        preload_this_cpu_lock(&free_vmap_area_lock, gfp_mask, node);
-       addr = __alloc_vmap_area(size, align, vstart, vend);
+       addr = __alloc_vmap_area(&free_vmap_area_root, &free_vmap_area_list,
+               size, align, vstart, vend);
        spin_unlock(&free_vmap_area_lock);
 
        /*
@@ -1663,7 +1694,7 @@ static atomic_long_t vmap_lazy_nr = ATOMIC_LONG_INIT(0);
 
 /*
  * Serialize vmap purging.  There is no actual critical section protected
- * by this look, but we want to avoid concurrent calls for performance
+ * by this lock, but we want to avoid concurrent calls for performance
  * reasons and to make the pcpu_get_vm_areas more deterministic.
  */
 static DEFINE_MUTEX(vmap_purge_lock);
@@ -1677,32 +1708,32 @@ static void purge_fragmented_blocks_allcpus(void);
 static bool __purge_vmap_area_lazy(unsigned long start, unsigned long end)
 {
        unsigned long resched_threshold;
-       struct list_head local_pure_list;
+       struct list_head local_purge_list;
        struct vmap_area *va, *n_va;
 
        lockdep_assert_held(&vmap_purge_lock);
 
        spin_lock(&purge_vmap_area_lock);
        purge_vmap_area_root = RB_ROOT;
-       list_replace_init(&purge_vmap_area_list, &local_pure_list);
+       list_replace_init(&purge_vmap_area_list, &local_purge_list);
        spin_unlock(&purge_vmap_area_lock);
 
-       if (unlikely(list_empty(&local_pure_list)))
+       if (unlikely(list_empty(&local_purge_list)))
                return false;
 
        start = min(start,
-               list_first_entry(&local_pure_list,
+               list_first_entry(&local_purge_list,
                        struct vmap_area, list)->va_start);
 
        end = max(end,
-               list_last_entry(&local_pure_list,
+               list_last_entry(&local_purge_list,
                        struct vmap_area, list)->va_end);
 
        flush_tlb_kernel_range(start, end);
        resched_threshold = lazy_max_pages() << 1;
 
        spin_lock(&free_vmap_area_lock);
-       list_for_each_entry_safe(va, n_va, &local_pure_list, list) {
+       list_for_each_entry_safe(va, n_va, &local_purge_list, list) {
                unsigned long nr = (va->va_end - va->va_start) >> PAGE_SHIFT;
                unsigned long orig_start = va->va_start;
                unsigned long orig_end = va->va_end;
@@ -1803,7 +1834,7 @@ struct vmap_area *find_vmap_area(unsigned long addr)
        struct vmap_area *va;
 
        spin_lock(&vmap_area_lock);
-       va = __find_vmap_area(addr);
+       va = __find_vmap_area(addr, &vmap_area_root);
        spin_unlock(&vmap_area_lock);
 
        return va;
@@ -2546,7 +2577,7 @@ struct vm_struct *remove_vm_area(const void *addr)
        might_sleep();
 
        spin_lock(&vmap_area_lock);
-       va = __find_vmap_area((unsigned long)addr);
+       va = __find_vmap_area((unsigned long)addr, &vmap_area_root);
        if (va && va->vm) {
                struct vm_struct *vm = va->vm;
 
@@ -3168,15 +3199,15 @@ again:
 
        /*
         * Mark the pages as accessible, now that they are mapped.
-        * The init condition should match the one in post_alloc_hook()
-        * (except for the should_skip_init() check) to make sure that memory
-        * is initialized under the same conditions regardless of the enabled
-        * KASAN mode.
+        * The condition for setting KASAN_VMALLOC_INIT should complement the
+        * one in post_alloc_hook() with regards to the __GFP_SKIP_ZERO check
+        * to make sure that memory is initialized under the same conditions.
         * Tag-based KASAN modes only assign tags to normal non-executable
         * allocations, see __kasan_unpoison_vmalloc().
         */
        kasan_flags |= KASAN_VMALLOC_VM_ALLOC;
-       if (!want_init_on_free() && want_init_on_alloc(gfp_mask))
+       if (!want_init_on_free() && want_init_on_alloc(gfp_mask) &&
+           (gfp_mask & __GFP_SKIP_ZERO))
                kasan_flags |= KASAN_VMALLOC_INIT;
        /* KASAN_VMALLOC_PROT_NORMAL already set if required. */
        area->addr = kasan_unpoison_vmalloc(area->addr, real_size, kasan_flags);
@@ -3735,7 +3766,6 @@ struct vm_struct **pcpu_get_vm_areas(const unsigned long *offsets,
        int area, area2, last_area, term_area;
        unsigned long base, start, size, end, last_end, orig_start, orig_end;
        bool purged = false;
-       enum fit_type type;
 
        /* verify parameters and allocate data structures */
        BUG_ON(offset_in_page(align) || !is_power_of_2(align));
@@ -3846,15 +3876,13 @@ retry:
                        /* It is a BUG(), but trigger recovery instead. */
                        goto recovery;
 
-               type = classify_va_fit_type(va, start, size);
-               if (WARN_ON_ONCE(type == NOTHING_FIT))
+               ret = adjust_va_to_fit_type(&free_vmap_area_root,
+                                           &free_vmap_area_list,
+                                           va, start, size);
+               if (WARN_ON_ONCE(unlikely(ret)))
                        /* It is a BUG(), but trigger recovery instead. */
                        goto recovery;
 
-               ret = adjust_va_to_fit_type(va, start, size, type);
-               if (unlikely(ret))
-                       goto recovery;
-
                /* Allocated area. */
                va = vas[area];
                va->va_start = start;
index 04f8671..b2b1431 100644 (file)
@@ -26,8 +26,7 @@
 #include <linux/file.h>
 #include <linux/writeback.h>
 #include <linux/blkdev.h>
-#include <linux/buffer_head.h> /* for try_to_release_page(),
-                                       buffer_heads_over_limit */
+#include <linux/buffer_head.h> /* for buffer_heads_over_limit */
 #include <linux/mm_inline.h>
 #include <linux/backing-dev.h>
 #include <linux/rmap.h>
@@ -102,6 +101,9 @@ struct scan_control {
        /* Can pages be swapped as part of reclaim? */
        unsigned int may_swap:1;
 
+       /* Proactive reclaim invoked by userspace through memory.reclaim */
+       unsigned int proactive:1;
+
        /*
         * Cgroup memory below memory.low is protected as long as we
         * don't threaten to OOM. If any cgroup is reclaimed at
@@ -160,17 +162,17 @@ struct scan_control {
 };
 
 #ifdef ARCH_HAS_PREFETCHW
-#define prefetchw_prev_lru_page(_page, _base, _field)                  \
+#define prefetchw_prev_lru_folio(_folio, _base, _field)                        \
        do {                                                            \
-               if ((_page)->lru.prev != _base) {                       \
-                       struct page *prev;                              \
+               if ((_folio)->lru.prev != _base) {                      \
+                       struct folio *prev;                             \
                                                                        \
-                       prev = lru_to_page(&(_page->lru));              \
+                       prev = lru_to_folio(&(_folio->lru));            \
                        prefetchw(&prev->_field);                       \
                }                                                       \
        } while (0)
 #else
-#define prefetchw_prev_lru_page(_page, _base, _field) do { } while (0)
+#define prefetchw_prev_lru_folio(_folio, _base, _field) do { } while (0)
 #endif
 
 /*
@@ -190,8 +192,8 @@ static void set_task_reclaim_state(struct task_struct *task,
        task->reclaim_state = rs;
 }
 
-static LIST_HEAD(shrinker_list);
-static DECLARE_RWSEM(shrinker_rwsem);
+LIST_HEAD(shrinker_list);
+DECLARE_RWSEM(shrinker_rwsem);
 
 #ifdef CONFIG_MEMCG
 static int shrinker_nr_max;
@@ -608,7 +610,7 @@ static unsigned long lruvec_lru_size(struct lruvec *lruvec, enum lru_list lru,
 /*
  * Add a shrinker callback to be called from the vm.
  */
-int prealloc_shrinker(struct shrinker *shrinker)
+static int __prealloc_shrinker(struct shrinker *shrinker)
 {
        unsigned int size;
        int err;
@@ -632,8 +634,39 @@ int prealloc_shrinker(struct shrinker *shrinker)
        return 0;
 }
 
+#ifdef CONFIG_SHRINKER_DEBUG
+int prealloc_shrinker(struct shrinker *shrinker, const char *fmt, ...)
+{
+       va_list ap;
+       int err;
+
+       va_start(ap, fmt);
+       shrinker->name = kvasprintf_const(GFP_KERNEL, fmt, ap);
+       va_end(ap);
+       if (!shrinker->name)
+               return -ENOMEM;
+
+       err = __prealloc_shrinker(shrinker);
+       if (err) {
+               kfree_const(shrinker->name);
+               shrinker->name = NULL;
+       }
+
+       return err;
+}
+#else
+int prealloc_shrinker(struct shrinker *shrinker, const char *fmt, ...)
+{
+       return __prealloc_shrinker(shrinker);
+}
+#endif
+
 void free_prealloced_shrinker(struct shrinker *shrinker)
 {
+#ifdef CONFIG_SHRINKER_DEBUG
+       kfree_const(shrinker->name);
+       shrinker->name = NULL;
+#endif
        if (shrinker->flags & SHRINKER_MEMCG_AWARE) {
                down_write(&shrinker_rwsem);
                unregister_memcg_shrinker(shrinker);
@@ -650,18 +683,45 @@ void register_shrinker_prepared(struct shrinker *shrinker)
        down_write(&shrinker_rwsem);
        list_add_tail(&shrinker->list, &shrinker_list);
        shrinker->flags |= SHRINKER_REGISTERED;
+       shrinker_debugfs_add(shrinker);
        up_write(&shrinker_rwsem);
 }
 
-int register_shrinker(struct shrinker *shrinker)
+static int __register_shrinker(struct shrinker *shrinker)
 {
-       int err = prealloc_shrinker(shrinker);
+       int err = __prealloc_shrinker(shrinker);
 
        if (err)
                return err;
        register_shrinker_prepared(shrinker);
        return 0;
 }
+
+#ifdef CONFIG_SHRINKER_DEBUG
+int register_shrinker(struct shrinker *shrinker, const char *fmt, ...)
+{
+       va_list ap;
+       int err;
+
+       va_start(ap, fmt);
+       shrinker->name = kvasprintf_const(GFP_KERNEL, fmt, ap);
+       va_end(ap);
+       if (!shrinker->name)
+               return -ENOMEM;
+
+       err = __register_shrinker(shrinker);
+       if (err) {
+               kfree_const(shrinker->name);
+               shrinker->name = NULL;
+       }
+       return err;
+}
+#else
+int register_shrinker(struct shrinker *shrinker, const char *fmt, ...)
+{
+       return __register_shrinker(shrinker);
+}
+#endif
 EXPORT_SYMBOL(register_shrinker);
 
 /*
@@ -677,6 +737,7 @@ void unregister_shrinker(struct shrinker *shrinker)
        shrinker->flags &= ~SHRINKER_REGISTERED;
        if (shrinker->flags & SHRINKER_MEMCG_AWARE)
                unregister_memcg_shrinker(shrinker);
+       shrinker_debugfs_remove(shrinker);
        up_write(&shrinker_rwsem);
 
        kfree(shrinker->nr_deferred);
@@ -1276,7 +1337,7 @@ static int __remove_mapping(struct address_space *mapping, struct folio *folio,
                mem_cgroup_swapout(folio, swap);
                if (reclaimed && !mapping_exiting(mapping))
                        shadow = workingset_eviction(folio, target_memcg);
-               __delete_from_swap_cache(&folio->page, swap, shadow);
+               __delete_from_swap_cache(folio, swap, shadow);
                xa_unlock_irq(&mapping->i_pages);
                put_swap_page(&folio->page, swap);
        } else {
@@ -1519,7 +1580,7 @@ static bool may_enter_fs(struct folio *folio, gfp_t gfp_mask)
         * but that will never affect SWP_FS_OPS, so the data_race
         * is safe.
         */
-       return !data_race(page_swap_flags(&folio->page) & SWP_FS_OPS);
+       return !data_race(folio_swap_flags(folio) & SWP_FS_OPS);
 }
 
 /*
@@ -1926,7 +1987,7 @@ free_it:
                 * appear not as the counts should be low
                 */
                if (unlikely(folio_test_large(folio)))
-                       destroy_compound_page(&folio->page);
+                       destroy_large_folio(folio);
                else
                        list_add(&folio->lru, &free_pages);
                continue;
@@ -1987,7 +2048,7 @@ keep:
 }
 
 unsigned int reclaim_clean_pages_from_list(struct zone *zone,
-                                           struct list_head *page_list)
+                                           struct list_head *folio_list)
 {
        struct scan_control sc = {
                .gfp_mask = GFP_KERNEL,
@@ -1995,16 +2056,16 @@ unsigned int reclaim_clean_pages_from_list(struct zone *zone,
        };
        struct reclaim_stat stat;
        unsigned int nr_reclaimed;
-       struct page *page, *next;
-       LIST_HEAD(clean_pages);
+       struct folio *folio, *next;
+       LIST_HEAD(clean_folios);
        unsigned int noreclaim_flag;
 
-       list_for_each_entry_safe(page, next, page_list, lru) {
-               if (!PageHuge(page) && page_is_file_lru(page) &&
-                   !PageDirty(page) && !__PageMovable(page) &&
-                   !PageUnevictable(page)) {
-                       ClearPageActive(page);
-                       list_move(&page->lru, &clean_pages);
+       list_for_each_entry_safe(folio, next, folio_list, lru) {
+               if (!folio_test_hugetlb(folio) && folio_is_file_lru(folio) &&
+                   !folio_test_dirty(folio) && !__folio_test_movable(folio) &&
+                   !folio_test_unevictable(folio)) {
+                       folio_clear_active(folio);
+                       list_move(&folio->lru, &clean_folios);
                }
        }
 
@@ -2015,11 +2076,11 @@ unsigned int reclaim_clean_pages_from_list(struct zone *zone,
         * change in the future.
         */
        noreclaim_flag = memalloc_noreclaim_save();
-       nr_reclaimed = shrink_page_list(&clean_pages, zone->zone_pgdat, &sc,
+       nr_reclaimed = shrink_page_list(&clean_folios, zone->zone_pgdat, &sc,
                                        &stat, true);
        memalloc_noreclaim_restore(noreclaim_flag);
 
-       list_splice(&clean_pages, page_list);
+       list_splice(&clean_folios, folio_list);
        mod_node_page_state(zone->zone_pgdat, NR_ISOLATED_FILE,
                            -(long)nr_reclaimed);
        /*
@@ -2085,72 +2146,72 @@ static unsigned long isolate_lru_pages(unsigned long nr_to_scan,
        unsigned long nr_skipped[MAX_NR_ZONES] = { 0, };
        unsigned long skipped = 0;
        unsigned long scan, total_scan, nr_pages;
-       LIST_HEAD(pages_skipped);
+       LIST_HEAD(folios_skipped);
 
        total_scan = 0;
        scan = 0;
        while (scan < nr_to_scan && !list_empty(src)) {
                struct list_head *move_to = src;
-               struct page *page;
+               struct folio *folio;
 
-               page = lru_to_page(src);
-               prefetchw_prev_lru_page(page, src, flags);
+               folio = lru_to_folio(src);
+               prefetchw_prev_lru_folio(folio, src, flags);
 
-               nr_pages = compound_nr(page);
+               nr_pages = folio_nr_pages(folio);
                total_scan += nr_pages;
 
-               if (page_zonenum(page) > sc->reclaim_idx) {
-                       nr_skipped[page_zonenum(page)] += nr_pages;
-                       move_to = &pages_skipped;
+               if (folio_zonenum(folio) > sc->reclaim_idx) {
+                       nr_skipped[folio_zonenum(folio)] += nr_pages;
+                       move_to = &folios_skipped;
                        goto move;
                }
 
                /*
-                * Do not count skipped pages because that makes the function
-                * return with no isolated pages if the LRU mostly contains
-                * ineligible pages.  This causes the VM to not reclaim any
-                * pages, triggering a premature OOM.
-                * Account all tail pages of THP.
+                * Do not count skipped folios because that makes the function
+                * return with no isolated folios if the LRU mostly contains
+                * ineligible folios.  This causes the VM to not reclaim any
+                * folios, triggering a premature OOM.
+                * Account all pages in a folio.
                 */
                scan += nr_pages;
 
-               if (!PageLRU(page))
+               if (!folio_test_lru(folio))
                        goto move;
-               if (!sc->may_unmap && page_mapped(page))
+               if (!sc->may_unmap && folio_mapped(folio))
                        goto move;
 
                /*
-                * Be careful not to clear PageLRU until after we're
-                * sure the page is not being freed elsewhere -- the
-                * page release code relies on it.
+                * Be careful not to clear the lru flag until after we're
+                * sure the folio is not being freed elsewhere -- the
+                * folio release code relies on it.
                 */
-               if (unlikely(!get_page_unless_zero(page)))
+               if (unlikely(!folio_try_get(folio)))
                        goto move;
 
-               if (!TestClearPageLRU(page)) {
-                       /* Another thread is already isolating this page */
-                       put_page(page);
+               if (!folio_test_clear_lru(folio)) {
+                       /* Another thread is already isolating this folio */
+                       folio_put(folio);
                        goto move;
                }
 
                nr_taken += nr_pages;
-               nr_zone_taken[page_zonenum(page)] += nr_pages;
+               nr_zone_taken[folio_zonenum(folio)] += nr_pages;
                move_to = dst;
 move:
-               list_move(&page->lru, move_to);
+               list_move(&folio->lru, move_to);
        }
 
        /*
-        * Splice any skipped pages to the start of the LRU list. Note that
+        * Splice any skipped folios to the start of the LRU list. Note that
         * this disrupts the LRU order when reclaiming for lower zones but
         * we cannot splice to the tail. If we did then the SWAP_CLUSTER_MAX
-        * scanning would soon rescan the same pages to skip and waste lots
+        * scanning would soon rescan the same folios to skip and waste lots
         * of cpu cycles.
         */
-       if (!list_empty(&pages_skipped)) {
+       if (!list_empty(&folios_skipped)) {
                int zid;
 
-               list_splice(&pages_skipped, src);
+               list_splice(&folios_skipped, src);
                for (zid = 0; zid < MAX_NR_ZONES; zid++) {
                        if (!nr_skipped[zid])
                                continue;
@@ -2254,8 +2315,8 @@ static int too_many_isolated(struct pglist_data *pgdat, int file,
 }
 
 /*
- * move_pages_to_lru() moves pages from private @list to appropriate LRU list.
- * On return, @list is reused as a list of pages to be freed by the caller.
+ * move_pages_to_lru() moves folios from private @list to appropriate LRU list.
+ * On return, @list is reused as a list of folios to be freed by the caller.
  *
  * Returns the number of pages moved to the given lruvec.
  */
@@ -2263,42 +2324,42 @@ static unsigned int move_pages_to_lru(struct lruvec *lruvec,
                                      struct list_head *list)
 {
        int nr_pages, nr_moved = 0;
-       LIST_HEAD(pages_to_free);
-       struct page *page;
+       LIST_HEAD(folios_to_free);
 
        while (!list_empty(list)) {
-               page = lru_to_page(list);
-               VM_BUG_ON_PAGE(PageLRU(page), page);
-               list_del(&page->lru);
-               if (unlikely(!page_evictable(page))) {
+               struct folio *folio = lru_to_folio(list);
+
+               VM_BUG_ON_FOLIO(folio_test_lru(folio), folio);
+               list_del(&folio->lru);
+               if (unlikely(!folio_evictable(folio))) {
                        spin_unlock_irq(&lruvec->lru_lock);
-                       putback_lru_page(page);
+                       folio_putback_lru(folio);
                        spin_lock_irq(&lruvec->lru_lock);
                        continue;
                }
 
                /*
-                * The SetPageLRU needs to be kept here for list integrity.
+                * The folio_set_lru needs to be kept here for list integrity.
                 * Otherwise:
                 *   #0 move_pages_to_lru             #1 release_pages
-                *   if !put_page_testzero
-                *                                    if (put_page_testzero())
-                *                                      !PageLRU //skip lru_lock
-                *     SetPageLRU()
-                *     list_add(&page->lru,)
-                *                                        list_add(&page->lru,)
+                *   if (!folio_put_testzero())
+                *                                    if (folio_put_testzero())
+                *                                      !lru //skip lru_lock
+                *     folio_set_lru()
+                *     list_add(&folio->lru,)
+                *                                        list_add(&folio->lru,)
                 */
-               SetPageLRU(page);
+               folio_set_lru(folio);
 
-               if (unlikely(put_page_testzero(page))) {
-                       __clear_page_lru_flags(page);
+               if (unlikely(folio_put_testzero(folio))) {
+                       __folio_clear_lru_flags(folio);
 
-                       if (unlikely(PageCompound(page))) {
+                       if (unlikely(folio_test_large(folio))) {
                                spin_unlock_irq(&lruvec->lru_lock);
-                               destroy_compound_page(page);
+                               destroy_large_folio(folio);
                                spin_lock_irq(&lruvec->lru_lock);
                        } else
-                               list_add(&page->lru, &pages_to_free);
+                               list_add(&folio->lru, &folios_to_free);
 
                        continue;
                }
@@ -2307,18 +2368,18 @@ static unsigned int move_pages_to_lru(struct lruvec *lruvec,
                 * All pages were isolated from the same lruvec (and isolation
                 * inhibits memcg migration).
                 */
-               VM_BUG_ON_PAGE(!folio_matches_lruvec(page_folio(page), lruvec), page);
-               add_page_to_lru_list(page, lruvec);
-               nr_pages = thp_nr_pages(page);
+               VM_BUG_ON_FOLIO(!folio_matches_lruvec(folio, lruvec), folio);
+               lruvec_add_folio(lruvec, folio);
+               nr_pages = folio_nr_pages(folio);
                nr_moved += nr_pages;
-               if (PageActive(page))
+               if (folio_test_active(folio))
                        workingset_age_nonresident(lruvec, nr_pages);
        }
 
        /*
         * To save our caller's stack, now use input list for pages to free.
         */
-       list_splice(&pages_to_free, list);
+       list_splice(&folios_to_free, list);
 
        return nr_moved;
 }
@@ -2429,21 +2490,21 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec,
 }
 
 /*
- * shrink_active_list() moves pages from the active LRU to the inactive LRU.
+ * shrink_active_list() moves folios from the active LRU to the inactive LRU.
  *
- * We move them the other way if the page is referenced by one or more
+ * We move them the other way if the folio is referenced by one or more
  * processes.
  *
- * If the pages are mostly unmapped, the processing is fast and it is
+ * If the folios are mostly unmapped, the processing is fast and it is
  * appropriate to hold lru_lock across the whole operation.  But if
- * the pages are mapped, the processing is slow (folio_referenced()), so
- * we should drop lru_lock around each page.  It's impossible to balance
- * this, so instead we remove the pages from the LRU while processing them.
- * It is safe to rely on PG_active against the non-LRU pages in here because
- * nobody will play with that bit on a non-LRU page.
+ * the folios are mapped, the processing is slow (folio_referenced()), so
+ * we should drop lru_lock around each folio.  It's impossible to balance
+ * this, so instead we remove the folios from the LRU while processing them.
+ * It is safe to rely on the active flag against the non-LRU folios in here
+ * because nobody will play with that bit on a non-LRU folio.
  *
- * The downside is that we have to touch page->_refcount against each page.
- * But we had to alter page->flags anyway.
+ * The downside is that we have to touch folio->_refcount against each folio.
+ * But we had to alter folio->flags anyway.
  */
 static void shrink_active_list(unsigned long nr_to_scan,
                               struct lruvec *lruvec,
@@ -2453,7 +2514,7 @@ static void shrink_active_list(unsigned long nr_to_scan,
        unsigned long nr_taken;
        unsigned long nr_scanned;
        unsigned long vm_flags;
-       LIST_HEAD(l_hold);      /* The pages which were snipped off */
+       LIST_HEAD(l_hold);      /* The folios which were snipped off */
        LIST_HEAD(l_active);
        LIST_HEAD(l_inactive);
        unsigned nr_deactivate, nr_activate;
@@ -2478,23 +2539,21 @@ static void shrink_active_list(unsigned long nr_to_scan,
 
        while (!list_empty(&l_hold)) {
                struct folio *folio;
-               struct page *page;
 
                cond_resched();
                folio = lru_to_folio(&l_hold);
                list_del(&folio->lru);
-               page = &folio->page;
 
-               if (unlikely(!page_evictable(page))) {
-                       putback_lru_page(page);
+               if (unlikely(!folio_evictable(folio))) {
+                       folio_putback_lru(folio);
                        continue;
                }
 
                if (unlikely(buffer_heads_over_limit)) {
-                       if (page_has_private(page) && trylock_page(page)) {
-                               if (page_has_private(page))
-                                       try_to_release_page(page, 0);
-                               unlock_page(page);
+                       if (folio_get_private(folio) && folio_trylock(folio)) {
+                               if (folio_get_private(folio))
+                                       filemap_release_folio(folio, 0);
+                               folio_unlock(folio);
                        }
                }
 
@@ -2502,34 +2561,34 @@ static void shrink_active_list(unsigned long nr_to_scan,
                if (folio_referenced(folio, 0, sc->target_mem_cgroup,
                                     &vm_flags) != 0) {
                        /*
-                        * Identify referenced, file-backed active pages and
+                        * Identify referenced, file-backed active folios and
                         * give them one more trip around the active list. So
                         * that executable code get better chances to stay in
-                        * memory under moderate memory pressure.  Anon pages
+                        * memory under moderate memory pressure.  Anon folios
                         * are not likely to be evicted by use-once streaming
-                        * IO, plus JVM can create lots of anon VM_EXEC pages,
+                        * IO, plus JVM can create lots of anon VM_EXEC folios,
                         * so we ignore them here.
                         */
-                       if ((vm_flags & VM_EXEC) && page_is_file_lru(page)) {
-                               nr_rotated += thp_nr_pages(page);
-                               list_add(&page->lru, &l_active);
+                       if ((vm_flags & VM_EXEC) && folio_is_file_lru(folio)) {
+                               nr_rotated += folio_nr_pages(folio);
+                               list_add(&folio->lru, &l_active);
                                continue;
                        }
                }
 
-               ClearPageActive(page);  /* we are de-activating */
-               SetPageWorkingset(page);
-               list_add(&page->lru, &l_inactive);
+               folio_clear_active(folio);      /* we are de-activating */
+               folio_set_workingset(folio);
+               list_add(&folio->lru, &l_inactive);
        }
 
        /*
-        * Move pages back to the lru list.
+        * Move folios back to the lru list.
         */
        spin_lock_irq(&lruvec->lru_lock);
 
        nr_activate = move_pages_to_lru(lruvec, &l_active);
        nr_deactivate = move_pages_to_lru(lruvec, &l_inactive);
-       /* Keep all free pages in l_active list */
+       /* Keep all free folios in l_active list */
        list_splice(&l_inactive, &l_active);
 
        __count_vm_events(PGDEACTIVATE, nr_deactivate);
@@ -2568,34 +2627,33 @@ static unsigned int reclaim_page_list(struct list_head *page_list,
        return nr_reclaimed;
 }
 
-unsigned long reclaim_pages(struct list_head *page_list)
+unsigned long reclaim_pages(struct list_head *folio_list)
 {
        int nid;
        unsigned int nr_reclaimed = 0;
-       LIST_HEAD(node_page_list);
-       struct page *page;
+       LIST_HEAD(node_folio_list);
        unsigned int noreclaim_flag;
 
-       if (list_empty(page_list))
+       if (list_empty(folio_list))
                return nr_reclaimed;
 
        noreclaim_flag = memalloc_noreclaim_save();
 
-       nid = page_to_nid(lru_to_page(page_list));
+       nid = folio_nid(lru_to_folio(folio_list));
        do {
-               page = lru_to_page(page_list);
+               struct folio *folio = lru_to_folio(folio_list);
 
-               if (nid == page_to_nid(page)) {
-                       ClearPageActive(page);
-                       list_move(&page->lru, &node_page_list);
+               if (nid == folio_nid(folio)) {
+                       folio_clear_active(folio);
+                       list_move(&folio->lru, &node_folio_list);
                        continue;
                }
 
-               nr_reclaimed += reclaim_page_list(&node_page_list, NODE_DATA(nid));
-               nid = page_to_nid(lru_to_page(page_list));
-       } while (!list_empty(page_list));
+               nr_reclaimed += reclaim_page_list(&node_folio_list, NODE_DATA(nid));
+               nid = folio_nid(lru_to_folio(folio_list));
+       } while (!list_empty(folio_list));
 
-       nr_reclaimed += reclaim_page_list(&node_page_list, NODE_DATA(nid));
+       nr_reclaimed += reclaim_page_list(&node_folio_list, NODE_DATA(nid));
 
        memalloc_noreclaim_restore(noreclaim_flag);
 
@@ -3125,9 +3183,10 @@ static void shrink_node_memcgs(pg_data_t *pgdat, struct scan_control *sc)
                            sc->priority);
 
                /* Record the group's reclaim efficiency */
-               vmpressure(sc->gfp_mask, memcg, false,
-                          sc->nr_scanned - scanned,
-                          sc->nr_reclaimed - reclaimed);
+               if (!sc->proactive)
+                       vmpressure(sc->gfp_mask, memcg, false,
+                                  sc->nr_scanned - scanned,
+                                  sc->nr_reclaimed - reclaimed);
 
        } while ((memcg = mem_cgroup_iter(target_memcg, memcg, NULL)));
 }
@@ -3250,9 +3309,10 @@ again:
        }
 
        /* Record the subtree's reclaim efficiency */
-       vmpressure(sc->gfp_mask, sc->target_mem_cgroup, true,
-                  sc->nr_scanned - nr_scanned,
-                  sc->nr_reclaimed - nr_reclaimed);
+       if (!sc->proactive)
+               vmpressure(sc->gfp_mask, sc->target_mem_cgroup, true,
+                          sc->nr_scanned - nr_scanned,
+                          sc->nr_reclaimed - nr_reclaimed);
 
        if (sc->nr_reclaimed - nr_reclaimed)
                reclaimable = true;
@@ -3534,8 +3594,9 @@ retry:
                __count_zid_vm_events(ALLOCSTALL, sc->reclaim_idx, 1);
 
        do {
-               vmpressure_prio(sc->gfp_mask, sc->target_mem_cgroup,
-                               sc->priority);
+               if (!sc->proactive)
+                       vmpressure_prio(sc->gfp_mask, sc->target_mem_cgroup,
+                                       sc->priority);
                sc->nr_scanned = 0;
                shrink_zones(zonelist, sc);
 
@@ -3825,7 +3886,7 @@ unsigned long mem_cgroup_shrink_node(struct mem_cgroup *memcg,
 unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
                                           unsigned long nr_pages,
                                           gfp_t gfp_mask,
-                                          bool may_swap)
+                                          unsigned int reclaim_options)
 {
        unsigned long nr_reclaimed;
        unsigned int noreclaim_flag;
@@ -3838,7 +3899,8 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
                .priority = DEF_PRIORITY,
                .may_writepage = !laptop_mode,
                .may_unmap = 1,
-               .may_swap = may_swap,
+               .may_swap = !!(reclaim_options & MEMCG_RECLAIM_MAY_SWAP),
+               .proactive = !!(reclaim_options & MEMCG_RECLAIM_PROACTIVE),
        };
        /*
         * Traverse the ZONELIST_FALLBACK zonelist of the current node to put
@@ -4595,7 +4657,7 @@ void kswapd_run(int nid)
 
 /*
  * Called by memory hotplug when all memory in a node is offlined.  Caller must
- * hold mem_hotplug_begin/end().
+ * be holding mem_hotplug_begin/done().
  */
 void kswapd_stop(int nid)
 {
index 592569a..a5e8486 100644 (file)
@@ -625,7 +625,7 @@ static int __init workingset_init(void)
        pr_info("workingset: timestamp_bits=%d max_order=%d bucket_order=%u\n",
               timestamp_bits, max_order, bucket_order);
 
-       ret = prealloc_shrinker(&workingset_shadow_shrinker);
+       ret = prealloc_shrinker(&workingset_shadow_shrinker, "mm-shadow");
        if (ret)
                goto err;
        ret = __list_lru_init(&shadow_nodes, true, &shadow_nodes_key,
index 71d6edc..34f784a 100644 (file)
@@ -386,7 +386,10 @@ static int zs_zpool_malloc(void *pool, size_t size, gfp_t gfp,
                        unsigned long *handle)
 {
        *handle = zs_malloc(pool, size, gfp);
-       return *handle ? 0 : -1;
+
+       if (IS_ERR((void *)(*handle)))
+               return PTR_ERR((void *)*handle);
+       return 0;
 }
 static void zs_zpool_free(void *pool, unsigned long handle)
 {
@@ -1388,7 +1391,7 @@ static unsigned long obj_malloc(struct zs_pool *pool,
  * @gfp: gfp flags when allocating object
  *
  * On success, handle to the allocated object is returned,
- * otherwise 0.
+ * otherwise an ERR_PTR().
  * Allocation requests with size > ZS_MAX_ALLOC_SIZE will fail.
  */
 unsigned long zs_malloc(struct zs_pool *pool, size_t size, gfp_t gfp)
@@ -1399,11 +1402,11 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size, gfp_t gfp)
        struct zspage *zspage;
 
        if (unlikely(!size || size > ZS_MAX_ALLOC_SIZE))
-               return 0;
+               return (unsigned long)ERR_PTR(-EINVAL);
 
        handle = cache_alloc_handle(pool, gfp);
        if (!handle)
-               return 0;
+               return (unsigned long)ERR_PTR(-ENOMEM);
 
        /* extra space in chunk to keep the handle */
        size += ZS_HANDLE_SIZE;
@@ -1428,7 +1431,7 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size, gfp_t gfp)
        zspage = alloc_zspage(pool, class, gfp);
        if (!zspage) {
                cache_free_handle(pool, handle);
-               return 0;
+               return (unsigned long)ERR_PTR(-ENOMEM);
        }
 
        spin_lock(&class->lock);
@@ -2169,7 +2172,8 @@ static int zs_register_shrinker(struct zs_pool *pool)
        pool->shrinker.batch = 0;
        pool->shrinker.seeks = DEFAULT_SEEKS;
 
-       return register_shrinker(&pool->shrinker);
+       return register_shrinker(&pool->shrinker, "mm-zspool:%s",
+                                pool->name);
 }
 
 /**
index 0ec2f59..6b9f191 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/user_namespace.h>
 #include <linux/net_namespace.h>
 #include <linux/sched/task.h>
+#include <linux/sched/mm.h>
 #include <linux/uidgid.h>
 #include <linux/cookie.h>
 
@@ -1143,7 +1144,13 @@ static int __register_pernet_operations(struct list_head *list,
                 * setup_net() and cleanup_net() are not possible.
                 */
                for_each_net(net) {
+                       struct mem_cgroup *old, *memcg;
+
+                       memcg = mem_cgroup_or_root(get_mem_cgroup_from_obj(net));
+                       old = set_active_memcg(memcg);
                        error = ops_init(ops, net);
+                       set_active_memcg(old);
+                       mem_cgroup_put(memcg);
                        if (error)
                                goto out_undo;
                        list_add_tail(&net->exit_list, &net_exit_list);
index b74905f..9b203d8 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/dma-direction.h>
 #include <linux/dma-mapping.h>
 #include <linux/page-flags.h>
-#include <linux/mm.h> /* for __put_page() */
+#include <linux/mm.h> /* for put_page() */
 #include <linux/poison.h>
 #include <linux/ethtool.h>
 
index 682fcd2..04e7b55 100644 (file)
@@ -874,7 +874,7 @@ int __init rpcauth_init_module(void)
        err = rpc_init_authunix();
        if (err < 0)
                goto out1;
-       err = register_shrinker(&rpc_cred_shrinker);
+       err = register_shrinker(&rpc_cred_shrinker, "sunrpc_cred");
        if (err < 0)
                goto out2;
        return 0;
diff --git a/tools/cgroup/memcg_shrinker.py b/tools/cgroup/memcg_shrinker.py
new file mode 100644 (file)
index 0000000..706ab27
--- /dev/null
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 Roman Gushchin <roman.gushchin@linux.dev>
+# Copyright (C) 2022 Meta
+
+import os
+import argparse
+import sys
+
+
+def scan_cgroups(cgroup_root):
+    cgroups = {}
+
+    for root, subdirs, _ in os.walk(cgroup_root):
+        for cgroup in subdirs:
+            path = os.path.join(root, cgroup)
+            ino = os.stat(path).st_ino
+            cgroups[ino] = path
+
+    # (memcg ino, path)
+    return cgroups
+
+
+def scan_shrinkers(shrinker_debugfs):
+    shrinkers = []
+
+    for root, subdirs, _ in os.walk(shrinker_debugfs):
+        for shrinker in subdirs:
+            count_path = os.path.join(root, shrinker, "count")
+            with open(count_path) as f:
+                for line in f.readlines():
+                    items = line.split(' ')
+                    ino = int(items[0])
+                    # (count, shrinker, memcg ino)
+                    shrinkers.append((int(items[1]), shrinker, ino))
+    return shrinkers
+
+
+def main():
+    parser = argparse.ArgumentParser(description='Display biggest shrinkers')
+    parser.add_argument('-n', '--lines', type=int, help='Number of lines to print')
+
+    args = parser.parse_args()
+
+    cgroups = scan_cgroups("/sys/fs/cgroup/")
+    shrinkers = scan_shrinkers("/sys/kernel/debug/shrinker/")
+    shrinkers = sorted(shrinkers, reverse = True, key = lambda x: x[0])
+
+    n = 0
+    for s in shrinkers:
+        count, name, ino = (s[0], s[1], s[2])
+        if count == 0:
+            break
+
+        if ino == 0 or ino == 1:
+            cg = "/"
+        else:
+            try:
+                cg = cgroups[ino]
+            except KeyError:
+                cg = "unknown (%d)" % ino
+
+        print("%-8s %-20s %s" % (count, name, cg))
+
+        n += 1
+        if args.lines and n >= args.lines:
+            break
+
+
+if __name__ == '__main__':
+    main()
index 462f8c5..5fed13b 100644 (file)
@@ -7,7 +7,7 @@ static inline void kmemleak_free_part_phys(phys_addr_t phys, size_t size)
 }
 
 static inline void kmemleak_alloc_phys(phys_addr_t phys, size_t size,
-                                      int min_count, gfp_t gfp)
+                                      gfp_t gfp)
 {
 }
 
index 108587c..d9fa6a9 100644 (file)
@@ -93,6 +93,7 @@ TEST_PROGS := run_vmtests.sh
 
 TEST_FILES := test_vmalloc.sh
 TEST_FILES += test_hmm.sh
+TEST_FILES += va_128TBswitch.sh
 
 include ../lib.mk
 
index 2033239..529f53b 100644 (file)
@@ -36,6 +36,7 @@
  * in the usual include/uapi/... directory.
  */
 #include "../../../../lib/test_hmm_uapi.h"
+#include "../../../../mm/gup_test.h"
 
 struct hmm_buffer {
        void            *ptr;
@@ -46,12 +47,22 @@ struct hmm_buffer {
        uint64_t        faults;
 };
 
+enum {
+       HMM_PRIVATE_DEVICE_ONE,
+       HMM_PRIVATE_DEVICE_TWO,
+       HMM_COHERENCE_DEVICE_ONE,
+       HMM_COHERENCE_DEVICE_TWO,
+};
+
 #define TWOMEG         (1 << 21)
 #define HMM_BUFFER_SIZE (1024 << 12)
 #define HMM_PATH_MAX    64
 #define NTIMES         10
 
 #define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1)))
+/* Just the flags we need, copied from mm.h: */
+#define FOLL_WRITE     0x01    /* check pte is writable */
+#define FOLL_LONGTERM   0x10000 /* mapping lifetime is indefinite */
 
 FIXTURE(hmm)
 {
@@ -60,6 +71,21 @@ FIXTURE(hmm)
        unsigned int    page_shift;
 };
 
+FIXTURE_VARIANT(hmm)
+{
+       int     device_number;
+};
+
+FIXTURE_VARIANT_ADD(hmm, hmm_device_private)
+{
+       .device_number = HMM_PRIVATE_DEVICE_ONE,
+};
+
+FIXTURE_VARIANT_ADD(hmm, hmm_device_coherent)
+{
+       .device_number = HMM_COHERENCE_DEVICE_ONE,
+};
+
 FIXTURE(hmm2)
 {
        int             fd0;
@@ -68,6 +94,24 @@ FIXTURE(hmm2)
        unsigned int    page_shift;
 };
 
+FIXTURE_VARIANT(hmm2)
+{
+       int     device_number0;
+       int     device_number1;
+};
+
+FIXTURE_VARIANT_ADD(hmm2, hmm2_device_private)
+{
+       .device_number0 = HMM_PRIVATE_DEVICE_ONE,
+       .device_number1 = HMM_PRIVATE_DEVICE_TWO,
+};
+
+FIXTURE_VARIANT_ADD(hmm2, hmm2_device_coherent)
+{
+       .device_number0 = HMM_COHERENCE_DEVICE_ONE,
+       .device_number1 = HMM_COHERENCE_DEVICE_TWO,
+};
+
 static int hmm_open(int unit)
 {
        char pathname[HMM_PATH_MAX];
@@ -81,12 +125,19 @@ static int hmm_open(int unit)
        return fd;
 }
 
+static bool hmm_is_coherent_type(int dev_num)
+{
+       return (dev_num >= HMM_COHERENCE_DEVICE_ONE);
+}
+
 FIXTURE_SETUP(hmm)
 {
        self->page_size = sysconf(_SC_PAGE_SIZE);
        self->page_shift = ffs(self->page_size) - 1;
 
-       self->fd = hmm_open(0);
+       self->fd = hmm_open(variant->device_number);
+       if (self->fd < 0 && hmm_is_coherent_type(variant->device_number))
+               SKIP(exit(0), "DEVICE_COHERENT not available");
        ASSERT_GE(self->fd, 0);
 }
 
@@ -95,9 +146,11 @@ FIXTURE_SETUP(hmm2)
        self->page_size = sysconf(_SC_PAGE_SIZE);
        self->page_shift = ffs(self->page_size) - 1;
 
-       self->fd0 = hmm_open(0);
+       self->fd0 = hmm_open(variant->device_number0);
+       if (self->fd0 < 0 && hmm_is_coherent_type(variant->device_number0))
+               SKIP(exit(0), "DEVICE_COHERENT not available");
        ASSERT_GE(self->fd0, 0);
-       self->fd1 = hmm_open(1);
+       self->fd1 = hmm_open(variant->device_number1);
        ASSERT_GE(self->fd1, 0);
 }
 
@@ -211,6 +264,20 @@ static void hmm_nanosleep(unsigned int n)
        nanosleep(&t, NULL);
 }
 
+static int hmm_migrate_sys_to_dev(int fd,
+                                  struct hmm_buffer *buffer,
+                                  unsigned long npages)
+{
+       return hmm_dmirror_cmd(fd, HMM_DMIRROR_MIGRATE_TO_DEV, buffer, npages);
+}
+
+static int hmm_migrate_dev_to_sys(int fd,
+                                  struct hmm_buffer *buffer,
+                                  unsigned long npages)
+{
+       return hmm_dmirror_cmd(fd, HMM_DMIRROR_MIGRATE_TO_SYS, buffer, npages);
+}
+
 /*
  * Simple NULL test of device open/close.
  */
@@ -875,7 +942,7 @@ TEST_F(hmm, migrate)
                ptr[i] = i;
 
        /* Migrate memory to device. */
-       ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_MIGRATE, buffer, npages);
+       ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
        ASSERT_EQ(ret, 0);
        ASSERT_EQ(buffer->cpages, npages);
 
@@ -923,7 +990,7 @@ TEST_F(hmm, migrate_fault)
                ptr[i] = i;
 
        /* Migrate memory to device. */
-       ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_MIGRATE, buffer, npages);
+       ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
        ASSERT_EQ(ret, 0);
        ASSERT_EQ(buffer->cpages, npages);
 
@@ -936,7 +1003,7 @@ TEST_F(hmm, migrate_fault)
                ASSERT_EQ(ptr[i], i);
 
        /* Migrate memory to the device again. */
-       ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_MIGRATE, buffer, npages);
+       ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
        ASSERT_EQ(ret, 0);
        ASSERT_EQ(buffer->cpages, npages);
 
@@ -976,7 +1043,7 @@ TEST_F(hmm, migrate_shared)
        ASSERT_NE(buffer->ptr, MAP_FAILED);
 
        /* Migrate memory to device. */
-       ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_MIGRATE, buffer, npages);
+       ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
        ASSERT_EQ(ret, -ENOENT);
 
        hmm_buffer_free(buffer);
@@ -1015,7 +1082,7 @@ TEST_F(hmm2, migrate_mixed)
        p = buffer->ptr;
 
        /* Migrating a protected area should be an error. */
-       ret = hmm_dmirror_cmd(self->fd1, HMM_DMIRROR_MIGRATE, buffer, npages);
+       ret = hmm_migrate_sys_to_dev(self->fd1, buffer, npages);
        ASSERT_EQ(ret, -EINVAL);
 
        /* Punch a hole after the first page address. */
@@ -1023,7 +1090,7 @@ TEST_F(hmm2, migrate_mixed)
        ASSERT_EQ(ret, 0);
 
        /* We expect an error if the vma doesn't cover the range. */
-       ret = hmm_dmirror_cmd(self->fd1, HMM_DMIRROR_MIGRATE, buffer, 3);
+       ret = hmm_migrate_sys_to_dev(self->fd1, buffer, 3);
        ASSERT_EQ(ret, -EINVAL);
 
        /* Page 2 will be a read-only zero page. */
@@ -1055,13 +1122,13 @@ TEST_F(hmm2, migrate_mixed)
 
        /* Now try to migrate pages 2-5 to device 1. */
        buffer->ptr = p + 2 * self->page_size;
-       ret = hmm_dmirror_cmd(self->fd1, HMM_DMIRROR_MIGRATE, buffer, 4);
+       ret = hmm_migrate_sys_to_dev(self->fd1, buffer, 4);
        ASSERT_EQ(ret, 0);
        ASSERT_EQ(buffer->cpages, 4);
 
        /* Page 5 won't be migrated to device 0 because it's on device 1. */
        buffer->ptr = p + 5 * self->page_size;
-       ret = hmm_dmirror_cmd(self->fd0, HMM_DMIRROR_MIGRATE, buffer, 1);
+       ret = hmm_migrate_sys_to_dev(self->fd0, buffer, 1);
        ASSERT_EQ(ret, -ENOENT);
        buffer->ptr = p;
 
@@ -1070,8 +1137,12 @@ TEST_F(hmm2, migrate_mixed)
 }
 
 /*
- * Migrate anonymous memory to device private memory and fault it back to system
- * memory multiple times.
+ * Migrate anonymous memory to device memory and back to system memory
+ * multiple times. In case of private zone configuration, this is done
+ * through fault pages accessed by CPU. In case of coherent zone configuration,
+ * the pages from the device should be explicitly migrated back to system memory.
+ * The reason is Coherent device zone has coherent access by CPU, therefore
+ * it will not generate any page fault.
  */
 TEST_F(hmm, migrate_multiple)
 {
@@ -1107,8 +1178,7 @@ TEST_F(hmm, migrate_multiple)
                        ptr[i] = i;
 
                /* Migrate memory to device. */
-               ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_MIGRATE, buffer,
-                                     npages);
+               ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
                ASSERT_EQ(ret, 0);
                ASSERT_EQ(buffer->cpages, npages);
 
@@ -1116,7 +1186,13 @@ TEST_F(hmm, migrate_multiple)
                for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
                        ASSERT_EQ(ptr[i], i);
 
-               /* Fault pages back to system memory and check them. */
+               /* Migrate back to system memory and check them. */
+               if (hmm_is_coherent_type(variant->device_number)) {
+                       ret = hmm_migrate_dev_to_sys(self->fd, buffer, npages);
+                       ASSERT_EQ(ret, 0);
+                       ASSERT_EQ(buffer->cpages, npages);
+               }
+
                for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
                        ASSERT_EQ(ptr[i], i);
 
@@ -1354,13 +1430,13 @@ TEST_F(hmm2, snapshot)
 
        /* Page 5 will be migrated to device 0. */
        buffer->ptr = p + 5 * self->page_size;
-       ret = hmm_dmirror_cmd(self->fd0, HMM_DMIRROR_MIGRATE, buffer, 1);
+       ret = hmm_migrate_sys_to_dev(self->fd0, buffer, 1);
        ASSERT_EQ(ret, 0);
        ASSERT_EQ(buffer->cpages, 1);
 
        /* Page 6 will be migrated to device 1. */
        buffer->ptr = p + 6 * self->page_size;
-       ret = hmm_dmirror_cmd(self->fd1, HMM_DMIRROR_MIGRATE, buffer, 1);
+       ret = hmm_migrate_sys_to_dev(self->fd1, buffer, 1);
        ASSERT_EQ(ret, 0);
        ASSERT_EQ(buffer->cpages, 1);
 
@@ -1377,9 +1453,16 @@ TEST_F(hmm2, snapshot)
        ASSERT_EQ(m[2], HMM_DMIRROR_PROT_ZERO | HMM_DMIRROR_PROT_READ);
        ASSERT_EQ(m[3], HMM_DMIRROR_PROT_READ);
        ASSERT_EQ(m[4], HMM_DMIRROR_PROT_WRITE);
-       ASSERT_EQ(m[5], HMM_DMIRROR_PROT_DEV_PRIVATE_LOCAL |
-                       HMM_DMIRROR_PROT_WRITE);
-       ASSERT_EQ(m[6], HMM_DMIRROR_PROT_NONE);
+       if (!hmm_is_coherent_type(variant->device_number0)) {
+               ASSERT_EQ(m[5], HMM_DMIRROR_PROT_DEV_PRIVATE_LOCAL |
+                               HMM_DMIRROR_PROT_WRITE);
+               ASSERT_EQ(m[6], HMM_DMIRROR_PROT_NONE);
+       } else {
+               ASSERT_EQ(m[5], HMM_DMIRROR_PROT_DEV_COHERENT_LOCAL |
+                               HMM_DMIRROR_PROT_WRITE);
+               ASSERT_EQ(m[6], HMM_DMIRROR_PROT_DEV_COHERENT_REMOTE |
+                               HMM_DMIRROR_PROT_WRITE);
+       }
 
        hmm_buffer_free(buffer);
 }
@@ -1520,9 +1603,19 @@ TEST_F(hmm2, double_map)
        for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
                ASSERT_EQ(ptr[i], i);
 
-       /* Punch a hole after the first page address. */
-       ret = munmap(buffer->ptr + self->page_size, self->page_size);
+       /* Migrate pages to device 1 and try to read from device 0. */
+       ret = hmm_migrate_sys_to_dev(self->fd1, buffer, npages);
        ASSERT_EQ(ret, 0);
+       ASSERT_EQ(buffer->cpages, npages);
+
+       ret = hmm_dmirror_cmd(self->fd0, HMM_DMIRROR_READ, buffer, npages);
+       ASSERT_EQ(ret, 0);
+       ASSERT_EQ(buffer->cpages, npages);
+       ASSERT_EQ(buffer->faults, 1);
+
+       /* Check what device 0 read. */
+       for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+               ASSERT_EQ(ptr[i], i);
 
        hmm_buffer_free(buffer);
 }
@@ -1685,4 +1778,190 @@ TEST_F(hmm, exclusive_cow)
        hmm_buffer_free(buffer);
 }
 
+static int gup_test_exec(int gup_fd, unsigned long addr, int cmd,
+                        int npages, int size, int flags)
+{
+       struct gup_test gup = {
+               .nr_pages_per_call      = npages,
+               .addr                   = addr,
+               .gup_flags              = FOLL_WRITE | flags,
+               .size                   = size,
+       };
+
+       if (ioctl(gup_fd, cmd, &gup)) {
+               perror("ioctl on error\n");
+               return errno;
+       }
+
+       return 0;
+}
+
+/*
+ * Test get user device pages through gup_test. Setting PIN_LONGTERM flag.
+ * This should trigger a migration back to system memory for both, private
+ * and coherent type pages.
+ * This test makes use of gup_test module. Make sure GUP_TEST_CONFIG is added
+ * to your configuration before you run it.
+ */
+TEST_F(hmm, hmm_gup_test)
+{
+       struct hmm_buffer *buffer;
+       int gup_fd;
+       unsigned long npages;
+       unsigned long size;
+       unsigned long i;
+       int *ptr;
+       int ret;
+       unsigned char *m;
+
+       gup_fd = open("/sys/kernel/debug/gup_test", O_RDWR);
+       if (gup_fd == -1)
+               SKIP(return, "Skipping test, could not find gup_test driver");
+
+       npages = 4;
+       size = npages << self->page_shift;
+
+       buffer = malloc(sizeof(*buffer));
+       ASSERT_NE(buffer, NULL);
+
+       buffer->fd = -1;
+       buffer->size = size;
+       buffer->mirror = malloc(size);
+       ASSERT_NE(buffer->mirror, NULL);
+
+       buffer->ptr = mmap(NULL, size,
+                          PROT_READ | PROT_WRITE,
+                          MAP_PRIVATE | MAP_ANONYMOUS,
+                          buffer->fd, 0);
+       ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+       /* Initialize buffer in system memory. */
+       for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+               ptr[i] = i;
+
+       /* Migrate memory to device. */
+       ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
+       ASSERT_EQ(ret, 0);
+       ASSERT_EQ(buffer->cpages, npages);
+       /* Check what the device read. */
+       for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+               ASSERT_EQ(ptr[i], i);
+
+       ASSERT_EQ(gup_test_exec(gup_fd,
+                               (unsigned long)buffer->ptr,
+                               GUP_BASIC_TEST, 1, self->page_size, 0), 0);
+       ASSERT_EQ(gup_test_exec(gup_fd,
+                               (unsigned long)buffer->ptr + 1 * self->page_size,
+                               GUP_FAST_BENCHMARK, 1, self->page_size, 0), 0);
+       ASSERT_EQ(gup_test_exec(gup_fd,
+                               (unsigned long)buffer->ptr + 2 * self->page_size,
+                               PIN_FAST_BENCHMARK, 1, self->page_size, FOLL_LONGTERM), 0);
+       ASSERT_EQ(gup_test_exec(gup_fd,
+                               (unsigned long)buffer->ptr + 3 * self->page_size,
+                               PIN_LONGTERM_BENCHMARK, 1, self->page_size, 0), 0);
+
+       /* Take snapshot to CPU pagetables */
+       ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_SNAPSHOT, buffer, npages);
+       ASSERT_EQ(ret, 0);
+       ASSERT_EQ(buffer->cpages, npages);
+       m = buffer->mirror;
+       if (hmm_is_coherent_type(variant->device_number)) {
+               ASSERT_EQ(HMM_DMIRROR_PROT_DEV_COHERENT_LOCAL | HMM_DMIRROR_PROT_WRITE, m[0]);
+               ASSERT_EQ(HMM_DMIRROR_PROT_DEV_COHERENT_LOCAL | HMM_DMIRROR_PROT_WRITE, m[1]);
+       } else {
+               ASSERT_EQ(HMM_DMIRROR_PROT_WRITE, m[0]);
+               ASSERT_EQ(HMM_DMIRROR_PROT_WRITE, m[1]);
+       }
+       ASSERT_EQ(HMM_DMIRROR_PROT_WRITE, m[2]);
+       ASSERT_EQ(HMM_DMIRROR_PROT_WRITE, m[3]);
+       /*
+        * Check again the content on the pages. Make sure there's no
+        * corrupted data.
+        */
+       for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+               ASSERT_EQ(ptr[i], i);
+
+       close(gup_fd);
+       hmm_buffer_free(buffer);
+}
+
+/*
+ * Test copy-on-write in device pages.
+ * In case of writing to COW private page(s), a page fault will migrate pages
+ * back to system memory first. Then, these pages will be duplicated. In case
+ * of COW device coherent type, pages are duplicated directly from device
+ * memory.
+ */
+TEST_F(hmm, hmm_cow_in_device)
+{
+       struct hmm_buffer *buffer;
+       unsigned long npages;
+       unsigned long size;
+       unsigned long i;
+       int *ptr;
+       int ret;
+       unsigned char *m;
+       pid_t pid;
+       int status;
+
+       npages = 4;
+       size = npages << self->page_shift;
+
+       buffer = malloc(sizeof(*buffer));
+       ASSERT_NE(buffer, NULL);
+
+       buffer->fd = -1;
+       buffer->size = size;
+       buffer->mirror = malloc(size);
+       ASSERT_NE(buffer->mirror, NULL);
+
+       buffer->ptr = mmap(NULL, size,
+                          PROT_READ | PROT_WRITE,
+                          MAP_PRIVATE | MAP_ANONYMOUS,
+                          buffer->fd, 0);
+       ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+       /* Initialize buffer in system memory. */
+       for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+               ptr[i] = i;
+
+       /* Migrate memory to device. */
+
+       ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
+       ASSERT_EQ(ret, 0);
+       ASSERT_EQ(buffer->cpages, npages);
+
+       pid = fork();
+       if (pid == -1)
+               ASSERT_EQ(pid, 0);
+       if (!pid) {
+               /* Child process waitd for SIGTERM from the parent. */
+               while (1) {
+               }
+               perror("Should not reach this\n");
+               exit(0);
+       }
+       /* Parent process writes to COW pages(s) and gets a
+        * new copy in system. In case of device private pages,
+        * this write causes a migration to system mem first.
+        */
+       for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+               ptr[i] = i;
+
+       /* Terminate child and wait */
+       EXPECT_EQ(0, kill(pid, SIGTERM));
+       EXPECT_EQ(pid, waitpid(pid, &status, 0));
+       EXPECT_NE(0, WIFSIGNALED(status));
+       EXPECT_EQ(SIGTERM, WTERMSIG(status));
+
+       /* Take snapshot to CPU pagetables */
+       ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_SNAPSHOT, buffer, npages);
+       ASSERT_EQ(ret, 0);
+       ASSERT_EQ(buffer->cpages, npages);
+       m = buffer->mirror;
+       for (i = 0; i < npages; i++)
+               ASSERT_EQ(HMM_DMIRROR_PROT_WRITE, m[i]);
+
+       hmm_buffer_free(buffer);
+}
 TEST_HARNESS_MAIN
index 585978f..e63a021 100644 (file)
@@ -107,7 +107,7 @@ static void register_region_with_uffd(char *addr, size_t len)
 
 int main(int argc, char *argv[])
 {
-       size_t length;
+       size_t length = 0;
 
        if (argc != 2 && argc != 3) {
                printf("Usage: %s [length_in_MB] <hugetlb_file>\n", argv[0]);
index 6c6af40..3c99431 100644 (file)
@@ -89,10 +89,11 @@ void write_fault_pages(void *addr, unsigned long nr_pages)
 
 void read_fault_pages(void *addr, unsigned long nr_pages)
 {
-       unsigned long i, tmp;
+       unsigned long dummy = 0;
+       unsigned long i;
 
        for (i = 0; i < nr_pages; i++)
-               tmp += *((unsigned long *)(addr + (i * huge_page_size)));
+               dummy += *((unsigned long *)(addr + (i * huge_page_size)));
 }
 
 int main(int argc, char **argv)
index 96671c2..6c62966 100644 (file)
@@ -62,19 +62,22 @@ static int alloc_noexit(unsigned long nr_pages, int pipefd)
 /* The process_mrelease calls in this test are expected to fail */
 static void run_negative_tests(int pidfd)
 {
+       int res;
        /* Test invalid flags. Expect to fail with EINVAL error code. */
        if (!syscall(__NR_process_mrelease, pidfd, (unsigned int)-1) ||
                        errno != EINVAL) {
+               res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL);
                perror("process_mrelease with wrong flags");
-               exit(errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL);
+               exit(res);
        }
        /*
         * Test reaping while process is alive with no pending SIGKILL.
         * Expect to fail with EINVAL error code.
         */
        if (!syscall(__NR_process_mrelease, pidfd, 0) || errno != EINVAL) {
+               res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL);
                perror("process_mrelease on a live process");
-               exit(errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL);
+               exit(res);
        }
 }
 
@@ -100,8 +103,9 @@ int main(void)
 
        /* Test a wrong pidfd */
        if (!syscall(__NR_process_mrelease, -1, 0) || errno != EBADF) {
+               res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL);
                perror("process_mrelease with wrong pidfd");
-               exit(errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL);
+               exit(res);
        }
 
        /* Start the test with 1MB child memory allocation */
@@ -156,8 +160,9 @@ retry:
        run_negative_tests(pidfd);
 
        if (kill(pid, SIGKILL)) {
+               res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL);
                perror("kill");
-               exit(errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL);
+               exit(res);
        }
 
        success = (syscall(__NR_process_mrelease, pidfd, 0) == 0);
@@ -172,9 +177,10 @@ retry:
                if (errno == ESRCH) {
                        retry = (size <= MAX_SIZE_MB);
                } else {
+                       res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL);
                        perror("process_mrelease");
                        waitpid(pid, NULL, 0);
-                       exit(errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL);
+                       exit(res);
                }
        }
 
index 41fce8b..de86983 100755 (executable)
@@ -151,7 +151,7 @@ if [ $VADDR64 -ne 0 ]; then
        run_test ./virtual_address_range
 
        # virtual address 128TB switch test
-       run_test ./va_128TBswitch
+       run_test ./va_128TBswitch.sh
 fi # VADDR64
 
 # vmalloc stability smoke test
@@ -179,4 +179,17 @@ run_test ./ksm_tests -N -m 1
 # KSM test with 2 NUMA nodes and merge_across_nodes = 0
 run_test ./ksm_tests -N -m 0
 
+# protection_keys tests
+if [ -x ./protection_keys_32 ]
+then
+       run_test ./protection_keys_32
+fi
+
+if [ -x ./protection_keys_64 ]
+then
+       run_test ./protection_keys_64
+fi
+
+run_test ./soft-dirty
+
 exit $exitcode
index 08ab62a..e3a43f5 100644 (file)
@@ -121,13 +121,76 @@ static void test_hugepage(int pagemap_fd, int pagesize)
        free(map);
 }
 
+static void test_mprotect(int pagemap_fd, int pagesize, bool anon)
+{
+       const char *type[] = {"file", "anon"};
+       const char *fname = "./soft-dirty-test-file";
+       int test_fd;
+       char *map;
+
+       if (anon) {
+               map = mmap(NULL, pagesize, PROT_READ|PROT_WRITE,
+                          MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+               if (!map)
+                       ksft_exit_fail_msg("anon mmap failed\n");
+       } else {
+               test_fd = open(fname, O_RDWR | O_CREAT);
+               if (test_fd < 0) {
+                       ksft_test_result_skip("Test %s open() file failed\n", __func__);
+                       return;
+               }
+               unlink(fname);
+               ftruncate(test_fd, pagesize);
+               map = mmap(NULL, pagesize, PROT_READ|PROT_WRITE,
+                          MAP_SHARED, test_fd, 0);
+               if (!map)
+                       ksft_exit_fail_msg("file mmap failed\n");
+       }
+
+       *map = 1;
+       ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 1,
+                        "Test %s-%s dirty bit of new written page\n",
+                        __func__, type[anon]);
+       clear_softdirty();
+       ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 0,
+                        "Test %s-%s soft-dirty clear after clear_refs\n",
+                        __func__, type[anon]);
+       mprotect(map, pagesize, PROT_READ);
+       ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 0,
+                        "Test %s-%s soft-dirty clear after marking RO\n",
+                        __func__, type[anon]);
+       mprotect(map, pagesize, PROT_READ|PROT_WRITE);
+       ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 0,
+                        "Test %s-%s soft-dirty clear after marking RW\n",
+                        __func__, type[anon]);
+       *map = 2;
+       ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 1,
+                        "Test %s-%s soft-dirty after rewritten\n",
+                        __func__, type[anon]);
+
+       munmap(map, pagesize);
+
+       if (!anon)
+               close(test_fd);
+}
+
+static void test_mprotect_anon(int pagemap_fd, int pagesize)
+{
+       test_mprotect(pagemap_fd, pagesize, true);
+}
+
+static void test_mprotect_file(int pagemap_fd, int pagesize)
+{
+       test_mprotect(pagemap_fd, pagesize, false);
+}
+
 int main(int argc, char **argv)
 {
        int pagemap_fd;
        int pagesize;
 
        ksft_print_header();
-       ksft_set_plan(5);
+       ksft_set_plan(15);
 
        pagemap_fd = open(PAGEMAP_FILE_PATH, O_RDONLY);
        if (pagemap_fd < 0)
@@ -138,6 +201,8 @@ int main(int argc, char **argv)
        test_simple(pagemap_fd, pagesize);
        test_vma_reuse(pagemap_fd, pagesize);
        test_hugepage(pagemap_fd, pagesize);
+       test_mprotect_anon(pagemap_fd, pagesize);
+       test_mprotect_file(pagemap_fd, pagesize);
 
        close(pagemap_fd);
 
index 0647b52..539c937 100755 (executable)
@@ -40,11 +40,26 @@ check_test_requirements()
 
 load_driver()
 {
-       modprobe $DRIVER > /dev/null 2>&1
+       if [ $# -eq 0 ]; then
+               modprobe $DRIVER > /dev/null 2>&1
+       else
+               if [ $# -eq 2 ]; then
+                       modprobe $DRIVER spm_addr_dev0=$1 spm_addr_dev1=$2
+                               > /dev/null 2>&1
+               else
+                       echo "Missing module parameters. Make sure pass"\
+                       "spm_addr_dev0 and spm_addr_dev1"
+                       usage
+               fi
+       fi
        if [ $? == 0 ]; then
                major=$(awk "\$2==\"HMM_DMIRROR\" {print \$1}" /proc/devices)
                mknod /dev/hmm_dmirror0 c $major 0
                mknod /dev/hmm_dmirror1 c $major 1
+               if [ $# -eq 2 ]; then
+                       mknod /dev/hmm_dmirror2 c $major 2
+                       mknod /dev/hmm_dmirror3 c $major 3
+               fi
        fi
 }
 
@@ -58,7 +73,7 @@ run_smoke()
 {
        echo "Running smoke test. Note, this test provides basic coverage."
 
-       load_driver
+       load_driver $1 $2
        $(dirname "${BASH_SOURCE[0]}")/hmm-tests
        unload_driver
 }
@@ -75,6 +90,9 @@ usage()
        echo "# Smoke testing"
        echo "./${TEST_NAME}.sh smoke"
        echo
+       echo "# Smoke testing with SPM enabled"
+       echo "./${TEST_NAME}.sh smoke <spm_addr_dev0> <spm_addr_dev1>"
+       echo
        exit 0
 }
 
@@ -84,7 +102,7 @@ function run_test()
                usage
        else
                if [ "$1" = "smoke" ]; then
-                       run_smoke
+                       run_smoke $2 $3
                else
                        usage
                fi
index 4bc2458..7c3f1b0 100644 (file)
@@ -931,7 +931,7 @@ static int faulting_process(int signal_test)
        unsigned long split_nr_pages;
        unsigned long lastnr;
        struct sigaction act;
-       unsigned long signalled = 0;
+       volatile unsigned long signalled = 0;
 
        split_nr_pages = (nr_pages + 1) / 2;
 
@@ -946,7 +946,7 @@ static int faulting_process(int signal_test)
        }
 
        for (nr = 0; nr < split_nr_pages; nr++) {
-               int steps = 1;
+               volatile int steps = 1;
                unsigned long offset = nr * page_size;
 
                if (signal_test) {
index da6ec3b..1d20689 100644 (file)
@@ -231,7 +231,7 @@ static struct testcase hugetlb_testcases[] = {
 static int run_test(struct testcase *test, int count)
 {
        void *p;
-       int i, ret = 0;
+       int i, ret = KSFT_PASS;
 
        for (i = 0; i < count; i++) {
                struct testcase *t = test + i;
@@ -242,13 +242,13 @@ static int run_test(struct testcase *test, int count)
 
                if (p == MAP_FAILED) {
                        printf("FAILED\n");
-                       ret = 1;
+                       ret = KSFT_FAIL;
                        continue;
                }
 
                if (t->low_addr_required && p >= (void *)(ADDR_SWITCH_HINT)) {
                        printf("FAILED\n");
-                       ret = 1;
+                       ret = KSFT_FAIL;
                } else {
                        /*
                         * Do a dereference of the address returned so that we catch
@@ -280,7 +280,7 @@ int main(int argc, char **argv)
        int ret;
 
        if (!supported_arch())
-               return 0;
+               return KSFT_SKIP;
 
        ret = run_test(testcases, ARRAY_SIZE(testcases));
        if (argc == 2 && !strcmp(argv[1], "--run-hugetlb"))
diff --git a/tools/testing/selftests/vm/va_128TBswitch.sh b/tools/testing/selftests/vm/va_128TBswitch.sh
new file mode 100755 (executable)
index 0000000..4158075
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2022 Adam Sindelar (Meta) <adam@wowsignal.io>
+#
+# This is a test for mmap behavior with 5-level paging. This script wraps the
+# real test to check that the kernel is configured to support at least 5
+# pagetable levels.
+
+# 1 means the test failed
+exitcode=1
+
+# Kselftest framework requirement - SKIP code is 4.
+ksft_skip=4
+
+fail()
+{
+       echo "$1"
+       exit $exitcode
+}
+
+check_supported_x86_64()
+{
+       local config="/proc/config.gz"
+       [[ -f "${config}" ]] || config="/boot/config-$(uname -r)"
+       [[ -f "${config}" ]] || fail "Cannot find kernel config in /proc or /boot"
+
+       # gzip -dcfq automatically handles both compressed and plaintext input.
+       # See man 1 gzip under '-f'.
+       local pg_table_levels=$(gzip -dcfq "${config}" | grep PGTABLE_LEVELS | cut -d'=' -f 2)
+
+       if [[ "${pg_table_levels}" -lt 5 ]]; then
+               echo "$0: PGTABLE_LEVELS=${pg_table_levels}, must be >= 5 to run this test"
+               exit $ksft_skip
+       fi
+}
+
+check_test_requirements()
+{
+       # The test supports x86_64 and powerpc64. We currently have no useful
+       # eligibility check for powerpc64, and the test itself will reject other
+       # architectures.
+       case `uname -m` in
+               "x86_64")
+                       check_supported_x86_64
+               ;;
+               *)
+                       return 0
+               ;;
+       esac
+}
+
+check_test_requirements
+./va_128TBswitch
index c149427..ec2e67c 100644 (file)
@@ -8,7 +8,7 @@
  * Or sort by total memory:
  * ./page_owner_sort -m page_owner_full.txt sorted_page_owner.txt
  *
- * See Documentation/vm/page_owner.rst
+ * See Documentation/mm/page_owner.rst
 */
 
 #include <stdio.h>
@@ -470,23 +470,23 @@ static bool match_str_list(const char *str, char **list, int list_size)
 
 static bool is_need(char *buf)
 {
-               if ((filter & FILTER_UNRELEASE) && get_free_ts_nsec(buf) != 0)
-                       return false;
-               if ((filter & FILTER_PID) && !match_num_list(get_pid(buf), fc.pids, fc.pids_size))
-                       return false;
-               if ((filter & FILTER_TGID) &&
-                       !match_num_list(get_tgid(buf), fc.tgids, fc.tgids_size))
-                       return false;
+       if ((filter & FILTER_UNRELEASE) && get_free_ts_nsec(buf) != 0)
+               return false;
+       if ((filter & FILTER_PID) && !match_num_list(get_pid(buf), fc.pids, fc.pids_size))
+               return false;
+       if ((filter & FILTER_TGID) &&
+               !match_num_list(get_tgid(buf), fc.tgids, fc.tgids_size))
+               return false;
 
-               char *comm = get_comm(buf);
+       char *comm = get_comm(buf);
 
-               if ((filter & FILTER_COMM) &&
-               !match_str_list(comm, fc.comms, fc.comms_size)) {
-                       free(comm);
-                       return false;
-               }
+       if ((filter & FILTER_COMM) &&
+       !match_str_list(comm, fc.comms, fc.comms_size)) {
                free(comm);
-               return true;
+               return false;
+       }
+       free(comm);
+       return true;
 }
 
 static void add_list(char *buf, int len, char *ext_buf)
index 5b98f3e..0fffaee 100644 (file)
@@ -125,7 +125,7 @@ static void usage(void)
                "-n|--numa              Show NUMA information\n"
                "-N|--lines=K           Show the first K slabs\n"
                "-o|--ops               Show kmem_cache_ops\n"
-               "-P|--partial           Sort by number of partial slabs\n"
+               "-P|--partial           Sort by number of partial slabs\n"
                "-r|--report            Detailed report on single slabs\n"
                "-s|--shrink            Shrink slabs\n"
                "-S|--Size              Sort by size\n"
@@ -1067,15 +1067,27 @@ static void sort_slabs(void)
                for (s2 = s1 + 1; s2 < slabinfo + slabs; s2++) {
                        int result;
 
-                       if (sort_size)
-                               result = slab_size(s1) < slab_size(s2);
-                       else if (sort_active)
-                               result = slab_activity(s1) < slab_activity(s2);
-                       else if (sort_loss)
-                               result = slab_waste(s1) < slab_waste(s2);
-                       else if (sort_partial)
-                               result = s1->partial < s2->partial;
-                       else
+                       if (sort_size) {
+                               if (slab_size(s1) == slab_size(s2))
+                                       result = strcasecmp(s1->name, s2->name);
+                               else
+                                       result = slab_size(s1) < slab_size(s2);
+                       } else if (sort_active) {
+                               if (slab_activity(s1) == slab_activity(s2))
+                                       result = strcasecmp(s1->name, s2->name);
+                               else
+                                       result = slab_activity(s1) < slab_activity(s2);
+                       } else if (sort_loss) {
+                               if (slab_waste(s1) == slab_waste(s2))
+                                       result = strcasecmp(s1->name, s2->name);
+                               else
+                                       result = slab_waste(s1) < slab_waste(s2);
+                       } else if (sort_partial) {
+                               if (s1->partial == s2->partial)
+                                       result = strcasecmp(s1->name, s2->name);
+                               else
+                                       result = s1->partial < s2->partial;
+                       } else
                                result = strcasecmp(s1->name, s2->name);
 
                        if (show_inverted)