lib/raid6/altivec: Add vpermxor implementation for raid6 Q syndrome
authorMatt Brown <matthew.brown.dev@gmail.com>
Fri, 4 Aug 2017 03:42:32 +0000 (13:42 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 20 Mar 2018 05:47:25 +0000 (16:47 +1100)
This patch uses the vpermxor instruction to optimise the raid6 Q
syndrome. This instruction was made available with POWER8, ISA version
2.07. It allows for both vperm and vxor instructions to be done in a
single instruction. This has been tested for correctness on a ppc64le
vm with a basic RAID6 setup containing 5 drives.

The performance benchmarks are from the raid6test in the
/lib/raid6/test directory. These results are from an IBM Firestone
machine with ppc64le architecture. The benchmark results show a 35%
speed increase over the best existing algorithm for powerpc (altivec).
The raid6test has also been run on a big-endian ppc64 vm to ensure it
also works for big-endian architectures.

Performance benchmarks:
  raid6: altivecx4 gen() 18773 MB/s
  raid6: altivecx8 gen() 19438 MB/s

  raid6: vpermxor4 gen() 25112 MB/s
  raid6: vpermxor8 gen() 26279 MB/s

Signed-off-by: Matt Brown <matthew.brown.dev@gmail.com>
Reviewed-by: Daniel Axtens <dja@axtens.net>
[mpe: Add VPERMXOR macro so we can build with old binutils]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/include/asm/ppc-opcode.h
include/linux/raid/pq.h
lib/raid6/.gitignore
lib/raid6/Makefile
lib/raid6/algos.c
lib/raid6/test/Makefile
lib/raid6/vpermxor.uc [new file with mode: 0644]

index f1083bcf449c5a0f63e30ad226ee678293bf60fd..7370da18035e7d0cdf75ef450e8b386f99b42db0 100644 (file)
 #define PPC_INST_TLBSRX_DOT            0x7c0006a5
 #define PPC_INST_VPMSUMW               0x10000488
 #define PPC_INST_VPMSUMD               0x100004c8
+#define PPC_INST_VPERMXOR              0x1000002d
 #define PPC_INST_XXLOR                 0xf0000490
 #define PPC_INST_XXSWAPD               0xf0000250
 #define PPC_INST_XVCPSGNDP             0xf0000780
 #define XVCPSGNDP(t, a, b)     stringify_in_c(.long (PPC_INST_XVCPSGNDP | \
                                               VSX_XX3((t), (a), (b))))
 
+#define VPERMXOR(vrt, vra, vrb, vrc)                           \
+       stringify_in_c(.long (PPC_INST_VPERMXOR |               \
+                             ___PPC_RT(vrt) | ___PPC_RA(vra) | \
+                             ___PPC_RB(vrb) | (((vrc) & 0x1f) << 6)))
+
 #define PPC_NAP                        stringify_in_c(.long PPC_INST_NAP)
 #define PPC_SLEEP              stringify_in_c(.long PPC_INST_SLEEP)
 #define PPC_WINKLE             stringify_in_c(.long PPC_INST_WINKLE)
index 583cdd3d49ca8f01657fbec641a4b7984eac0e11..fd2e02461e4100440e2b1937df1b8073a5f61737 100644 (file)
@@ -107,6 +107,10 @@ extern const struct raid6_calls raid6_avx512x2;
 extern const struct raid6_calls raid6_avx512x4;
 extern const struct raid6_calls raid6_tilegx8;
 extern const struct raid6_calls raid6_s390vx8;
