copy_page improvements
cover copy_page improvements
Hi all,
This patch series aims to improve our copy_page implementation on both CPUs with and without hardware prefetchers. It is inspired by a previous patch series from Andrew, and one of the patches is lifted directly from there:
http://lists.infradead.org/pipermail/linux-arm-kernel/2016-January/399132.html
Changes from that series:
* Applies on top of mainline
* Inverted the capability to the more generic ARM64_HAS_NO_HW_PREFETCH
* Re-jigged the MIDR detection to try to keep it simple
I've left copy_template alone for now, since the template really deals with 64 bytes per iteration, which would need changing.
Andrew -- it would be helpful if you could take this for a spin on ThunderX to make sure it didn't screw anything up.
Cheers,
Will
--->8
Andrew Pinski (1):
arm64: lib: patch in prfm for copy_page if requested
Will Deacon (3):
arm64: prefetch: don't provide spin_lock_prefetch with LSE
arm64: prefetch: add alternative pattern for CPUs without a prefetcher
arm64: lib: improve copy_page to deal with 128 bytes at a time
arch/arm64/include/asm/cpufeature.h | 3 +- arch/arm64/include/asm/cputype.h | 17 +++++++++- arch/arm64/include/asm/processor.h | 7 +++-- arch/arm64/kernel/cpu_errata.c | 18 ++--------- arch/arm64/kernel/cpufeature.c | 17 ++++++++++ arch/arm64/lib/copy_page.S | 63 ++++++++++++++++++++++++++++++++----- 6 files changed, 98 insertions(+), 27 deletions(-)
Signature · 1 line
2.1.4
PATCH 1/4 arm64: prefetch: don't provide spin_lock_prefetch with LSE
The LSE atomics rely on us not dirtying data at L1 if we can avoid it, otherwise many of the potential scalability benefits are lost.
This patch replaces spin_lock_prefetch with a nop when the LSE atomics are in use, so that users don't shoot themselves in the foot by causing needless coherence traffic at L1.
--- arch/arm64/include/asm/processor.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
arch/arm64/include/asm/processor.h +5 -2
@@ -29,6 +29,7 @@#include <linux/string.h> +#include <asm/alternative.h> #include <asm/fpsimd.h> #include <asm/hw_breakpoint.h> #include <asm/pgtable-hwdef.h>
@@ -177,9 +178,11 @@ static inline void prefetchw(const void *ptr) }#define ARCH_HAS_SPINLOCK_PREFETCH -static inline void spin_lock_prefetch (const void *x) +static inline void spin_lock_prefetch (const void *ptr) { -prefetchw (x); + asm volatile(ARM64_LSE_ATOMIC_INSN( + "prfm pstl1strm, %a0", + "nop") : : "p" (ptr)); }#define HAVE_ARCH_PICK_MMAP_LAYOUT
Signature · 1 line
2.1.4
PATCH 2/4 arm64: prefetch: add alternative pattern for CPUs without a prefetcher
In reply to Will DeaconMost CPUs have a hardware prefetcher which generally performs better without explicit prefetch instructions issued by software, however some CPUs (e.g. Cavium ThunderX) rely solely on explicit prefetch instructions.
This patch adds an alternative pattern (ARM64_HAS_NO_HW_PREFETCH) to allow our library code to make use of explicit prefetch instructions during things like copy routines only when the CPU does not have the capability to perform the prefetching itself.
--- arch/arm64/include/asm/cpufeature.h | 3 ++- arch/arm64/include/asm/cputype.h | 17 ++++++++++++++++- arch/arm64/kernel/cpu_errata.c | 18 +++--------------- arch/arm64/kernel/cpufeature.c | 17 +++++++++++++++++ 4 files changed, 38 insertions(+), 17 deletions(-)
arch/arm64/include/asm/cpufeature.h +2 -1
@@ -30,8 +30,9 @@#define ARM64_HAS_LSE_ATOMICS 5#define ARM64_WORKAROUND_CAVIUM_23154 6#define ARM64_WORKAROUND_834220 7 +#define ARM64_HAS_NO_HW_PREFETCH 8 -#define ARM64_NCAPS 8 +#define ARM64_NCAPS 9#ifndef __ASSEMBLY__
arch/arm64/include/asm/cputype.h +16 -1
@@ -57,11 +57,22 @@#define MIDR_IMPLEMENTOR (midr) \ (((midr) & MIDR_IMPLEMENTOR_MASK) >> MIDR_IMPLEMENTOR_SHIFT) -#define MIDR_CPU_PART (imp, partnum) \ +#define MIDR_CPU_MODEL (imp, partnum) \ (((imp) << MIDR_IMPLEMENTOR_SHIFT) | \ (0xf << MIDR_ARCHITECTURE_SHIFT) | \ ((partnum) << MIDR_PARTNUM_SHIFT)) +#define MIDR_CPU_MODEL_MASK (MIDR_IMPLEMENTOR_MASK | MIDR_PARTNUM_MASK | \ + MIDR_ARCHITECTURE_MASK) + +#define MIDR_IS_CPU_MODEL_RANGE (midr, model, rv_min, rv_max) \ +({ \ + u32 _model = (midr) & MIDR_CPU_MODEL_MASK; \ + u32 rv = (midr) & (MIDR_REVISION_MASK | MIDR_VARIANT_MASK); \ + \ + _model == (model) && rv >= (rv_min) && rv <= (rv_max); \ + }) +#define ARM_CPU_IMP_ARM 0x41#define ARM_CPU_IMP_APM 0x50#define ARM_CPU_IMP_CAVIUM 0x43
@@ -75,6 +86,10 @@#define CAVIUM_CPU_PART_THUNDERX 0x0A1 +#define MIDR_CORTEX_A53 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A53) +#define MIDR_CORTEX_A57 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A57) +#define MIDR_THUNDERX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX) +#ifndef __ASSEMBLY__/*
arch/arm64/kernel/cpu_errata.c +3 -15
@@ -21,24 +21,12 @@#include <asm/cputype.h> #include <asm/cpufeature.h> -#define MIDR_CORTEX_A53 MIDR_CPU_PART(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A53) -#define MIDR_CORTEX_A57 MIDR_CPU_PART(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A57) -#define MIDR_THUNDERX MIDR_CPU_PART(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX) - -#define CPU_MODEL_MASK (MIDR_IMPLEMENTOR_MASK | MIDR_PARTNUM_MASK | \ - MIDR_ARCHITECTURE_MASK) -static bool __maybe_unused is_affected_midr_range (const struct arm64_cpu_capabilities *entry) { -u32 midr =read_cpuid_id (); - -if ((midr &CPU_MODEL_MASK ) != entry->midr_model) -return false; - - midr &=MIDR_REVISION_MASK |MIDR_VARIANT_MASK ; - -return (midr >= entry->midr_range_min && midr <= entry->midr_range_max); +return MIDR_IS_CPU_MODEL_RANGE (read_cpuid_id (), entry->midr_model, + entry->midr_range_min, + entry->midr_range_max); }#define MIDR_RANGE (model, min, max) \
arch/arm64/kernel/cpufeature.c +17 -0
@@ -621,6 +621,18 @@ static bool has_useable_gicv3_cpuif(const struct arm64_cpu_capabilities *entry)return has_sre; } +static bool has_no_hw_prefetch (const struct arm64_cpu_capabilities *entry) +{ +u32 midr =read_cpuid_id (); +u32 rv_min, rv_max; + +/* Cavium ThunderX pass 1.x and 2.x */ + rv_min =0 ; + rv_max = (1 <<MIDR_VARIANT_SHIFT ) |MIDR_REVISION_MASK ; + +return MIDR_IS_CPU_MODEL_RANGE (midr,MIDR_THUNDERX , rv_min, rv_max); +} +static const struct arm64_cpu_capabilities arm64_features[] = { { .desc ="GIC system register CPU interface" ,
@@ -651,6 +663,11 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .min_field_value = 2, },#endif /* CONFIG_AS_LSE && CONFIG_ARM64_LSE_ATOMICS */ + { + .desc = "Software prefetching using PRFM", + .capability = ARM64_HAS_NO_HW_PREFETCH, + .matches = has_no_hw_prefetch, + }, {}, };
Signature · 1 line
2.1.4
PATCH 3/4 arm64: lib: improve copy_page to deal with 128 bytes at a time
In reply to Will DeaconWe want to avoid lots of different copy_page implementations, settling for something that is "good enough" everywhere and hopefully easy to understand and maintain whilst we're at it.
This patch reworks our copy_page implementation based on discussions with Cavium on the list and benchmarking on Cortex-A processors so that:
- The loop is unrolled to copy 128 bytes per iteration
- The reads are offset so that we read from the next 128-byte block in the same iteration that we store the previous block
- Explicit prefetch instructions are removed for now, since they hurt performance on CPUs with hardware prefetching
- The loop exit condition is calculated at the start of the loop
--- arch/arm64/lib/copy_page.S | 46 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 8 deletions(-)
arch/arm64/lib/copy_page.S +38 -8
@@ -27,20 +27,50 @@ * x1 - src */ ENTRY(copy_page) - /* Assume cache line size is 64 bytes. */ - prfm pldl1strm, [x1, #64] -1: ldp x2, x3, [x1] + ldp x2, x3, [x1] ldp x4, x5, [x1, #16] ldp x6, x7, [x1, #32] ldp x8, x9, [x1, #48] - add x1, x1, #64 - prfm pldl1strm, [x1, #64] + ldp x10, x11, [x1, #64] + ldp x12, x13, [x1, #80] + ldp x14, x15, [x1, #96] + ldp x16, x17, [x1, #112] + + mov x18, #(PAGE_SIZE - 128) + add x1, x1, #128 +1: + subs x18, x18, #128 + stnp x2, x3, [x0] + ldp x2, x3, [x1] stnp x4, x5, [x0, #16] + ldp x4, x5, [x1, #16] stnp x6, x7, [x0, #32] + ldp x6, x7, [x1, #32] stnp x8, x9, [x0, #48] - add x0, x0, #64 - tst x1, #(PAGE_SIZE - 1) - b.ne 1b + ldp x8, x9, [x1, #48] + stnp x10, x11, [x0, #64] + ldp x10, x11, [x1, #64] + stnp x12, x13, [x0, #80] + ldp x12, x13, [x1, #80] + stnp x14, x15, [x0, #96] + ldp x14, x15, [x1, #96] + stnp x16, x17, [x0, #112] + ldp x16, x17, [x1, #112] + + add x0, x0, #128 + add x1, x1, #128 + + b.gt 1b + + stnp x2, x3, [x0] + stnp x4, x5, [x0, #16] + stnp x6, x7, [x0, #32] + stnp x8, x9, [x0, #48] + stnp x10, x11, [x0, #64] + stnp x12, x13, [x0, #80] + stnp x14, x15, [x0, #96] + stnp x16, x17, [x0, #112] + ret ENDPROC(copy_page)
Signature · 1 line
2.1.4
PATCH 4/4 arm64: lib: patch in prfm for copy_page if requested
In reply to Will DeaconFrom: Andrew Pinski <[email protected]>
On ThunderX T88 pass 1 and pass 2, there is no hardware prefetching so we need to patch in explicit software prefetching instructions
Prefetching improves this code by 60% over the original code and 2x over the code without prefetching for the affected hardware using the benchmark code at https://github.com/apinski-cavium/copy_page_benchmark
--- arch/arm64/lib/copy_page.S | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
arch/arm64/lib/copy_page.S +17 -0
@@ -18,6 +18,8 @@ #include <linux/const.h> #include <asm/assembler.h> #include <asm/page.h> +#include <asm/cpufeature.h> +#include <asm/alternative.h> /* * Copy a page from src to dest (both are page aligned)
@@ -27,6 +29,15 @@ * x1 - src */ ENTRY(copy_page) +alternative_if_not ARM64_HAS_NO_HW_PREFETCH + nop + nop +alternative_else + # Prefetch two cache lines ahead. + prfm pldl1strm, [x1, #128] + prfm pldl1strm, [x1, #256] +alternative_endif + ldp x2, x3, [x1] ldp x4, x5, [x1, #16] ldp x6, x7, [x1, #32]
@@ -41,6 +52,12 @@ ENTRY(copy_page) 1: subs x18, x18, #128 +alternative_if_not ARM64_HAS_NO_HW_PREFETCH + nop +alternative_else + prfm pldl1strm, [x1, #384] +alternative_endif + stnp x2, x3, [x0] ldp x2, x3, [x1] stnp x4, x5, [x0, #16]
Signature · 1 line
2.1.4
cover copy_page improvements
In reply to Will DeaconQuoted 9 lines · Will Deacon
Hi all,
This patch series aims to improve our
copy_pageimplementation on both CPUs with and without hardware prefetchers. It is inspired by a previous patch series from Andrew, and one of the patches is lifted directly from there:http://lists.infradead.org/pipermail/linux-arm-kernel/2016-January/399132.html
Quoted 8 lines
Changes from that series:
* Applies on top of mainline
* Inverted the capability to the more genericARM64_HAS_NO_HW_PREFETCH
* Re-jigged the MIDR detection to try to keep it simpleI've left
copy_templatealone for now, since the template really deals with 64 bytes per iteration, which would need changing.
Right, I had looked at doing that also for newlib/glibc but the code is too complex.
Quoted 3 lines
Andrew -- it would be helpful if you could take this for a spin on ThunderX to make sure it didn't screw anything up.
Yes it works and I get the needed prefetch in copy_page.
Thanks,
Andrew
Quoted 23 lines
Cheers,
Will
--->8
Andrew Pinski (1):
arm64: lib: patch in prfm forcopy_pageif requestedWill Deacon (3):
arm64: prefetch: don't providespin_lock_prefetchwith LSE
arm64: prefetch: add alternative pattern for CPUs without a prefetcher
arm64: lib: improvecopy_pageto deal with 128 bytes at a timearch/arm64/include/asm/cpufeature.h | 3 +- arch/arm64/include/asm/cputype.h | 17 +++++++++- arch/arm64/include/asm/processor.h | 7 +++-- arch/arm64/kernel/cpu_errata.c | 18 ++--------- arch/arm64/kernel/cpufeature.c | 17 ++++++++++ arch/arm64/lib/copy_page.S | 63 ++++++++++++++++++++++++++++++++----- 6 files changed, 98 insertions(+), 27 deletions(-)
Catalin Marinas
maintainercover copy_page improvements
In reply to Will DeaconQuoted 7 lines · Will Deacon
Andrew Pinski (1):
arm64: lib: patch in prfm forcopy_pageif requestedWill Deacon (3):
arm64: prefetch: don't providespin_lock_prefetchwith LSE
arm64: prefetch: add alternative pattern for CPUs without a prefetcher
arm64: lib: improvecopy_pageto deal with 128 bytes at a time
Patches queued for 4.6. Thanks.
Signature · 1 line
Catalin