Merge patch series "riscv, mm: detect svnapot cpu support at runtime"
authorPalmer Dabbelt <palmer@rivosinc.com>
Thu, 9 Mar 2023 23:46:40 +0000 (15:46 -0800)
committerPalmer Dabbelt <palmer@rivosinc.com>
Fri, 10 Mar 2023 02:13:45 +0000 (18:13 -0800)
Qinglin Pan <panqinglin00@gmail.com> says:

Svnapot is a RISC-V extension for marking contiguous 4K pages as a non-4K
page. This patch set is for using Svnapot in hugetlb fs and huge vmap.

This patchset adds a Kconfig item for using Svnapot in
"Platform type"->"SVNAPOT extension support". Its default value is on,
and people can set it off if they don't allow kernel to detect Svnapot
hardware support and leverage it.

Tested on:
  - qemu rv64 with "Svnapot support" off and svnapot=true.
  - qemu rv64 with "Svnapot support" on and svnapot=true.
  - qemu rv64 with "Svnapot support" off and svnapot=false.
  - qemu rv64 with "Svnapot support" on and svnapot=false.

* b4-shazam-merge:
  riscv: mm: support Svnapot in huge vmap
  riscv: mm: support Svnapot in hugetlb page
  riscv: mm: modify pte format for Svnapot

Link: https://lore.kernel.org/r/20230209131647.17245-1-panqinglin00@gmail.com
[Palmer: fix up the feature ordering in the merge]
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
1  2 
arch/riscv/Kconfig
arch/riscv/include/asm/hwcap.h
arch/riscv/include/asm/page.h
arch/riscv/include/asm/pgtable.h
arch/riscv/kernel/cpu.c
arch/riscv/kernel/cpufeature.c

diff --combined arch/riscv/Kconfig
@@@ -12,13 -12,13 +12,13 @@@ config 32BI
  
  config RISCV
        def_bool y
 -      select ARCH_CLOCKSOURCE_INIT
        select ARCH_ENABLE_HUGEPAGE_MIGRATION if HUGETLB_PAGE && MIGRATION
        select ARCH_ENABLE_SPLIT_PMD_PTLOCK if PGTABLE_LEVELS > 2
 +      select ARCH_ENABLE_THP_MIGRATION if TRANSPARENT_HUGEPAGE
        select ARCH_HAS_BINFMT_FLAT
        select ARCH_HAS_CURRENT_STACK_POINTER
 -      select ARCH_HAS_DEBUG_VM_PGTABLE
        select ARCH_HAS_DEBUG_VIRTUAL if MMU
 +      select ARCH_HAS_DEBUG_VM_PGTABLE
        select ARCH_HAS_DEBUG_WX
        select ARCH_HAS_FORTIFY_SOURCE
        select ARCH_HAS_GCOV_PROFILE_ALL
        select ARCH_USE_QUEUED_RWLOCKS
        select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT if MMU
        select ARCH_WANT_FRAME_POINTERS