+extern const struct raid6_calls raid6_vpermxor1;
+extern const struct raid6_calls raid6_vpermxor2;
+extern const struct raid6_calls raid6_vpermxor4;
+extern const struct raid6_calls raid6_vpermxor8;
 
 struct raid6_recov_calls {
        void (*data2)(int, size_t, int, int, void **);
index f01b1cb04f91949a58359bf4503cfbf5849e3db8..3de0d8921286095fe1f6a437364ccff1fdbb8b61 100644 (file)
@@ -4,3 +4,4 @@ int*.c
 tables.c
 neon?.c
 s390vx?.c
+vpermxor*.c
index 4add700ddfe32bc6d86913fd45f2af884d5ace6d..21f59443e99e41860f53ab23b747cbbf4bc41713 100644 (file)
@@ -5,7 +5,8 @@ raid6_pq-y      += algos.o recov.o tables.o int1.o int2.o int4.o \
                   int8.o int16.o int32.o
 
 raid6_pq-$(CONFIG_X86) += recov_ssse3.o recov_avx2.o mmx.o sse1.o sse2.o avx2.o avx512.o recov_avx512.o
-raid6_pq-$(CONFIG_ALTIVEC) += altivec1.o altivec2.o altivec4.o altivec8.o
+raid6_pq-$(CONFIG_ALTIVEC) += altivec1.o altivec2.o altivec4.o altivec8.o \
+                              vpermxor1.o vpermxor2.o vpermxor4.o vpermxor8.o
 raid6_pq-$(CONFIG_KERNEL_MODE_NEON) += neon.o neon1.o neon2.o neon4.o neon8.o recov_neon.o recov_neon_inner.o
 raid6_pq-$(CONFIG_TILEGX) += tilegx8.o
 raid6_pq-$(CONFIG_S390) += s390vx8.o recov_s390xc.o
@@ -91,6 +92,30 @@ $(obj)/altivec8.c:   UNROLL := 8
 $(obj)/altivec8.c:   $(src)/altivec.uc $(src)/unroll.awk FORCE
        $(call if_changed,unroll)
 
+CFLAGS_vpermxor1.o += $(altivec_flags)
+targets += vpermxor1.c
+$(obj)/vpermxor1.c: UNROLL := 1
+$(obj)/vpermxor1.c: $(src)/vpermxor.uc $(src)/unroll.awk FORCE
+       $(call if_changed,unroll)
+
+CFLAGS_vpermxor2.o += $(altivec_flags)
+targets += vpermxor2.c
+$(obj)/vpermxor2.c: UNROLL := 2
+$(obj)/vpermxor2.c: $(src)/vpermxor.uc $(src)/unroll.awk FORCE
+       $(call if_changed,unroll)
+
+CFLAGS_vpermxor4.o += $(altivec_flags)
+targets += vpermxor4.c
+$(obj)/vpermxor4.c: UNROLL := 4
+$(obj)/vpermxor4.c: $(src)/vpermxor.uc $(src)/unroll.awk FORCE
+       $(call if_changed,unroll)
+
+CFLAGS_vpermxor8.o += $(altivec_flags)
+targets += vpermxor8.c
+$(obj)/vpermxor8.c: UNROLL := 8
+$(obj)/vpermxor8.c: $(src)/vpermxor.uc $(src)/unroll.awk FORCE
+       $(call if_changed,unroll)
+
 CFLAGS_neon1.o += $(NEON_FLAGS)
 targets += neon1.c
 $(obj)/neon1.c:   UNROLL := 1
index 4769947232582c9a9074b167c2fe1c0a22143bf3..b2e681018145528096038a7f4adea9b29955d334 100644 (file)
@@ -74,6 +74,10 @@ const struct raid6_calls * const raid6_algos[] = {
        &raid6_altivec2,
        &raid6_altivec4,
        &raid6_altivec8,
+       &raid6_vpermxor1,
+       &raid6_vpermxor2,
+       &raid6_vpermxor4,
+       &raid6_vpermxor8,
 #endif
 #if defined(CONFIG_TILEGX)
        &raid6_tilegx8,
index be1010bdc43586528352dbb84010ea76cf6811a0..ef6d0e00f189531b427cdd584aa76561658cb249 100644 (file)
@@ -48,7 +48,8 @@ else
                          gcc -c -x c - >&/dev/null && \
                          rm ./-.o && echo yes)
         ifeq ($(HAS_ALTIVEC),yes)
-                OBJS += altivec1.o altivec2.o altivec4.o altivec8.o
+                OBJS += altivec1.o altivec2.o altivec4.o altivec8.o \
+                        vpermxor1.o vpermxor2.o vpermxor4.o vpermxor8.o
         endif
 endif
 ifeq ($(ARCH),tilegx)
@@ -98,6 +99,18 @@ altivec4.c: altivec.uc ../unroll.awk
 altivec8.c: altivec.uc ../unroll.awk
        $(AWK) ../unroll.awk -vN=8 < altivec.uc > $@
 
+vpermxor1.c: vpermxor.uc ../unroll.awk
+       $(AWK) ../unroll.awk -vN=1 < vpermxor.uc > $@
+
+vpermxor2.c: vpermxor.uc ../unroll.awk
+       $(AWK) ../unroll.awk -vN=2 < vpermxor.uc > $@
+
+vpermxor4.c: vpermxor.uc ../unroll.awk
+       $(AWK) ../unroll.awk -vN=4 < vpermxor.uc > $@
+
+vpermxor8.c: vpermxor.uc ../unroll.awk
+       $(AWK) ../unroll.awk -vN=8 < vpermxor.uc > $@
+
 int1.c: int.uc ../unroll.awk
        $(AWK) ../unroll.awk -vN=1 < int.uc > $@
 
@@ -123,7 +136,7 @@ tables.c: mktables
        ./mktables > tables.c
 
 clean:
-       rm -f *.o *.a mktables mktables.c *.uc int*.c altivec*.c neon*.c tables.c raid6test
+       rm -f *.o *.a mktables mktables.c *.uc int*.c altivec*.c vpermxor*.c neon*.c tables.c raid6test
        rm -f tilegx*.c
 
 spotless: clean
diff --git a/lib/raid6/vpermxor.uc b/lib/raid6/vpermxor.uc
new file mode 100644 (file)
index 0000000..10475dc
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2017, Matt Brown, IBM Corp.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * vpermxor$#.c
+ *
+ * Based on H. Peter Anvin's paper - The mathematics of RAID-6
+ *
+ * $#-way unrolled portable integer math RAID-6 instruction set
+ * This file is postprocessed using unroll.awk
+ *
+ * vpermxor$#.c makes use of the vpermxor instruction to optimise the RAID6 Q
+ * syndrome calculations.
+ * This can be run on systems which have both Altivec and vpermxor instruction.
+ *
+ * This instruction was introduced in POWER8 - ISA v2.07.
+ */
+
+#include <linux/raid/pq.h>
+#ifdef CONFIG_ALTIVEC
+
+#include <altivec.h>
+#ifdef __KERNEL__
+#include <asm/cputable.h>
+#include <asm/ppc-opcode.h>
+#include <asm/switch_to.h>
+#endif
+
+typedef vector unsigned char unative_t;
+#define NSIZE sizeof(unative_t)
+
+static const vector unsigned char gf_low = {0x1e, 0x1c, 0x1a, 0x18, 0x16, 0x14,
+                                           0x12, 0x10, 0x0e, 0x0c, 0x0a, 0x08,
+                                           0x06, 0x04, 0x02,0x00};
+static const vector unsigned char gf_high = {0xfd, 0xdd, 0xbd, 0x9d, 0x7d, 0x5d,
+                                            0x3d, 0x1d, 0xe0, 0xc0, 0xa0, 0x80,
+                                            0x60, 0x40, 0x20, 0x00};
+
+static void noinline raid6_vpermxor$#_gen_syndrome_real(int disks, size_t bytes,
+                                                       void **ptrs)
+{
+       u8 **dptr = (u8 **)ptrs;
+       u8 *p, *q;
+       int d, z, z0;
+       unative_t wp$$, wq$$, wd$$;
+
+       z0 = disks - 3;         /* Highest data disk */
+       p = dptr[z0+1];         /* XOR parity */
+       q = dptr[z0+2];         /* RS syndrome */
+
+       for (d = 0; d < bytes; d += NSIZE*$#) {
+               wp$$ = wq$$ = *(unative_t *)&dptr[z0][d+$$*NSIZE];
+
+               for (z = z0-1; z>=0; z--) {
+                       wd$$ = *(unative_t *)&dptr[z][d+$$*NSIZE];
+                       /* P syndrome */
+                       wp$$ = vec_xor(wp$$, wd$$);
+
+                       /* Q syndrome */
+                       asm(VPERMXOR(%0,%1,%2,%3):"=v"(wq$$):"v"(gf_high), "v"(gf_low), "v"(wq$$));
+                       wq$$ = vec_xor(wq$$, wd$$);
+               }
+               *(unative_t *)&p[d+NSIZE*$$] = wp$$;
+               *(unative_t *)&q[d+NSIZE*$$] = wq$$;
+       }
+}
+
+static void raid6_vpermxor$#_gen_syndrome(int disks, size_t bytes, void **ptrs)
+{
+       preempt_disable();
+       enable_kernel_altivec();
+
+       raid6_vpermxor$#_gen_syndrome_real(disks, bytes, ptrs);
+
+       disable_kernel_altivec();
+       preempt_enable();
+}
+
+int raid6_have_altivec_vpermxor(void);
+#if $# == 1
+int raid6_have_altivec_vpermxor(void)
+{
+       /* Check if arch has both altivec and the vpermxor instructions */
+# ifdef __KERNEL__
+       return (cpu_has_feature(CPU_FTR_ALTIVEC_COMP) &&
+               cpu_has_feature(CPU_FTR_ARCH_207S));
+# else
+       return 1;
+#endif
+
+}
+#endif
+
+const struct raid6_calls raid6_vpermxor$# = {
+       raid6_vpermxor$#_gen_syndrome,
+       NULL,
+       raid6_have_altivec_vpermxor,
+       "vpermxor$#",
+       0
+};
+#endif