From: Barry Song <v-songbaohua@xxxxxxxx> The patch has been extracted from the larger folios swap-in series [1], incorporating some new modifications. Introducing THP_SWAP support for ARM64 SoCs with MTE is essential, particularly due to its significance for widely used ARM64 products in the market. Without this support, Ryan's mTHP swap-out without splitting series won't operate effectively on these SoCs. Therefore, it's imperative for this update to be implemented sooner rather than later. There are a couple of differences with the code in [1]: 1. minor code cleanup, Ryan 2. always pass the first swap entry of a folio to arch_swap_restore, Ryan [1] https://lore.kernel.org/linux-mm/20240304081348.197341-2-21cnbao@xxxxxxxxx/ Barry Song (1): arm64: mm: swap: support THP_SWAP on hardware with MTE arch/arm64/include/asm/pgtable.h | 19 ++------------ arch/arm64/mm/mteswap.c | 45 ++++++++++++++++++++++++++++++++ include/linux/huge_mm.h | 12 --------- include/linux/pgtable.h | 2 +- mm/internal.h | 14 ++++++++++ mm/memory.c | 2 +- mm/page_io.c | 2 +- mm/shmem.c | 2 +- mm/swap_slots.c | 2 +- mm/swapfile.c | 2 +- 10 files changed, 67 insertions(+), 35 deletions(-) Appendix I also have a small test program specifically designed for running MTE on a THP that I can share with those who are interested in this subject. /* * To be compiled with -march=armv8.5-a+memtag */ #include <errno.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/auxv.h> #include <sys/mman.h> #include <sys/prctl.h> /* * From arch/arm64/include/uapi/asm/hwcap.h */ #define HWCAP2_MTE (1 << 18) /* * From arch/arm64/include/uapi/asm/mman.h */ #define PROT_MTE 0x20 /* * From include/uapi/linux/prctl.h */ #define PR_SET_TAGGED_ADDR_CTRL 55 #define PR_GET_TAGGED_ADDR_CTRL 56 # define PR_TAGGED_ADDR_ENABLE (1UL << 0) # define PR_MTE_TCF_SHIFT 1 # define PR_MTE_TCF_NONE (0UL << PR_MTE_TCF_SHIFT) # define PR_MTE_TCF_SYNC (1UL << PR_MTE_TCF_SHIFT) # define PR_MTE_TCF_ASYNC (2UL << PR_MTE_TCF_SHIFT) # define PR_MTE_TCF_MASK (3UL << PR_MTE_TCF_SHIFT) # define PR_MTE_TAG_SHIFT 3 # define PR_MTE_TAG_MASK (0xffffUL << PR_MTE_TAG_SHIFT) /* * Insert a random logical tag into the given pointer. */ #define insert_random_tag(ptr) ({ \ uint64_t __val; \ asm("irg %0, %1" : "=r" (__val) : "r" (ptr)); \ __val; \ }) /* * Set the allocation tag on the destination address. */ #define set_tag(tagged_addr) do { \ asm volatile("stg %0, [%0]" : : "r" (tagged_addr) : "memory"); \ } while (0) int main() { unsigned char *a, *p[512]; unsigned long page_sz = 4 * 1024UL; unsigned long mem_sz = 2 * 1024 * 1024UL; unsigned long hwcap2 = getauxval(AT_HWCAP2); int i; if (!(hwcap2 & HWCAP2_MTE)) return EXIT_FAILURE; if (prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC | (0xfffe << PR_MTE_TAG_SHIFT), 0, 0, 0)) { perror("prctl() failed"); return EXIT_FAILURE; } a = mmap(0, mem_sz * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (a == MAP_FAILED) { perror("mmap() failed"); return EXIT_FAILURE; } /* make sure a is aligned with 2MiB THP */ a = (unsigned char *)(((unsigned long)a + mem_sz - 1) & ~(mem_sz - 1)); madvise(a, mem_sz, MADV_HUGEPAGE); memset(a, 0x11, mem_sz); if (mprotect(a, mem_sz, PROT_READ | PROT_WRITE | PROT_MTE)) { perror("mprotect() failed"); return EXIT_FAILURE; } printf("set tag for each 4KiB page\n"); for (i = 0; i < 512; i++) { p[i] = a + i * page_sz; p[i] = (unsigned char *)insert_random_tag(p[i]); set_tag(p[i]); p[i][0] = 0x33; } printf("swap-out the whole THP\n"); madvise(a, mem_sz, MADV_PAGEOUT); printf("swap-in each page of the original THP\n"); for (i = 0; i < 512; i++) { if (p[i][0] != 0x33) { printf("test fails, unmatched value after swap-in\n"); return EXIT_FAILURE; } } printf("we should get here\n"); for (i = 0; i < 512; i++) { printf("page :%d val: expect segment fault, is %02x\n", i, p[i][16]); } printf("we shouldn't get here\n"); return EXIT_FAILURE; } -- 2.34.1