-       select ARCH_WANT_GENERAL_HUGETLB
+       select ARCH_WANT_GENERAL_HUGETLB if !RISCV_ISA_SVNAPOT
 +      select ARCH_WANT_HUGETLB_PAGE_OPTIMIZE_VMEMMAP
        select ARCH_WANT_HUGE_PMD_SHARE if 64BIT
 +      select ARCH_WANT_LD_ORPHAN_WARN if !XIP_KERNEL
        select ARCH_WANTS_THP_SWAP if HAVE_ARCH_TRANSPARENT_HUGEPAGE
        select BINFMT_FLAT_NO_DATA_START_OFFSET if !MMU
        select BUILDTIME_TABLE_SORT if MMU
 -      select CLONE_BACKWARDS
        select CLINT_TIMER if !MMU
 +      select CLONE_BACKWARDS
        select COMMON_CLK
        select CPU_PM if CPU_IDLE
        select EDAC_SUPPORT
        select HAVE_ARCH_MMAP_RND_BITS if MMU
        select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
        select HAVE_ARCH_SECCOMP_FILTER
 +      select HAVE_ARCH_THREAD_STRUCT_WHITELIST
        select HAVE_ARCH_TRACEHOOK
        select HAVE_ARCH_TRANSPARENT_HUGEPAGE if 64BIT && MMU
 -      select ARCH_ENABLE_THP_MIGRATION if TRANSPARENT_HUGEPAGE
 -      select HAVE_ARCH_THREAD_STRUCT_WHITELIST
        select HAVE_ARCH_VMAP_STACK if MMU && 64BIT
        select HAVE_ASM_MODVERSIONS
        select HAVE_CONTEXT_TRACKING_USER
        select HAVE_DEBUG_KMEMLEAK
        select HAVE_DMA_CONTIGUOUS if MMU
        select HAVE_EBPF_JIT if MMU
 +      select HAVE_FUNCTION_ARG_ACCESS_API
        select HAVE_FUNCTION_ERROR_INJECTION
        select HAVE_GCC_PLUGINS
        select HAVE_GENERIC_VDSO if MMU && 64BIT
        select HAVE_PERF_USER_STACK_DUMP
        select HAVE_POSIX_CPU_TIMERS_TASK_WORK
        select HAVE_REGS_AND_STACK_ACCESS_API
 -      select HAVE_FUNCTION_ARG_ACCESS_API
 +      select HAVE_RSEQ
        select HAVE_STACKPROTECTOR
        select HAVE_SYSCALL_TRACEPOINTS
 -      select HAVE_RSEQ
        select IRQ_DOMAIN
        select IRQ_FORCED_THREADING
        select MODULES_USE_ELF_RELA if MODULES
        select HAVE_DYNAMIC_FTRACE_WITH_REGS if HAVE_DYNAMIC_FTRACE
        select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL
        select HAVE_FUNCTION_GRAPH_TRACER
 -      select HAVE_FUNCTION_TRACER if !XIP_KERNEL
 +      select HAVE_FUNCTION_TRACER if !XIP_KERNEL && !PREEMPTION
  
  config ARCH_MMAP_RND_BITS_MIN
        default 18 if 64BIT
@@@ -236,9 -235,9 +236,9 @@@ config LOCKDEP_SUPPOR
  config RISCV_DMA_NONCOHERENT
        bool
        select ARCH_HAS_DMA_PREP_COHERENT
 -      select ARCH_HAS_SYNC_DMA_FOR_DEVICE
 -      select ARCH_HAS_SYNC_DMA_FOR_CPU
        select ARCH_HAS_SETUP_DMA_OPS
 +      select ARCH_HAS_SYNC_DMA_FOR_CPU
 +      select ARCH_HAS_SYNC_DMA_FOR_DEVICE
        select DMA_DIRECT_REMAP
  
  config AS_HAS_INSN
@@@ -353,11 -352,11 +353,11 @@@ endchoic
  config NUMA
        bool "NUMA Memory Allocation and Scheduler Support"
        depends on SMP && MMU
 +      select ARCH_SUPPORTS_NUMA_BALANCING
        select GENERIC_ARCH_NUMA
 +      select NEED_PER_CPU_EMBED_FIRST_CHUNK
        select OF_NUMA
 -      select ARCH_SUPPORTS_NUMA_BALANCING
        select USE_PERCPU_NUMA_NODE_ID
 -      select NEED_PER_CPU_EMBED_FIRST_CHUNK
        help
          Enable NUMA (Non-Uniform Memory Access) support.
  
@@@ -398,12 -397,31 +398,31 @@@ config RISCV_ISA_
  
          If you don't know what to do here, say Y.
  
