diff --git a/arch/x86/include/asm/page_64.h b/arch/x86/include/asm/page_64.h index 939b1cff4a7b7fe259df579f2742c067600afa4d..9b02293ac61799a994e9b060e75f14cce0c24372 100644 --- a/arch/x86/include/asm/page_64.h +++ b/arch/x86/include/asm/page_64.h @@ -56,6 +56,16 @@ static inline void clear_page(void *page) void copy_page(void *to, void *from); +void copy_page_nocache(void *to, void *from); +void copy_page_nocache_barrir(void); + +struct page; +struct ctl_table; +# define __HAVE_ARCH_COPY_HUGEPAGES 1 +void copy_highpages(struct page *to, struct page *from, int nr_pages); +int sysctl_hugepage_nocache_copy(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos); + #endif /* !__ASSEMBLY__ */ #ifdef CONFIG_X86_VSYSCALL_EMULATION diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index bad4dee4f0e4274d00c60145db3ee01a010847d2..ab0d91b808be17bd33e48c06781332687d0d1a7a 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -70,4 +70,5 @@ else lib-y += memmove_64.o memset_64.o lib-y += copy_user_64.o lib-y += cmpxchg16b_emu.o + lib-y += copy_highpages.o endif diff --git a/arch/x86/lib/copy_highpages.c b/arch/x86/lib/copy_highpages.c new file mode 100644 index 0000000000000000000000000000000000000000..74a94703f09b68156fcfb2cc21fc3eee7a8928e4 --- /dev/null +++ b/arch/x86/lib/copy_highpages.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * accelerate copying page to pmem with non-temproal stroes + */ +#include +#include +#include +#include + +DEFINE_STATIC_KEY_FALSE(hugepage_nocache_copy); + +static void set_hugepage_nocache_copy(bool enabled) +{ + if (enabled) + static_branch_enable(&hugepage_nocache_copy); + else + static_branch_disable(&hugepage_nocache_copy); +} + +int sysctl_hugepage_nocache_copy(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + struct ctl_table t; + int err; + int state = static_branch_unlikely(&hugepage_nocache_copy); + + if (write && !capable(CAP_SYS_ADMIN)) + return -EPERM; + + t = *table; + t.data = &state; + err = proc_dointvec_minmax(&t, write, buffer, lenp, ppos); + if (err < 0) + return err; + if (write) + set_hugepage_nocache_copy(state); + return err; +} + +static void copy_highpages_nocache(struct page *to, struct page *from, int nr_pages) +{ + char *vfrom, *vto; + int i; + + for (i = 0; i < nr_pages; i++) { + cond_resched(); + vfrom = kmap_atomic(from); + vto = kmap_atomic(to); + copy_page_nocache(vto, vfrom); + kunmap_atomic(vto); + kunmap_atomic(vfrom); + to++; + from++; + } + copy_page_nocache_barrir(); +} + +static void copy_highpages_cache(struct page *to, struct page *from, int nr_pages) +{ + int i; + + for (i = 0; i < nr_pages; i++) { + cond_resched(); + copy_highpage(to + i, from + i); + } +} + +void copy_highpages(struct page *to, struct page *from, int nr_pages) +{ + if (static_branch_unlikely(&hugepage_nocache_copy) && is_node_pmem(page_to_nid(to))) + return copy_highpages_nocache(to, from, nr_pages); + + return copy_highpages_cache(to, from, nr_pages); +} diff --git a/arch/x86/lib/copy_page_64.S b/arch/x86/lib/copy_page_64.S index 2402d4c489d298cf05e368ef4cb40d457ed5e574..16ca196d349bec8b473a7670f82ae3da6ae02235 100644 --- a/arch/x86/lib/copy_page_64.S +++ b/arch/x86/lib/copy_page_64.S @@ -87,3 +87,76 @@ SYM_FUNC_START_LOCAL(copy_page_regs) addq $2*8, %rsp ret SYM_FUNC_END(copy_page_regs) + +SYM_FUNC_START(copy_page_nocache) + ALTERNATIVE "jmp copy_page", "", X86_FEATURE_XMM2 + subq $2*8, %rsp + movq %rbx, (%rsp) + movq %r12, 1*8(%rsp) + + movl $(4096/64)-5, %ecx + .p2align 4 +.LoopNT64: + dec %rcx + movq 0x8*0(%rsi), %rax + movq 0x8*1(%rsi), %rbx + movq 0x8*2(%rsi), %rdx + movq 0x8*3(%rsi), %r8 + movq 0x8*4(%rsi), %r9 + movq 0x8*5(%rsi), %r10 + movq 0x8*6(%rsi), %r11 + movq 0x8*7(%rsi), %r12 + + prefetcht0 5*64(%rsi) + + movnti %rax, 0x8*0(%rdi) + movnti %rbx, 0x8*1(%rdi) + movnti %rdx, 0x8*2(%rdi) + movnti %r8, 0x8*3(%rdi) + movnti %r9, 0x8*4(%rdi) + movnti %r10, 0x8*5(%rdi) + movnti %r11, 0x8*6(%rdi) + movnti %r12, 0x8*7(%rdi) + + leaq 64 (%rsi), %rsi + leaq 64 (%rdi), %rdi + + jnz .LoopNT64 + + movl $5, %ecx + .p2align 4 +.LoopNT2: + decl %ecx + + movq 0x8*0(%rsi), %rax + movq 0x8*1(%rsi), %rbx + movq 0x8*2(%rsi), %rdx + movq 0x8*3(%rsi), %r8 + movq 0x8*4(%rsi), %r9 + movq 0x8*5(%rsi), %r10 + movq 0x8*6(%rsi), %r11 + movq 0x8*7(%rsi), %r12 + + movnti %rax, 0x8*0(%rdi) + movnti %rbx, 0x8*1(%rdi) + movnti %rdx, 0x8*2(%rdi) + movnti %r8, 0x8*3(%rdi) + movnti %r9, 0x8*4(%rdi) + movnti %r10, 0x8*5(%rdi) + movnti %r11, 0x8*6(%rdi) + movnti %r12, 0x8*7(%rdi) + + leaq 64(%rdi), %rdi + leaq 64(%rsi), %rsi + jnz .LoopNT2 + + movq (%rsp), %rbx + movq 1*8(%rsp), %r12 + addq $2*8, %rsp + ret +SYM_FUNC_END(copy_page_nocache) + +SYM_FUNC_START(copy_page_nocache_barrir) + ALTERNATIVE "", "sfence", X86_FEATURE_XMM2 + ret +SYM_FUNC_END(copy_page_nocache_barrir) diff --git a/include/linux/highmem.h b/include/linux/highmem.h index 14e6202ce47f1395b61a45dc2d7455723dfdc0aa..db8fc7a9631e371799c5da82ee515b7a261acbc9 100644 --- a/include/linux/highmem.h +++ b/include/linux/highmem.h @@ -345,4 +345,18 @@ static inline void copy_highpage(struct page *to, struct page *from) #endif +#ifndef __HAVE_ARCH_COPY_HUGEPAGES + +static inline void copy_highpages(struct page *to, struct page *from, int nr_pages) +{ + int i; + + for (i = 0; i < nr_pages; i++) { + cond_resched(); + copy_highpage(to + i, from + i); + } +} + +#endif /* __HAVE_ARCH_COPY_HUGEPAGES */ + #endif /* _LINUX_HIGHMEM_H */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 5aeb9b26aaa0da613dea7c3f0f1056a3e4fae78f..4fbc106aa195cba3cbd2b89aadb6ebbded69a9a4 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -3147,6 +3147,17 @@ static struct ctl_table vm_table[] = { .extra1 = SYSCTL_ZERO, .extra2 = SYSCTL_ONE, }, +#if defined(CONFIG_X86_64) + { + .procname = "hugepage_nocache_copy", + .data = NULL, /* filled in by handler */ + .maxlen = sizeof(unsigned int), + .mode = 0600, + .proc_handler = sysctl_hugepage_nocache_copy, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }, +#endif { } }; diff --git a/mm/migrate.c b/mm/migrate.c index 7982256a51250f290e9959e2837d05324832dce1..465a227d13e764d2adcc464cb757b963101f64f1 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -577,7 +577,6 @@ static void __copy_gigantic_page(struct page *dst, struct page *src, static void copy_huge_page(struct page *dst, struct page *src) { - int i; int nr_pages; if (PageHuge(src)) { @@ -595,10 +594,7 @@ static void copy_huge_page(struct page *dst, struct page *src) nr_pages = thp_nr_pages(src); } - for (i = 0; i < nr_pages; i++) { - cond_resched(); - copy_highpage(dst + i, src + i); - } + copy_highpages(dst, src, nr_pages); } /*