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 | |||