+ config RISCV_ISA_SVNAPOT
+       bool "SVNAPOT extension support"
+       depends on 64BIT && MMU
+       default y
+       select RISCV_ALTERNATIVE
+       help
+         Allow kernel to detect the SVNAPOT ISA-extension dynamically at boot
+         time and enable its usage.
+         The SVNAPOT extension is used to mark contiguous PTEs as a range
+         of contiguous virtual-to-physical translations for a naturally
+         aligned power-of-2 (NAPOT) granularity larger than the base 4KB page
+         size. When HUGETLBFS is also selected this option unconditionally
+         allocates some memory for each NAPOT page size supported by the kernel.
+         When optimizing for low memory consumption and for platforms without
+         the SVNAPOT extension, it may be better to say N here.
+         If you don't know what to do here, say Y.
  config RISCV_ISA_SVPBMT
        bool "SVPBMT extension support"
        depends on 64BIT && MMU
        depends on !XIP_KERNEL
 -      select RISCV_ALTERNATIVE
        default y
 +      select RISCV_ALTERNATIVE
        help
           Adds support to dynamically detect the presence of the SVPBMT
           ISA-extension (Supervisor-mode: page-based memory types) and
@@@ -441,12 -459,20 +460,12 @@@ config RISCV_ISA_ZB
  
           If you don't know what to do here, say Y.
  
 -config TOOLCHAIN_HAS_ZICBOM
 -      bool
 -      default y
 -      depends on !64BIT || $(cc-option,-mabi=lp64 -march=rv64ima_zicbom)
 -      depends on !32BIT || $(cc-option,-mabi=ilp32 -march=rv32ima_zicbom)
 -      depends on LLD_VERSION >= 150000 || LD_VERSION >= 23800
 -
  config RISCV_ISA_ZICBOM
        bool "Zicbom extension support for non-coherent DMA operation"
 -      depends on TOOLCHAIN_HAS_ZICBOM
        depends on !XIP_KERNEL && MMU
 -      select RISCV_DMA_NONCOHERENT
 -      select RISCV_ALTERNATIVE
        default y
 +      select RISCV_ALTERNATIVE
 +      select RISCV_DMA_NONCOHERENT
        help
           Adds support to dynamically detect the presence of the ZICBOM
           extension (Cache Block Management Operations) and enable its
@@@ -508,9 -534,9 +527,9 @@@ config RISCV_BOOT_SPINWAI
  
  config KEXEC
        bool "Kexec system call"
 -      select KEXEC_CORE
 -      select HOTPLUG_CPU if SMP
        depends on MMU
 +      select HOTPLUG_CPU if SMP
 +      select KEXEC_CORE
        help
          kexec is a system call that implements the ability to shutdown your
          current kernel, and to start another kernel. It is like a reboot
  
  config KEXEC_FILE
        bool "kexec file based systmem call"
 +      depends on 64BIT && MMU
 +      select HAVE_IMA_KEXEC if IMA
        select KEXEC_CORE
        select KEXEC_ELF
 -      select HAVE_IMA_KEXEC if IMA
 -      depends on 64BIT && MMU
        help
          This is new version of kexec system call. This system call is
          file based and takes file descriptors as system call argument
