diff options
author | Jay Vosburgh <jay.vosburgh@canonical.com> | 2014-11-14 14:05:06 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-11-14 16:36:25 -0500 |
commit | a77f9c5dcdf8480a93332792c336fa2bf9d31229 (patch) | |
tree | 003964de0cc772f361d254385406fa75f3fcbff2 | |
parent | 8cd4313aa775537f724486d5b7503b4d46c9f012 (diff) |
Revert "fast_hash: avoid indirect function calls"
This reverts commit e5a2c899957659cd1a9f789bc462f9c0b35f5150.
Commit e5a2c899 introduced an alternative_call, arch_fast_hash2,
that selects between __jhash2 and __intel_crc4_2_hash based on the
X86_FEATURE_XMM4_2.
Unfortunately, the alternative_call system does not appear to be
suitable for use with C functions, as register usage is not handled
properly for the called functions. The __jhash2 function in particular
clobbers registers that are not preserved when called via
alternative_call, resulting in a panic for direct callers of
arch_fast_hash2 on older CPUs lacking sse4_2. It is possible that
__intel_crc4_2_hash works merely by chance because it uses fewer
registers.
This commit was suggested as the source of the problem by Jesse
Gross <jesse@nicira.com>.
Signed-off-by: Jay Vosburgh <jay.vosburgh@canonical.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | arch/x86/include/asm/hash.h | 51 | ||||
-rw-r--r-- | arch/x86/lib/hash.c | 29 | ||||
-rw-r--r-- | include/asm-generic/hash.h | 36 | ||||
-rw-r--r-- | include/linux/hash.h | 34 | ||||
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/hash.c | 39 |
6 files changed, 93 insertions, 98 deletions
diff --git a/arch/x86/include/asm/hash.h b/arch/x86/include/asm/hash.h index a881d784f044..e8c58f88b1d4 100644 --- a/arch/x86/include/asm/hash.h +++ b/arch/x86/include/asm/hash.h | |||
@@ -1,48 +1,7 @@ | |||
1 | #ifndef __ASM_X86_HASH_H | 1 | #ifndef _ASM_X86_HASH_H |
2 | #define __ASM_X86_HASH_H | 2 | #define _ASM_X86_HASH_H |
3 | 3 | ||
4 | #include <linux/cpufeature.h> | 4 | struct fast_hash_ops; |
5 | #include <asm/alternative.h> | 5 | extern void setup_arch_fast_hash(struct fast_hash_ops *ops); |
6 | 6 | ||
7 | u32 __intel_crc4_2_hash(const void *data, u32 len, u32 seed); | 7 | #endif /* _ASM_X86_HASH_H */ |
8 | u32 __intel_crc4_2_hash2(const u32 *data, u32 len, u32 seed); | ||
9 | |||
10 | /* | ||
11 | * non-inline versions of jhash so gcc does not need to generate | ||
12 | * duplicate code in every object file | ||
13 | */ | ||
14 | u32 __jhash(const void *data, u32 len, u32 seed); | ||
15 | u32 __jhash2(const u32 *data, u32 len, u32 seed); | ||
16 | |||
17 | /* | ||
18 | * for documentation of these functions please look into | ||
19 | * <include/asm-generic/hash.h> | ||
20 | */ | ||
21 | |||
22 | static inline u32 arch_fast_hash(const void *data, u32 len, u32 seed) | ||
23 | { | ||
24 | u32 hash; | ||
25 | |||
26 | alternative_call(__jhash, __intel_crc4_2_hash, X86_FEATURE_XMM4_2, | ||
27 | #ifdef CONFIG_X86_64 | ||
28 | "=a" (hash), "D" (data), "S" (len), "d" (seed)); | ||
29 | #else | ||
30 | "=a" (hash), "a" (data), "d" (len), "c" (seed)); | ||
31 | #endif | ||
32 | return hash; | ||
33 | } | ||
34 | |||
35 | static inline u32 arch_fast_hash2(const u32 *data, u32 len, u32 seed) | ||
36 | { | ||
37 | u32 hash; | ||
38 | |||
39 | alternative_call(__jhash2, __intel_crc4_2_hash2, X86_FEATURE_XMM4_2, | ||
40 | #ifdef CONFIG_X86_64 | ||
41 | "=a" (hash), "D" (data), "S" (len), "d" (seed)); | ||
42 | #else | ||
43 | "=a" (hash), "a" (data), "d" (len), "c" (seed)); | ||
44 | #endif | ||
45 | return hash; | ||
46 | } | ||
47 | |||
48 | #endif /* __ASM_X86_HASH_H */ | ||
diff --git a/arch/x86/lib/hash.c b/arch/x86/lib/hash.c index e14327198835..ff4fa51a5b1f 100644 --- a/arch/x86/lib/hash.c +++ b/arch/x86/lib/hash.c | |||
@@ -31,13 +31,13 @@ | |||
31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
32 | */ | 32 | */ |
33 | 33 | ||
34 | #include <linux/hash.h> | ||
35 | #include <linux/init.h> | ||
36 | |||
34 | #include <asm/processor.h> | 37 | #include <asm/processor.h> |
35 | #include <asm/cpufeature.h> | 38 | #include <asm/cpufeature.h> |
36 | #include <asm/hash.h> | 39 | #include <asm/hash.h> |
37 | 40 | ||
38 | #include <linux/hash.h> | ||
39 | #include <linux/jhash.h> | ||
40 | |||
41 | static inline u32 crc32_u32(u32 crc, u32 val) | 41 | static inline u32 crc32_u32(u32 crc, u32 val) |
42 | { | 42 | { |
43 | #ifdef CONFIG_AS_CRC32 | 43 | #ifdef CONFIG_AS_CRC32 |
@@ -48,7 +48,7 @@ static inline u32 crc32_u32(u32 crc, u32 val) | |||
48 | return crc; | 48 | return crc; |
49 | } | 49 | } |
50 | 50 | ||
51 | u32 __intel_crc4_2_hash(const void *data, u32 len, u32 seed) | 51 | static u32 intel_crc4_2_hash(const void *data, u32 len, u32 seed) |
52 | { | 52 | { |
53 | const u32 *p32 = (const u32 *) data; | 53 | const u32 *p32 = (const u32 *) data; |
54 | u32 i, tmp = 0; | 54 | u32 i, tmp = 0; |
@@ -71,27 +71,22 @@ u32 __intel_crc4_2_hash(const void *data, u32 len, u32 seed) | |||
71 | 71 | ||
72 | return seed; | 72 | return seed; |
73 | } | 73 | } |
74 | EXPORT_SYMBOL(__intel_crc4_2_hash); | ||
75 | 74 | ||
76 | u32 __intel_crc4_2_hash2(const u32 *data, u32 len, u32 seed) | 75 | static u32 intel_crc4_2_hash2(const u32 *data, u32 len, u32 seed) |
77 | { | 76 | { |
77 | const u32 *p32 = (const u32 *) data; | ||
78 | u32 i; | 78 | u32 i; |
79 | 79 | ||
80 | for (i = 0; i < len; i++) | 80 | for (i = 0; i < len; i++) |
81 | seed = crc32_u32(seed, *data++); | 81 | seed = crc32_u32(seed, *p32++); |
82 | 82 | ||
83 | return seed; | 83 | return seed; |
84 | } | 84 | } |
85 | EXPORT_SYMBOL(__intel_crc4_2_hash2); | ||
86 | 85 | ||
87 | u32 __jhash(const void *data, u32 len, u32 seed) | 86 | void __init setup_arch_fast_hash(struct fast_hash_ops *ops) |
88 | { | 87 | { |
89 | return jhash(data, len, seed); | 88 | if (cpu_has_xmm4_2) { |
90 | } | 89 | ops->hash = intel_crc4_2_hash; |
91 | EXPORT_SYMBOL(__jhash); | 90 | ops->hash2 = intel_crc4_2_hash2; |
92 | 91 | } | |
93 | u32 __jhash2(const u32 *data, u32 len, u32 seed) | ||
94 | { | ||
95 | return jhash2(data, len, seed); | ||
96 | } | 92 | } |
97 | EXPORT_SYMBOL(__jhash2); | ||
diff --git a/include/asm-generic/hash.h b/include/asm-generic/hash.h index 3c82760ff2a4..b6312843dbd9 100644 --- a/include/asm-generic/hash.h +++ b/include/asm-generic/hash.h | |||
@@ -1,41 +1,9 @@ | |||
1 | #ifndef __ASM_GENERIC_HASH_H | 1 | #ifndef __ASM_GENERIC_HASH_H |
2 | #define __ASM_GENERIC_HASH_H | 2 | #define __ASM_GENERIC_HASH_H |
3 | 3 | ||
4 | #include <linux/jhash.h> | 4 | struct fast_hash_ops; |
5 | 5 | static inline void setup_arch_fast_hash(struct fast_hash_ops *ops) | |
6 | /** | ||
7 | * arch_fast_hash - Caclulates a hash over a given buffer that can have | ||
8 | * arbitrary size. This function will eventually use an | ||
9 | * architecture-optimized hashing implementation if | ||
10 | * available, and trades off distribution for speed. | ||
11 | * | ||
12 | * @data: buffer to hash | ||
13 | * @len: length of buffer in bytes | ||
14 | * @seed: start seed | ||
15 | * | ||
16 | * Returns 32bit hash. | ||
17 | */ | ||
18 | static inline u32 arch_fast_hash(const void *data, u32 len, u32 seed) | ||
19 | { | ||
20 | return jhash(data, len, seed); | ||
21 | } | ||
22 | |||
23 | /** | ||
24 | * arch_fast_hash2 - Caclulates a hash over a given buffer that has a | ||
25 | * size that is of a multiple of 32bit words. This | ||
26 | * function will eventually use an architecture- | ||
27 | * optimized hashing implementation if available, | ||
28 | * and trades off distribution for speed. | ||
29 | * | ||
30 | * @data: buffer to hash (must be 32bit padded) | ||
31 | * @len: number of 32bit words | ||
32 | * @seed: start seed | ||
33 | * | ||
34 | * Returns 32bit hash. | ||
35 | */ | ||
36 | static inline u32 arch_fast_hash2(const u32 *data, u32 len, u32 seed) | ||
37 | { | 6 | { |
38 | return jhash2(data, len, seed); | ||
39 | } | 7 | } |
40 | 8 | ||
41 | #endif /* __ASM_GENERIC_HASH_H */ | 9 | #endif /* __ASM_GENERIC_HASH_H */ |
diff --git a/include/linux/hash.h b/include/linux/hash.h index 6e8fb028848c..d0494c399392 100644 --- a/include/linux/hash.h +++ b/include/linux/hash.h | |||
@@ -84,4 +84,38 @@ static inline u32 hash32_ptr(const void *ptr) | |||
84 | return (u32)val; | 84 | return (u32)val; |
85 | } | 85 | } |
86 | 86 | ||
87 | struct fast_hash_ops { | ||
88 | u32 (*hash)(const void *data, u32 len, u32 seed); | ||
89 | u32 (*hash2)(const u32 *data, u32 len, u32 seed); | ||
90 | }; | ||
91 | |||
92 | /** | ||
93 | * arch_fast_hash - Caclulates a hash over a given buffer that can have | ||
94 | * arbitrary size. This function will eventually use an | ||
95 | * architecture-optimized hashing implementation if | ||
96 | * available, and trades off distribution for speed. | ||
97 | * | ||
98 | * @data: buffer to hash | ||
99 | * @len: length of buffer in bytes | ||
100 | * @seed: start seed | ||
101 | * | ||
102 | * Returns 32bit hash. | ||
103 | */ | ||
104 | extern u32 arch_fast_hash(const void *data, u32 len, u32 seed); | ||
105 | |||
106 | /** | ||
107 | * arch_fast_hash2 - Caclulates a hash over a given buffer that has a | ||
108 | * size that is of a multiple of 32bit words. This | ||
109 | * function will eventually use an architecture- | ||
110 | * optimized hashing implementation if available, | ||
111 | * and trades off distribution for speed. | ||
112 | * | ||
113 | * @data: buffer to hash (must be 32bit padded) | ||
114 | * @len: number of 32bit words | ||
115 | * @seed: start seed | ||
116 | * | ||
117 | * Returns 32bit hash. | ||
118 | */ | ||
119 | extern u32 arch_fast_hash2(const u32 *data, u32 len, u32 seed); | ||
120 | |||
87 | #endif /* _LINUX_HASH_H */ | 121 | #endif /* _LINUX_HASH_H */ |
diff --git a/lib/Makefile b/lib/Makefile index 04e53dd16070..7512dc978f18 100644 --- a/lib/Makefile +++ b/lib/Makefile | |||
@@ -26,7 +26,7 @@ obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ | |||
26 | bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \ | 26 | bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \ |
27 | gcd.o lcm.o list_sort.o uuid.o flex_array.o iovec.o clz_ctz.o \ | 27 | gcd.o lcm.o list_sort.o uuid.o flex_array.o iovec.o clz_ctz.o \ |
28 | bsearch.o find_last_bit.o find_next_bit.o llist.o memweight.o kfifo.o \ | 28 | bsearch.o find_last_bit.o find_next_bit.o llist.o memweight.o kfifo.o \ |
29 | percpu-refcount.o percpu_ida.o rhashtable.o | 29 | percpu-refcount.o percpu_ida.o hash.o rhashtable.o |
30 | obj-y += string_helpers.o | 30 | obj-y += string_helpers.o |
31 | obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o | 31 | obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o |
32 | obj-y += kstrtox.o | 32 | obj-y += kstrtox.o |
diff --git a/lib/hash.c b/lib/hash.c new file mode 100644 index 000000000000..fea973f4bd57 --- /dev/null +++ b/lib/hash.c | |||
@@ -0,0 +1,39 @@ | |||
1 | /* General purpose hashing library | ||
2 | * | ||
3 | * That's a start of a kernel hashing library, which can be extended | ||
4 | * with further algorithms in future. arch_fast_hash{2,}() will | ||
5 | * eventually resolve to an architecture optimized implementation. | ||
6 | * | ||
7 | * Copyright 2013 Francesco Fusco <ffusco@redhat.com> | ||
8 | * Copyright 2013 Daniel Borkmann <dborkman@redhat.com> | ||
9 | * Copyright 2013 Thomas Graf <tgraf@redhat.com> | ||
10 | * Licensed under the GNU General Public License, version 2.0 (GPLv2) | ||
11 | */ | ||
12 | |||
13 | #include <linux/jhash.h> | ||
14 | #include <linux/hash.h> | ||
15 | #include <linux/cache.h> | ||
16 | |||
17 | static struct fast_hash_ops arch_hash_ops __read_mostly = { | ||
18 | .hash = jhash, | ||
19 | .hash2 = jhash2, | ||
20 | }; | ||
21 | |||
22 | u32 arch_fast_hash(const void *data, u32 len, u32 seed) | ||
23 | { | ||
24 | return arch_hash_ops.hash(data, len, seed); | ||
25 | } | ||
26 | EXPORT_SYMBOL_GPL(arch_fast_hash); | ||
27 | |||
28 | u32 arch_fast_hash2(const u32 *data, u32 len, u32 seed) | ||
29 | { | ||
30 | return arch_hash_ops.hash2(data, len, seed); | ||
31 | } | ||
32 | EXPORT_SYMBOL_GPL(arch_fast_hash2); | ||
33 | |||
34 | static int __init hashlib_init(void) | ||
35 | { | ||
36 | setup_arch_fast_hash(&arch_hash_ops); | ||
37 | return 0; | ||
38 | } | ||
39 | early_initcall(hashlib_init); | ||