ipv6: Implement optimized IPv6 masked address comparison for ARM64
Subsystems (22)
PATCH 1/1 ipv6: Implement optimized IPv6 masked address comparison for ARM64
Android devices use multiple ip[6]tables for statistics, UID matching and other functionality. Perf output indicated that ip6_do_table was taking a considerable amount of CPU and more that ip_do_table for an equivalent rate. ipv6_masked_addr_cmp was chosen for optimization as there are more instructions required than the equivalent operation in ip_packet_match.
Using 128 bit operations helps to reduce the number of instructions for the match on an ARM64 system. This helps to improve UDPv6 DL performance by 40Mbps (860Mbps -> 900Mbps) on a CPU limited system.
Tested on x86_64 UML to check if generic version is used and ARM64 to verify that ARM64 version is used.
--- arch/alpha/include/asm/Kbuild | 1 + arch/arc/include/asm/Kbuild | 1 + arch/arm/include/asm/Kbuild | 1 + arch/arm64/include/asm/ipv6.h | 29 +++++++++++++++++++++++++++++ arch/avr32/include/asm/Kbuild | 1 + arch/blackfin/include/asm/Kbuild | 1 + arch/c6x/include/asm/Kbuild | 1 + arch/cris/include/asm/Kbuild | 1 + arch/frv/include/asm/Kbuild | 1 + arch/h8300/include/asm/Kbuild | 1 + arch/hexagon/include/asm/Kbuild | 1 + arch/ia64/include/asm/Kbuild | 1 + arch/m32r/include/asm/Kbuild | 1 + arch/m68k/include/asm/Kbuild | 1 + arch/metag/include/asm/Kbuild | 1 + arch/microblaze/include/asm/Kbuild | 1 + arch/mips/include/asm/Kbuild | 1 + arch/mn10300/include/asm/Kbuild | 1 + arch/nios2/include/asm/Kbuild | 1 + arch/openrisc/include/asm/Kbuild | 1 + arch/parisc/include/asm/Kbuild | 1 + arch/powerpc/include/asm/Kbuild | 1 + arch/s390/include/asm/Kbuild | 1 + arch/score/include/asm/Kbuild | 1 + arch/sh/include/asm/Kbuild | 1 + arch/sparc/include/asm/Kbuild | 1 + arch/tile/include/asm/Kbuild | 1 + arch/um/include/asm/Kbuild | 1 + arch/unicore32/include/asm/Kbuild | 1 + arch/x86/include/asm/Kbuild | 1 + arch/xtensa/include/asm/Kbuild | 1 + include/asm-generic/ipv6.h | 32 ++++++++++++++++++++++++++++++++ include/net/ipv6.h | 20 +------------------- 33 files changed, 92 insertions(+), 19 deletions(-) create mode 100644 arch/arm64/include/asm/ipv6.h create mode 100644 include/asm-generic/ipv6.h
arch/alpha/include/asm/Kbuild +1 -0
@@ -3,6 +3,7 @@ generic-y += clkdev.h generic-y += exec.h generic-y += export.h +generic-y += ipv6.h generic-y += irq_work.h generic-y += mcs_spinlock.h generic-y += mm-arch-hooks.h
arch/arc/include/asm/Kbuild +1 -0
@@ -14,6 +14,7 @@ generic-y += hw_irq.h generic-y += ioctl.h generic-y += ioctls.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kmap_types.h
arch/arm/include/asm/Kbuild +1 -0
@@ -9,6 +9,7 @@ generic-y += errno.h generic-y += exec.h generic-y += ioctl.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += kdebug.h generic-y += local.h
arch/arm64/include/asm/ipv6.h new file +29 -0
@@ -0,0 +1,29 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 and +* only version 2 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#ifndef __ASM_IPV6_H +#define __ASM_IPV6_H + +#include <linux/types.h> + +static inline bool +ipv6_masked_addr_cmp (const struct in6_addr *a1,const struct in6_addr *m, +const struct in6_addr *a2) +{ +const __uint128_t *ul1 = (const __uint128_t *)a1; +const __uint128_t *ulm = (const __uint128_t *)m; +const __uint128_t *ul2 = (const __uint128_t *)a1; + +return !!((*ul1 ^ *ul2) & *ulm); +} +#define ipv6_masked_addr_cmp ipv6_masked_addr_cmp +#endif /* __ASM_IPV6_H */
arch/avr32/include/asm/Kbuild +1 -0
@@ -6,6 +6,7 @@ generic-y += div64.h generic-y += emergency-restart.h generic-y += exec.h generic-y += futex.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += local.h
arch/blackfin/include/asm/Kbuild +1 -0
@@ -12,6 +12,7 @@ generic-y += futex.h generic-y += hw_irq.h generic-y += ioctl.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.h
arch/c6x/include/asm/Kbuild +1 -0
@@ -20,6 +20,7 @@ generic-y += io.h generic-y += ioctl.h generic-y += ioctls.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.h
arch/cris/include/asm/Kbuild +1 -0
@@ -15,6 +15,7 @@ generic-y += futex.h generic-y += hardirq.h generic-y += ioctl.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.h
arch/frv/include/asm/Kbuild +1 -0
@@ -1,6 +1,7 @@ generic-y += clkdev.h generic-y += exec.h +generic-y += ipv6.h generic-y += irq_work.h generic-y += mcs_spinlock.h generic-y += mm-arch-hooks.h
arch/h8300/include/asm/Kbuild +1 -0
@@ -23,6 +23,7 @@ generic-y += hw_irq.h generic-y += ioctl.h generic-y += ioctls.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.h
arch/hexagon/include/asm/Kbuild +1 -0
@@ -20,6 +20,7 @@ generic-y += ioctl.h generic-y += ioctls.h generic-y += iomap.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.h
arch/ia64/include/asm/Kbuild +1 -0
@@ -1,6 +1,7 @@ generic-y += clkdev.h generic-y += exec.h +generic-y += ipv6.h generic-y += irq_work.h generic-y += kvm_para.h generic-y += mcs_spinlock.h
arch/m32r/include/asm/Kbuild +1 -0
@@ -2,6 +2,7 @@ generic-y += clkdev.h generic-y += current.h generic-y += exec.h +generic-y += ipv6.h generic-y += irq_work.h generic-y += kvm_para.h generic-y += mcs_spinlock.h
arch/m68k/include/asm/Kbuild +1 -0
@@ -9,6 +9,7 @@ generic-y += futex.h generic-y += hw_irq.h generic-y += ioctl.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.h
arch/metag/include/asm/Kbuild +1 -0
@@ -16,6 +16,7 @@ generic-y += hw_irq.h generic-y += ioctl.h generic-y += ioctls.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.h
arch/microblaze/include/asm/Kbuild +1 -0
@@ -3,6 +3,7 @@ generic-y += barrier.h generic-y += clkdev.h generic-y += device.h generic-y += exec.h +generic-y += ipv6.h generic-y += irq_work.h generic-y += mcs_spinlock.h generic-y += mm-arch-hooks.h
arch/mips/include/asm/Kbuild +1 -0
@@ -5,6 +5,7 @@ generic-y += current.h generic-y += dma-contiguous.h generic-y += emergency-restart.h generic-y += export.h +generic-y += ipv6.h generic-y += irq_work.h generic-y += local64.h generic-y += mcs_spinlock.h
arch/mn10300/include/asm/Kbuild +1 -0
@@ -2,6 +2,7 @@ generic-y += barrier.h generic-y += clkdev.h generic-y += exec.h +generic-y += ipv6.h generic-y += irq_work.h generic-y += mcs_spinlock.h generic-y += mm-arch-hooks.h
arch/nios2/include/asm/Kbuild +1 -0
@@ -22,6 +22,7 @@ generic-y += hw_irq.h generic-y += ioctl.h generic-y += ioctls.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.h
arch/openrisc/include/asm/Kbuild +1 -0
@@ -25,6 +25,7 @@ generic-y += ioctl.h generic-y += ioctls.h generic-y += ipcbuf.h generic-y += irq.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.h
arch/parisc/include/asm/Kbuild +1 -0
@@ -8,6 +8,7 @@ generic-y += div64.h generic-y += emergency-restart.h generic-y += exec.h generic-y += hw_irq.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.h
arch/powerpc/include/asm/Kbuild +1 -0
@@ -1,6 +1,7 @@ generic-y += clkdev.h generic-y += div64.h generic-y += export.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += local64.h
arch/s390/include/asm/Kbuild +1 -0
@@ -2,6 +2,7 @@ generic-y += asm-offsets.h generic-y += clkdev.h generic-y += dma-contiguous.h generic-y += export.h +generic-y += ipv6.h generic-y += irq_work.h generic-y += mcs_spinlock.h generic-y += mm-arch-hooks.h
arch/score/include/asm/Kbuild +1 -0
@@ -4,6 +4,7 @@ header-y += generic-y += barrier.h generic-y += clkdev.h generic-y += current.h +generic-y += ipv6.h generic-y += irq_work.h generic-y += mcs_spinlock.h generic-y += mm-arch-hooks.h
arch/sh/include/asm/Kbuild +1 -0
@@ -10,6 +10,7 @@ generic-y += exec.h generic-y += fcntl.h generic-y += ioctl.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kvm_para.h
arch/sparc/include/asm/Kbuild +1 -0
@@ -6,6 +6,7 @@ generic-y += div64.h generic-y += emergency-restart.h generic-y += exec.h generic-y += export.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += linkage.h
arch/tile/include/asm/Kbuild +1 -0
@@ -12,6 +12,7 @@ generic-y += fcntl.h generic-y += hw_irq.h generic-y += ioctl.h generic-y += ioctls.h +generic-y += ipv6.h generic-y += ipcbuf.h generic-y += irq_regs.h generic-y += local.h
arch/um/include/asm/Kbuild +1 -0
@@ -11,6 +11,7 @@ generic-y += futex.h generic-y += hardirq.h generic-y += hw_irq.h generic-y += io.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.h
arch/unicore32/include/asm/Kbuild +1 -0
@@ -19,6 +19,7 @@ generic-y += hw_irq.h generic-y += ioctl.h generic-y += ioctls.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.h
arch/x86/include/asm/Kbuild +1 -0
@@ -9,5 +9,6 @@ generated-y += xen-hypercalls.h generic-y += clkdev.h generic-y += dma-contiguous.h generic-y += early_ioremap.h +generic-y += ipv6.h generic-y += mcs_spinlock.h generic-y += mm-arch-hooks.h
arch/xtensa/include/asm/Kbuild +1 -0
@@ -9,6 +9,7 @@ generic-y += exec.h generic-y += fcntl.h generic-y += hardirq.h generic-y += ioctl.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.h
include/asm-generic/ipv6.h new file +32 -0
@@ -0,0 +1,32 @@ +/* Linux INET6 implementation +* +* Authors: +* Pedro Roque <[email protected]> +* +* 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. +*/ + +#ifndef ipv6_masked_addr_cmp +#define ipv6_masked_addr_cmp ipv6_masked_addr_cmp +static inline bool +ipv6_masked_addr_cmp (const struct in6_addr *a1,const struct in6_addr *m, +const struct in6_addr *a2) +{ +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS ) &&BITS_PER_LONG ==64 +const unsigned long *ul1 = (const unsigned long *)a1; +const unsigned long *ulm = (const unsigned long *)m; +const unsigned long *ul2 = (const unsigned long *)a2; + +return !!(((ul1[0 ] ^ ul2[0 ]) & ulm[0 ]) | + ((ul1[1 ] ^ ul2[1 ]) & ulm[1 ])); +#else +return !!(((a1->s6_addr32[0 ] ^ a2->s6_addr32[0 ]) & m->s6_addr32[0 ]) | + ((a1->s6_addr32[1 ] ^ a2->s6_addr32[1 ]) & m->s6_addr32[1 ]) | + ((a1->s6_addr32[2 ] ^ a2->s6_addr32[2 ]) & m->s6_addr32[2 ]) | + ((a1->s6_addr32[3 ] ^ a2->s6_addr32[3 ]) & m->s6_addr32[3 ])); +#endif +} +#endif
include/net/ipv6.h +1 -19
@@ -21,6 +21,7 @@#include <net/flow.h> #include <net/flow_dissector.h> #include <net/snmp.h> +#include <asm/ipv6.h> #define SIN6_LEN_RFC2133 24
@@ -385,25 +386,6 @@ static inline int ipv6_addr_cmp(const struct in6_addr *a1, const struct in6_addrreturn memcmp (a1, a2,sizeof (struct in6_addr )); } -static inline bool -ipv6_masked_addr_cmp (const struct in6_addr *a1,const struct in6_addr *m, -const struct in6_addr *a2) -{ -#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS ) &&BITS_PER_LONG ==64 -const unsigned long *ul1 = (const unsigned long *)a1; -const unsigned long *ulm = (const unsigned long *)m; -const unsigned long *ul2 = (const unsigned long *)a2; - -return !!(((ul1[0 ] ^ ul2[0 ]) & ulm[0 ]) | - ((ul1[1 ] ^ ul2[1 ]) & ulm[1 ])); -#else -return !!(((a1->s6_addr32[0 ] ^ a2->s6_addr32[0 ]) & m->s6_addr32[0 ]) | - ((a1->s6_addr32[1 ] ^ a2->s6_addr32[1 ]) & m->s6_addr32[1 ]) | - ((a1->s6_addr32[2 ] ^ a2->s6_addr32[2 ]) & m->s6_addr32[2 ]) | - ((a1->s6_addr32[3 ] ^ a2->s6_addr32[3 ]) & m->s6_addr32[3 ])); -#endif -} -static inline void ipv6_addr_prefix (struct in6_addr *pfx,const struct in6_addr *addr,int plen)
Signature · 1 line
1.9.1
Quoted 10 lines · Subash Abhinov Kasiviswanathan
Android devices use multiple ip[6]tables for statistics, UID matching and other functionality. Perf output indicated that
ip6_do_tablewas taking a considerable amount of CPU and more thatip_do_tablefor an equivalent rate.ipv6_masked_addr_cmpwas chosen for optimization as there are more instructions required than the equivalent operation inip_packet_match.Using 128 bit operations helps to reduce the number of instructions for the match on an ARM64 system. This helps to improve UDPv6 DL performance by 40Mbps (860Mbps -> 900Mbps) on a CPU limited system.
After trying to have a look at the codegen difference it makes, I think I may have found why it's faster ;)
----------
[root@space-channel-5 ~]# cat > ip.c
#include <stdbool.h>
#include <netinet/in.h>
bool
ipv6_masked_addr_cmp(const struct in6_addr *a1, const struct in6_addr *m,
const struct in6_addr *a2)
{
const unsigned long *ul1 = (const unsigned long *)a1;
const unsigned long *ulm = (const unsigned long *)m;
const unsigned long *ul2 = (const unsigned long *)a2;
return !!(((ul1[0] ^ ul2[0]) & ulm[0]) |
((ul1[1] ^ ul2[1]) & ulm[1]));
}bool
ipv6_masked_addr_cmp_new(const struct in6_addr *a1, const struct in6_addr *m,
const struct in6_addr *a2)
{
const __uint128_t *ul1 = (const __uint128_t *)a1;
const __uint128_t *ulm = (const __uint128_t *)m;
const __uint128_t *ul2 = (const __uint128_t *)a1;
return !!((*ul1 ^ *ul2) & *ulm);
}[root@space-channel-5 ~]# gcc -c -O2 ip.c
[root@space-channel-5 ~]# objdump -d ip.o
ip.o: file format elf64-littleaarch64
Disassembly of section .text:
0000000000000000 <ipv6_masked_addr_cmp>:
0: a9401847 ldp x7, x6, [x2]
4: a9401003 ldp x3, x4, [x0]
8: f9400025 ldr x5, [x1]
c: f9400422 ldr x2, [x1, #8]
10: ca070060 eor x0, x3, x7
14: ca060081 eor x1, x4, x6
18: 8a050000 and x0, x0, x5
1c: 8a020021 and x1, x1, x2
20: aa010000 orr x0, x0, x1
24: f100001f cmp x0, #0x0
28: 1a9f07e0 cset w0, ne // ne = any
2c: d65f03c0 ret
0000000000000030 <ipv6_masked_addr_cmp_new>:
30: 52800000 mov w0, #0x0 // #0
34: d65f03c0 ret
[root@space-channel-5 ~]# gcc --version
gcc (GCC) 6.3.1 20170306
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
----------
That's clearly not right - I'm not sure quite what undefined behaviour assumption convinces GCC to optimise the whole thing away, but I do note that the generic 64-bit version really isn't far off optimal already. Even if it happens to work out in practice due to inlining behaviour, I don't think that's something we'd want to rely on.
Robin.
Quoted 513 lines
Tested on
x86_64UML to check if generic version is used and ARM64 to verify that ARM64 version is used.Signed-off-by Subash Abhinov Kasiviswanathan <[email protected]>--- arch/alpha/include/asm/Kbuild | 1 + arch/arc/include/asm/Kbuild | 1 + arch/arm/include/asm/Kbuild | 1 + arch/arm64/include/asm/ipv6.h | 29 +++++++++++++++++++++++++++++ arch/avr32/include/asm/Kbuild | 1 + arch/blackfin/include/asm/Kbuild | 1 + arch/c6x/include/asm/Kbuild | 1 + arch/cris/include/asm/Kbuild | 1 + arch/frv/include/asm/Kbuild | 1 + arch/h8300/include/asm/Kbuild | 1 + arch/hexagon/include/asm/Kbuild | 1 + arch/ia64/include/asm/Kbuild | 1 + arch/m32r/include/asm/Kbuild | 1 + arch/m68k/include/asm/Kbuild | 1 + arch/metag/include/asm/Kbuild | 1 + arch/microblaze/include/asm/Kbuild | 1 + arch/mips/include/asm/Kbuild | 1 + arch/mn10300/include/asm/Kbuild | 1 + arch/nios2/include/asm/Kbuild | 1 + arch/openrisc/include/asm/Kbuild | 1 + arch/parisc/include/asm/Kbuild | 1 + arch/powerpc/include/asm/Kbuild | 1 + arch/s390/include/asm/Kbuild | 1 + arch/score/include/asm/Kbuild | 1 + arch/sh/include/asm/Kbuild | 1 + arch/sparc/include/asm/Kbuild | 1 + arch/tile/include/asm/Kbuild | 1 + arch/um/include/asm/Kbuild | 1 + arch/unicore32/include/asm/Kbuild | 1 + arch/x86/include/asm/Kbuild | 1 + arch/xtensa/include/asm/Kbuild | 1 + include/asm-generic/ipv6.h | 32 ++++++++++++++++++++++++++++++++ include/net/ipv6.h | 20 +------------------- 33 files changed, 92 insertions(+), 19 deletions(-) create mode 100644 arch/arm64/include/asm/ipv6.h create mode 100644 include/asm-generic/ipv6.harch/alpha/include/asm/Kbuild +1
-0@@ -3,6 +3,7 @@ generic-y += clkdev.h generic-y += exec.h generic-y += export.h +generic-y += ipv6.h generic-y += irq_work.h generic-y += mcs_spinlock.h generic-y += mm-arch-hooks.harch/arc/include/asm/Kbuild +1
-0@@ -14,6 +14,7 @@ generic-y += hw_irq.h generic-y += ioctl.h generic-y += ioctls.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kmap_types.harch/arm/include/asm/Kbuild +1
-0@@ -9,6 +9,7 @@ generic-y += errno.h generic-y += exec.h generic-y += ioctl.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += kdebug.h generic-y += local.harch/arm64/include/asm/ipv6.h new file +29
-0@@ -0,0 +1,29 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 and +* only version 2 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#ifndef __ASM_IPV6_H +#define __ASM_IPV6_H + +#include <linux/types.h> + +static inline bool +ipv6_masked_addr_cmp (const struct in6_addr *a1,const struct in6_addr *m, +const struct in6_addr *a2) +{ +const __uint128_t *ul1 = (const __uint128_t *)a1; +const __uint128_t *ulm = (const __uint128_t *)m; +const __uint128_t *ul2 = (const __uint128_t *)a1; + +return !!((*ul1 ^ *ul2) & *ulm); +} +#define ipv6_masked_addr_cmp ipv6_masked_addr_cmp +#endif /* __ASM_IPV6_H */ arch/avr32/include/asm/Kbuild +1
-0@@ -6,6 +6,7 @@ generic-y += div64.h generic-y += emergency-restart.h generic-y += exec.h generic-y += futex.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += local.harch/blackfin/include/asm/Kbuild +1
-0@@ -12,6 +12,7 @@ generic-y += futex.h generic-y += hw_irq.h generic-y += ioctl.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.harch/c6x/include/asm/Kbuild +1
-0@@ -20,6 +20,7 @@ generic-y += io.h generic-y += ioctl.h generic-y += ioctls.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.harch/cris/include/asm/Kbuild +1
-0@@ -15,6 +15,7 @@ generic-y += futex.h generic-y += hardirq.h generic-y += ioctl.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.harch/frv/include/asm/Kbuild +1
-0@@ -1,6 +1,7 @@ generic-y += clkdev.h generic-y += exec.h +generic-y += ipv6.h generic-y += irq_work.h generic-y += mcs_spinlock.h generic-y += mm-arch-hooks.harch/h8300/include/asm/Kbuild +1
-0@@ -23,6 +23,7 @@ generic-y += hw_irq.h generic-y += ioctl.h generic-y += ioctls.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.harch/hexagon/include/asm/Kbuild +1
-0@@ -20,6 +20,7 @@ generic-y += ioctl.h generic-y += ioctls.h generic-y += iomap.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.harch/ia64/include/asm/Kbuild +1
-0@@ -1,6 +1,7 @@ generic-y += clkdev.h generic-y += exec.h +generic-y += ipv6.h generic-y += irq_work.h generic-y += kvm_para.h generic-y += mcs_spinlock.harch/m32r/include/asm/Kbuild +1
-0@@ -2,6 +2,7 @@ generic-y += clkdev.h generic-y += current.h generic-y += exec.h +generic-y += ipv6.h generic-y += irq_work.h generic-y += kvm_para.h generic-y += mcs_spinlock.harch/m68k/include/asm/Kbuild +1
-0@@ -9,6 +9,7 @@ generic-y += futex.h generic-y += hw_irq.h generic-y += ioctl.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.harch/metag/include/asm/Kbuild +1
-0@@ -16,6 +16,7 @@ generic-y += hw_irq.h generic-y += ioctl.h generic-y += ioctls.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.harch/microblaze/include/asm/Kbuild +1
-0@@ -3,6 +3,7 @@ generic-y += barrier.h generic-y += clkdev.h generic-y += device.h generic-y += exec.h +generic-y += ipv6.h generic-y += irq_work.h generic-y += mcs_spinlock.h generic-y += mm-arch-hooks.harch/mips/include/asm/Kbuild +1
-0@@ -5,6 +5,7 @@ generic-y += current.h generic-y += dma-contiguous.h generic-y += emergency-restart.h generic-y += export.h +generic-y += ipv6.h generic-y += irq_work.h generic-y += local64.h generic-y += mcs_spinlock.harch/mn10300/include/asm/Kbuild +1
-0@@ -2,6 +2,7 @@ generic-y += barrier.h generic-y += clkdev.h generic-y += exec.h +generic-y += ipv6.h generic-y += irq_work.h generic-y += mcs_spinlock.h generic-y += mm-arch-hooks.harch/nios2/include/asm/Kbuild +1
-0@@ -22,6 +22,7 @@ generic-y += hw_irq.h generic-y += ioctl.h generic-y += ioctls.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.harch/openrisc/include/asm/Kbuild +1
-0@@ -25,6 +25,7 @@ generic-y += ioctl.h generic-y += ioctls.h generic-y += ipcbuf.h generic-y += irq.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.harch/parisc/include/asm/Kbuild +1
-0@@ -8,6 +8,7 @@ generic-y += div64.h generic-y += emergency-restart.h generic-y += exec.h generic-y += hw_irq.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.harch/powerpc/include/asm/Kbuild +1
-0@@ -1,6 +1,7 @@ generic-y += clkdev.h generic-y += div64.h generic-y += export.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += local64.harch/s390/include/asm/Kbuild +1
-0@@ -2,6 +2,7 @@ generic-y += asm-offsets.h generic-y += clkdev.h generic-y += dma-contiguous.h generic-y += export.h +generic-y += ipv6.h generic-y += irq_work.h generic-y += mcs_spinlock.h generic-y += mm-arch-hooks.harch/score/include/asm/Kbuild +1
-0@@ -4,6 +4,7 @@ header-y += generic-y += barrier.h generic-y += clkdev.h generic-y += current.h +generic-y += ipv6.h generic-y += irq_work.h generic-y += mcs_spinlock.h generic-y += mm-arch-hooks.harch/sh/include/asm/Kbuild +1
-0@@ -10,6 +10,7 @@ generic-y += exec.h generic-y += fcntl.h generic-y += ioctl.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kvm_para.harch/sparc/include/asm/Kbuild +1
-0@@ -6,6 +6,7 @@ generic-y += div64.h generic-y += emergency-restart.h generic-y += exec.h generic-y += export.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += linkage.harch/tile/include/asm/Kbuild +1
-0@@ -12,6 +12,7 @@ generic-y += fcntl.h generic-y += hw_irq.h generic-y += ioctl.h generic-y += ioctls.h +generic-y += ipv6.h generic-y += ipcbuf.h generic-y += irq_regs.h generic-y += local.harch/um/include/asm/Kbuild +1
-0@@ -11,6 +11,7 @@ generic-y += futex.h generic-y += hardirq.h generic-y += hw_irq.h generic-y += io.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.harch/unicore32/include/asm/Kbuild +1
-0@@ -19,6 +19,7 @@ generic-y += hw_irq.h generic-y += ioctl.h generic-y += ioctls.h generic-y += ipcbuf.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.harch/x86/include/asm/Kbuild +1
-0@@ -9,5 +9,6 @@ generated-y += xen-hypercalls.h generic-y += clkdev.h generic-y += dma-contiguous.h generic-y += early_ioremap.h +generic-y += ipv6.h generic-y += mcs_spinlock.h generic-y += mm-arch-hooks.harch/xtensa/include/asm/Kbuild +1
-0@@ -9,6 +9,7 @@ generic-y += exec.h generic-y += fcntl.h generic-y += hardirq.h generic-y += ioctl.h +generic-y += ipv6.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.hinclude/asm-generic/ipv6.h new file +32
-0@@ -0,0 +1,32 @@ +/* Linux INET6 implementation +* +* Authors: +* Pedro Roque <[email protected]> +* +* 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. +*/ + +#ifndef ipv6_masked_addr_cmp +#define ipv6_masked_addr_cmp ipv6_masked_addr_cmp +static inline bool +ipv6_masked_addr_cmp (const struct in6_addr *a1,const struct in6_addr *m, +const struct in6_addr *a2) +{ +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS ) &&BITS_PER_LONG ==64 +const unsigned long *ul1 = (const unsigned long *)a1; +const unsigned long *ulm = (const unsigned long *)m; +const unsigned long *ul2 = (const unsigned long *)a2; + +return !!(((ul1[0 ] ^ ul2[0 ]) & ulm[0 ]) | + ((ul1[1 ] ^ ul2[1 ]) & ulm[1 ])); +#else +return !!(((a1->s6_addr32[0 ] ^ a2->s6_addr32[0 ]) & m->s6_addr32[0 ]) | + ((a1->s6_addr32[1 ] ^ a2->s6_addr32[1 ]) & m->s6_addr32[1 ]) | + ((a1->s6_addr32[2 ] ^ a2->s6_addr32[2 ]) & m->s6_addr32[2 ]) | + ((a1->s6_addr32[3 ] ^ a2->s6_addr32[3 ]) & m->s6_addr32[3 ])); +#endif +} +#endif include/net/ipv6.h +1
-19@@ -21,6 +21,7 @@#include <net/flow.h> #include <net/flow_dissector.h> #include <net/snmp.h> +#include <asm/ipv6.h> #define SIN6_LEN_RFC2133 24@@ -385,25 +386,6 @@ static inline int ipv6_addr_cmp(const struct in6_addr *a1, const struct in6_addrreturn memcmp (a1, a2,sizeof (struct in6_addr )); } -static inline bool -ipv6_masked_addr_cmp (const struct in6_addr *a1,const struct in6_addr *m, -const struct in6_addr *a2) -{ -#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS ) &&BITS_PER_LONG ==64 -const unsigned long *ul1 = (const unsigned long *)a1; -const unsigned long *ulm = (const unsigned long *)m; -const unsigned long *ul2 = (const unsigned long *)a2; - -return !!(((ul1[0 ] ^ ul2[0 ]) & ulm[0 ]) | - ((ul1[1 ] ^ ul2[1 ]) & ulm[1 ])); -#else -return !!(((a1->s6_addr32[0 ] ^ a2->s6_addr32[0 ]) & m->s6_addr32[0 ]) | - ((a1->s6_addr32[1 ] ^ a2->s6_addr32[1 ]) & m->s6_addr32[1 ]) | - ((a1->s6_addr32[2 ] ^ a2->s6_addr32[2 ]) & m->s6_addr32[2 ]) | - ((a1->s6_addr32[3 ] ^ a2->s6_addr32[3 ]) & m->s6_addr32[3 ])); -#endif -} -static inline void ipv6_addr_prefix (struct in6_addr *pfx,const struct in6_addr *addr,int plen)
Quoted 43 lines · Robin Murphy
Quoted 10 lines · Subash Abhinov Kasiviswanathan
Android devices use multiple ip[6]tables for statistics, UID matching and other functionality. Perf output indicated that
ip6_do_tablewas taking a considerable amount of CPU and more thatip_do_tablefor an equivalent rate.ipv6_masked_addr_cmpwas chosen for optimization as there are more instructions required than the equivalent operation inip_packet_match.Using 128 bit operations helps to reduce the number of instructions for the match on an ARM64 system. This helps to improve UDPv6 DL performance by 40Mbps (860Mbps -> 900Mbps) on a CPU limited system.
After trying to have a look at the codegen difference it makes, I think I may have found why it's faster ;)
----------
[root@space-channel-5 ~]# cat >ip.c
#include <stdbool.h>
#include <netinet/in.h>bool
ipv6_masked_addr_cmp(const structin6_addr*a1, const structin6_addr*m,
const structin6_addr*a2){ const unsigned long *ul1 = (const unsigned long *)a1; const unsigned long *ulm = (const unsigned long *)m; const unsigned long *ul2 = (const unsigned long *)a2; return !!(((ul1[0] ^ ul2[0]) & ulm[0]) | ((ul1[1] ^ ul2[1]) & ulm[1])); }bool
ipv6_masked_addr_cmp_new(const structin6_addr*a1, const structin6_addr*m,
const structin6_addr*a2){ const __uint128_t *ul1 = (const __uint128_t *)a1; const __uint128_t *ulm = (const __uint128_t *)m; const __uint128_t *ul2 = (const __uint128_t *)a1; return !!((*ul1 ^ *ul2) & *ulm); }
<snip>
Quoted 2 lines
That's clearly not right - I'm not sure quite what undefined behaviour assumption convinces GCC to optimise the whole thing away>
While the pointer casting is a bit ghastly, I don't actually think that GCC is taking advantage of undefined behaviour here, rather it looks like you have a simple typo on line 3:
Quoted 3 lines
const __uint128_t *ul1 = (const __uint128_t *)a1; const __uint128_t *ulm = (const __uint128_t *)m; const __uint128_t *ul2 = (const __uint128_t *)a1;
ul2 = a2, surely?
As it is (stripping casts) you have a1 ^ a1, which will get you to 0 pretty quickly. Fixing that up for you;
bool
ipv6_masked_addr_cmp_new(const struct in6_addr *a1, const struct
in6_addr *m,
const struct in6_addr *a2)
{
const __uint128_t *ul1 = (const __uint128_t *)a1;
const __uint128_t *ulm = (const __uint128_t *)m;
const __uint128_t *ul2 = (const __uint128_t *)a2;
return !!((*ul1 ^ *ul2) & *ulm);
}$ gcc -O2
ipv6_masked_addr_cmp_new:
ldp x4, x3, [x0] ldp x5, x2, [x2] ldp x0, x1, [x1] eor x4, x4, x5 eor x2, x3, x2 and x0, x0, x4 and x1, x1, x2 orr x0, x0, x1 cmp x0, 0 cset w0, ne ret
Which at least looks like it might calculate something useful :-)
Cheers,
James
Quoted 44 lines
Quoted 3 lines
That's clearly not right - I'm not sure quite what undefined behaviour assumption convinces GCC to optimise the whole thing away>
While the pointer casting is a bit ghastly, I don't actually think that GCC is taking advantage of undefined behaviour here, rather it looks like you have a simple typo on line 3:
Quoted 3 lines
const __uint128_t *ul1 = (const __uint128_t *)a1; const __uint128_t *ulm = (const __uint128_t *)m; const __uint128_t *ul2 = (const __uint128_t *)a1;ul2 = a2, surely?
As it is (stripping casts) you have a1 ^ a1, which will get you to 0 pretty quickly. Fixing that up for you;
bool ipv6_masked_addr_cmp_new(const struct in6_addr *a1, const struct in6_addr *m, const struct in6_addr *a2) { const __uint128_t *ul1 = (const __uint128_t *)a1; const __uint128_t *ulm = (const __uint128_t *)m; const __uint128_t *ul2 = (const __uint128_t *)a2; return !!((*ul1 ^ *ul2) & *ulm); }$ gcc -O2
ipv6_masked_addr_cmp_new:ldp x4, x3, [x0] ldp x5, x2, [x2] ldp x0, x1, [x1] eor x4, x4, x5 eor x2, x3, x2 and x0, x0, x4 and x1, x1, x2 orr x0, x0, x1 cmp x0, 0 cset w0, ne ret
Quoted 2 lines
Which at least looks like it might calculate something useful :-)
Hi Robin / James
Thanks for checking and sorry for the confusion. I'll retest this.