@@@ -613,15 -639,15 +632,15 @@@ config EFI_STU
  config EFI
        bool "UEFI runtime support"
        depends on OF && !XIP_KERNEL
 -      select LIBFDT
 -      select UCS2_STRING
 -      select EFI_PARAMS_FROM_FDT
 -      select EFI_STUB
 +      depends on MMU
 +      default y
        select EFI_GENERIC_STUB
 +      select EFI_PARAMS_FROM_FDT
        select EFI_RUNTIME_WRAPPERS
 +      select EFI_STUB
 +      select LIBFDT
        select RISCV_ISA_C
 -      depends on MMU
 -      default y
 +      select UCS2_STRING
        help
          This option provides support for runtime services provided
          by UEFI firmware (such as non-volatile variables, realtime
@@@ -700,8 -726,8 +719,8 @@@ config PORTABL
        bool
        default !NONPORTABLE
        select EFI
 -      select OF
        select MMU
 +      select OF
  
  menu "Power management options"
  
  #define RISCV_ISA_EXT_u               ('u' - 'a')
  
  /*
 - * Increse this to higher value as kernel support more ISA extensions.
 + * These macros represent the logical IDs of each multi-letter RISC-V ISA
 + * extension and are used in the ISA bitmap. The logical IDs start from
 + * RISCV_ISA_EXT_BASE, which allows the 0-25 range to be reserved for single
 + * letter extensions. The maximum, RISCV_ISA_EXT_MAX, is defined in order
 + * to allocate the bitmap and may be increased when necessary.
 + *
 + * New extensions should just be added to the bottom, rather than added
 + * alphabetically, in order to avoid unnecessary shuffling.
   */
 -#define RISCV_ISA_EXT_MAX     64
 -#define RISCV_ISA_EXT_NAME_LEN_MAX 32
 +#define RISCV_ISA_EXT_BASE            26
  
 -/* The base ID for multi-letter ISA extensions */
 -#define RISCV_ISA_EXT_BASE 26
 +#define RISCV_ISA_EXT_SSCOFPMF                26
 +#define RISCV_ISA_EXT_SSTC            27
 +#define RISCV_ISA_EXT_SVINVAL         28
 +#define RISCV_ISA_EXT_SVPBMT          29
 +#define RISCV_ISA_EXT_ZBB             30
 +#define RISCV_ISA_EXT_ZICBOM          31
 +#define RISCV_ISA_EXT_ZIHINTPAUSE     32
++#define RISCV_ISA_EXT_SVNAPOT         33
  
 -/*
 - * These macros represent the logical ID for each multi-letter RISC-V ISA extension.
 - * The logical ID should start from RISCV_ISA_EXT_BASE and must not exceed
 - * RISCV_ISA_EXT_MAX. 0-25 range is reserved for single letter
 - * extensions while all the multi-letter extensions should define the next
 - * available logical extension id.
 - * Entries are sorted alphabetically.
 - */
 -#define RISCV_ISA_EXT_SSCOFPMF         26
 -#define RISCV_ISA_EXT_SSTC             27
 -#define RISCV_ISA_EXT_SVINVAL          28
 -#define RISCV_ISA_EXT_SVNAPOT          29
 -#define RISCV_ISA_EXT_SVPBMT           30
 -#define RISCV_ISA_EXT_ZBB              31
 -#define RISCV_ISA_EXT_ZICBOM           32
 -#define RISCV_ISA_EXT_ZIHINTPAUSE      33
 +#define RISCV_ISA_EXT_MAX             64
 +#define RISCV_ISA_EXT_NAME_LEN_MAX    32
  
  #ifndef __ASSEMBLY__
  
  #include <linux/jump_label.h>
  
 -/*
 - * This yields a mask that user programs can use to figure out what
 - * instruction set this cpu supports.
 - */
 -#define ELF_HWCAP             (elf_hwcap)
 -
 -enum {
 -      CAP_HWCAP = 1,
 -};
 -
 -extern unsigned long elf_hwcap;
 -
  struct riscv_isa_ext_data {
        /* Name of the extension displayed to userspace via /proc/cpuinfo */
        char uprop[RISCV_ISA_EXT_NAME_LEN_MAX];
  #define PAGE_SIZE     (_AC(1, UL) << PAGE_SHIFT)
  #define PAGE_MASK     (~(PAGE_SIZE - 1))
  
- #ifdef CONFIG_64BIT
- #define HUGE_MAX_HSTATE               2
- #else
- #define HUGE_MAX_HSTATE               1
- #endif
  #define HPAGE_SHIFT           PMD_SHIFT
  #define HPAGE_SIZE            (_AC(1, UL) << HPAGE_SHIFT)
  #define HPAGE_MASK              (~(HPAGE_SIZE - 1))
@@@ -171,6 -166,11 +166,6 @@@ extern phys_addr_t __phys_addr_symbol(u
  
  #define sym_to_pfn(x)           __phys_to_pfn(__pa_symbol(x))
  
 -#ifdef CONFIG_FLATMEM
 -#define pfn_valid(pfn) \
 -      (((pfn) >= ARCH_PFN_OFFSET) && (((pfn) - ARCH_PFN_OFFSET) < max_mapnr))
 -#endif
 -
  #endif /* __ASSEMBLY__ */
  
  #define virt_addr_valid(vaddr)        ({                                              \
@@@ -264,10 -264,47 +264,47 @@@ static inline pte_t pud_pte(pud_t pud
        return __pte(pud_val(pud));
  }
  
+ #ifdef CONFIG_RISCV_ISA_SVNAPOT
+ static __always_inline bool has_svnapot(void)
+ {
+       return riscv_has_extension_likely(RISCV_ISA_EXT_SVNAPOT);
+ }
+ static inline unsigned long pte_napot(pte_t pte)
+ {
+       return pte_val(pte) & _PAGE_NAPOT;
+ }
+ static inline pte_t pte_mknapot(pte_t pte, unsigned int order)
+ {
+       int pos = order - 1 + _PAGE_PFN_SHIFT;
+       unsigned long napot_bit = BIT(pos);
+       unsigned long napot_mask = ~GENMASK(pos, _PAGE_PFN_SHIFT);
+       return __pte((pte_val(pte) & napot_mask) | napot_bit | _PAGE_NAPOT);
+ }
+ #else
+ static __always_inline bool has_svnapot(void) { return false; }
+ static inline unsigned long pte_napot(pte_t pte)
+ {
+       return 0;
+ }
+ #endif /* CONFIG_RISCV_ISA_SVNAPOT */
  /* Yields the page frame number (PFN) of a page table entry */
  static inline unsigned long pte_pfn(pte_t pte)
  {
-       return __page_val_to_pfn(pte_val(pte));
+       unsigned long res  = __page_val_to_pfn(pte_val(pte));
+       if (has_svnapot() && pte_napot(pte))
+               res = res & (res - 1UL);
+       return res;
  }
  
  #define pte_page(x)     pfn_to_page(pte_pfn(x))
@@@ -415,7 -452,7 +452,7 @@@ static inline void update_mmu_cache(str
         * Relying on flush_tlb_fix_spurious_fault would suffice, but
         * the extra traps reduce performance.  So, eagerly SFENCE.VMA.
         */
 -      flush_tlb_page(vma, address);
 +      local_flush_tlb_page(address);
  }
  
  #define __HAVE_ARCH_UPDATE_MMU_TLB
@@@ -721,25 -758,19 +758,25 @@@ static inline pmd_t pmdp_establish(stru
        page_table_check_pmd_set(vma->vm_mm, address, pmdp, pmd);
        return __pmd(atomic_long_xchg((atomic_long_t *)pmdp, pmd_val(pmd)));
  }
 +
 +#define pmdp_collapse_flush pmdp_collapse_flush
 +extern pmd_t pmdp_collapse_flush(struct vm_area_struct *vma,
 +                               unsigned long address, pmd_t *pmdp);
  #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
  
  /*
 - * Encode and decode a swap entry
 + * Encode/decode swap entries and swap PTEs. Swap PTEs are all PTEs that
 + * are !pte_none() && !pte_present().
   *
   * Format of swap PTE:
   *    bit            0:       _PAGE_PRESENT (zero)
   *    bit       1 to 3:       _PAGE_LEAF (zero)
   *    bit            5:       _PAGE_PROT_NONE (zero)
 - *    bits      6 to 10:      swap type
 - *    bits 10 to XLEN-1:      swap offset
 + *    bit            6:       exclusive marker
 + *    bits      7 to 11:      swap type
 + *    bits 11 to XLEN-1:      swap offset
   */
 -#define __SWP_TYPE_SHIFT      6
 +#define __SWP_TYPE_SHIFT      7
  #define __SWP_TYPE_BITS               5
  #define __SWP_TYPE_MASK               ((1UL << __SWP_TYPE_BITS) - 1)
  #define __SWP_OFFSET_SHIFT    (__SWP_TYPE_BITS + __SWP_TYPE_SHIFT)
  #define __swp_type(x) (((x).val >> __SWP_TYPE_SHIFT) & __SWP_TYPE_MASK)
  #define __swp_offset(x)       ((x).val >> __SWP_OFFSET_SHIFT)
  #define __swp_entry(type, offset) ((swp_entry_t) \
 -      { ((type) << __SWP_TYPE_SHIFT) | ((offset) << __SWP_OFFSET_SHIFT) })
 +      { (((type) & __SWP_TYPE_MASK) << __SWP_TYPE_SHIFT) | \
 +        ((offset) << __SWP_OFFSET_SHIFT) })
  
  #define __pte_to_swp_entry(pte)       ((swp_entry_t) { pte_val(pte) })
  #define __swp_entry_to_pte(x) ((pte_t) { (x).val })
  
 +static inline int pte_swp_exclusive(pte_t pte)
 +{
 +      return pte_val(pte) & _PAGE_SWP_EXCLUSIVE;
 +}
 +
 +static inline pte_t pte_swp_mkexclusive(pte_t pte)
 +{
 +      return __pte(pte_val(pte) | _PAGE_SWP_EXCLUSIVE);
 +}
 +
 +static inline pte_t pte_swp_clear_exclusive(pte_t pte)
 +{
 +      return __pte(pte_val(pte) & ~_PAGE_SWP_EXCLUSIVE);
 +}
 +
  #ifdef CONFIG_ARCH_ENABLE_THP_MIGRATION
  #define __pmd_to_swp_entry(pmd) ((swp_entry_t) { pmd_val(pmd) })
  #define __swp_entry_to_pmd(swp) __pmd((swp).val)
diff --combined arch/riscv/kernel/cpu.c
@@@ -185,12 -185,13 +185,13 @@@ arch_initcall(riscv_cpuinfo_init)
   * New entries to this struct should follow the ordering rules described above.
   */
  static struct riscv_isa_ext_data isa_ext_arr[] = {
 -      __RISCV_ISA_EXT_DATA(zbb, RISCV_ISA_EXT_ZBB),
        __RISCV_ISA_EXT_DATA(zicbom, RISCV_ISA_EXT_ZICBOM),
        __RISCV_ISA_EXT_DATA(zihintpause, RISCV_ISA_EXT_ZIHINTPAUSE),
 +      __RISCV_ISA_EXT_DATA(zbb, RISCV_ISA_EXT_ZBB),
        __RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF),
        __RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC),
        __RISCV_ISA_EXT_DATA(svinval, RISCV_ISA_EXT_SVINVAL),
