powerpc/pmem: Add flush routines using new pmem store and sync instruction
authorAneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
Wed, 1 Jul 2020 07:22:31 +0000 (12:52 +0530)
committerMichael Ellerman <mpe@ellerman.id.au>
Thu, 16 Jul 2020 03:00:22 +0000 (13:00 +1000)
Start using dcbstps; phwsync; sequence for flushing persistent memory range.
The new instructions are implemented as a variant of dcbf and hwsync and on
P8 and P9 they will be executed as those instructions. We avoid using them on
older hardware. This helps to avoid difficult to debug bugs.

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20200701072235.223558-4-aneesh.kumar@linux.ibm.com
arch/powerpc/include/asm/cacheflush.h
arch/powerpc/lib/pmem.c

index de600b915a3c5e22cc84319914e2cce66ec30a15..54764c6e922def0e74528b159c091079389a000e 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <linux/mm.h>
 #include <asm/cputable.h>
+#include <asm/cpu_has_feature.h>
 
 #ifdef CONFIG_PPC_BOOK3S_64
 /*
index 0666a8d2959622208aaf36ad45f9777dfbdc1ed1..5a61aaeb69302b1568635a3b45e2636acc64e75a 100644 (file)
@@ -9,20 +9,62 @@
 
 #include <asm/cacheflush.h>
 
+static inline void __clean_pmem_range(unsigned long start, unsigned long stop)
+{
+       unsigned long shift = l1_dcache_shift();
+       unsigned long bytes = l1_dcache_bytes();
+       void *addr = (void *)(start & ~(bytes - 1));
+       unsigned long size = stop - (unsigned long)addr + (bytes - 1);
+       unsigned long i;
+
+       for (i = 0; i < size >> shift; i++, addr += bytes)
+               asm volatile(PPC_DCBSTPS(%0, %1): :"i"(0), "r"(addr): "memory");
+
+
+       asm volatile(PPC_PHWSYNC ::: "memory");
+}
+
+static inline void __flush_pmem_range(unsigned long start, unsigned long stop)
+{
+       unsigned long shift = l1_dcache_shift();
+       unsigned long bytes = l1_dcache_bytes();
+       void *addr = (void *)(start & ~(bytes - 1));
+       unsigned long size = stop - (unsigned long)addr + (bytes - 1);
+       unsigned long i;
+
+       for (i = 0; i < size >> shift; i++, addr += bytes)
+               asm volatile(PPC_DCBFPS(%0, %1): :"i"(0), "r"(addr): "memory");
+
+
+       asm volatile(PPC_PHWSYNC ::: "memory");
+}
+
+static inline void clean_pmem_range(unsigned long start, unsigned long stop)
+{
+       if (cpu_has_feature(CPU_FTR_ARCH_207S))
+               return __clean_pmem_range(start, stop);
+}
+
+static inline void flush_pmem_range(unsigned long start, unsigned long stop)
+{
+       if (cpu_has_feature(CPU_FTR_ARCH_207S))
+               return __flush_pmem_range(start, stop);
+}
+
 /*
  * CONFIG_ARCH_HAS_PMEM_API symbols
  */
 void arch_wb_cache_pmem(void *addr, size_t size)
 {
        unsigned long start = (unsigned long) addr;
-       flush_dcache_range(start, start + size);
+       clean_pmem_range(start, start + size);
 }
 EXPORT_SYMBOL_GPL(arch_wb_cache_pmem);
 
 void arch_invalidate_pmem(void *addr, size_t size)
 {
        unsigned long start = (unsigned long) addr;
-       flush_dcache_range(start, start + size);
+       flush_pmem_range(start, start + size);
 }
 EXPORT_SYMBOL_GPL(arch_invalidate_pmem);
 
@@ -35,7 +77,7 @@ long __copy_from_user_flushcache(void *dest, const void __user *src,
        unsigned long copied, start = (unsigned long) dest;
 
        copied = __copy_from_user(dest, src, size);
-       flush_dcache_range(start, start + size);
+       clean_pmem_range(start, start + size);
 
        return copied;
 }
@@ -45,7 +87,7 @@ void *memcpy_flushcache(void *dest, const void *src, size_t size)
        unsigned long start = (unsigned long) dest;
 
        memcpy(dest, src, size);
-       flush_dcache_range(start, start + size);
+       clean_pmem_range(start, start + size);
 
        return dest;
 }