+       __RISCV_ISA_EXT_DATA(svnapot, RISCV_ISA_EXT_SVNAPOT),
        __RISCV_ISA_EXT_DATA(svpbmt, RISCV_ISA_EXT_SVPBMT),
        __RISCV_ISA_EXT_DATA("", RISCV_ISA_EXT_MAX),
  };
@@@ -10,7 -10,6 +10,7 @@@
  #include <linux/ctype.h>
  #include <linux/libfdt.h>
  #include <linux/log2.h>
 +#include <linux/memory.h>
  #include <linux/module.h>
  #include <linux/of.h>
  #include <asm/alternative.h>
@@@ -224,6 -223,7 +224,7 @@@ void __init riscv_fill_hwcap(void
                                SET_ISA_EXT_MAP("sscofpmf", RISCV_ISA_EXT_SSCOFPMF);
                                SET_ISA_EXT_MAP("sstc", RISCV_ISA_EXT_SSTC);
                                SET_ISA_EXT_MAP("svinval", RISCV_ISA_EXT_SVINVAL);
+                               SET_ISA_EXT_MAP("svnapot", RISCV_ISA_EXT_SVNAPOT);
                                SET_ISA_EXT_MAP("svpbmt", RISCV_ISA_EXT_SVPBMT);
                                SET_ISA_EXT_MAP("zbb", RISCV_ISA_EXT_ZBB);
                                SET_ISA_EXT_MAP("zicbom", RISCV_ISA_EXT_ZICBOM);
@@@ -293,11 -293,8 +294,11 @@@ void __init_or_module riscv_cpufeature_
  
                oldptr = ALT_OLD_PTR(alt);
                altptr = ALT_ALT_PTR(alt);
 +
 +              mutex_lock(&text_mutex);
                patch_text_nosync(oldptr, altptr, alt->alt_len);
                riscv_alternative_fix_offsets(oldptr, alt->alt_len, oldptr - altptr);
 +              mutex_unlock(&text_mutex);
        }
  }
  #endif