This is a flavor of full_name_hash() which does not truncate the long hash value to 32-bit in case long is 64-bit. A helper __hash_64() is introduced to multiply with GOLDEN_RATIO_64, just as the the helper __hash_32() multiples with GOLDEN_RATIO_32. This makes the 32 and 64 bit variants of fold_long_hash() look similar. Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx> --- fs/namei.c | 41 ++++++++++++++++++++++++++++++----------- include/linux/hash.h | 10 +++++++++- include/linux/stringhash.h | 6 +++++- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 921ae32dbc80..afe2a8af9ce4 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1804,7 +1804,7 @@ static int walk_component(struct nameidata *nd, int flags) #ifdef HASH_MIX -/* Architecture provides HASH_MIX and fold_hash() in <asm/hash.h> */ +/* Architecture provides HASH_MIX and fold_long_hash() in <asm/hash.h> */ #elif defined(CONFIG_64BIT) /* @@ -1843,15 +1843,13 @@ static int walk_component(struct nameidata *nd, int flags) y *= 9 ) /* - * Fold two longs into one 32-bit hash value. This must be fast, but + * Fold two longs into one long hash value. This must be fast, but * latency isn't quite as critical, as there is a fair bit of additional * work done before the hash value is used. */ -static inline unsigned int fold_hash(unsigned long x, unsigned long y) +static inline unsigned long fold_long_hash(unsigned long x, unsigned long y) { - y ^= x * GOLDEN_RATIO_64; - y *= GOLDEN_RATIO_64; - return y >> 32; + return __hash_64(y ^ __hash_64(x)); } #else /* 32-bit case */ @@ -1872,7 +1870,7 @@ static inline unsigned int fold_hash(unsigned long x, unsigned long y) x += y, y = rol32(y,20),\ y *= 9 ) -static inline unsigned int fold_hash(unsigned long x, unsigned long y) +static inline unsigned long fold_long_hash(unsigned long x, unsigned long y) { /* Use arch-optimized multiply if one exists */ return __hash_32(y ^ __hash_32(x)); @@ -1880,6 +1878,11 @@ static inline unsigned int fold_hash(unsigned long x, unsigned long y) #endif +static inline unsigned int fold_hash(unsigned long x, unsigned long y) +{ + return fold_long_hash(x, y) >> (BITS_PER_LONG - 32); +} + /* * Return the hash of a string of known length. This is carfully * designed to match hash_name(), which is the more critical function. @@ -1887,7 +1890,8 @@ static inline unsigned int fold_hash(unsigned long x, unsigned long y) * payload bytes, to match the way that hash_name() iterates until it * finds the delimiter after the name. */ -unsigned int full_name_hash(const void *salt, const char *name, unsigned int len) +unsigned long full_name_long_hash(const void *salt, const char *name, + unsigned int len) { unsigned long a, x = 0, y = (unsigned long)salt; @@ -1903,7 +1907,14 @@ unsigned int full_name_hash(const void *salt, const char *name, unsigned int len } x ^= a & bytemask_from_count(len); done: - return fold_hash(x, y); + return fold_long_hash(x, y); +} +EXPORT_SYMBOL(full_name_long_hash); + +unsigned int full_name_hash(const void *salt, const char *name, + unsigned int len) +{ + return full_name_long_hash(salt, name, len) >> (BITS_PER_LONG - 32); } EXPORT_SYMBOL(full_name_hash); @@ -1964,12 +1975,20 @@ static inline u64 hash_name(const void *salt, const char *name) #else /* !CONFIG_DCACHE_WORD_ACCESS: Slow, byte-at-a-time version */ /* Return the hash of a string of known length */ -unsigned int full_name_hash(const void *salt, const char *name, unsigned int len) +unsigned long full_name_long_hash(const void *salt, const char *name, + unsigned int len) { unsigned long hash = init_name_hash(salt); while (len--) hash = partial_name_hash((unsigned char)*name++, hash); - return end_name_hash(hash); + return hash; +} +EXPORT_SYMBOL(full_name_long_hash); + +unsigned int full_name_hash(const void *salt, const char *name, + unsigned int len) +{ + return end_name_hash(full_name_long_hash(salt, name, len)); } EXPORT_SYMBOL(full_name_hash); diff --git a/include/linux/hash.h b/include/linux/hash.h index ad6fa21d977b..fb2324f20c22 100644 --- a/include/linux/hash.h +++ b/include/linux/hash.h @@ -71,6 +71,14 @@ static inline u32 hash_32_generic(u32 val, unsigned int bits) return __hash_32(val) >> (32 - bits); } +#ifndef HAVE_ARCH__HASH_64 +#define __hash_64 __hash_64_generic +#endif +static inline u64 __hash_64_generic(u64 val) +{ + return val * GOLDEN_RATIO_64; +} + #ifndef HAVE_ARCH_HASH_64 #define hash_64 hash_64_generic #endif @@ -78,7 +86,7 @@ static __always_inline u32 hash_64_generic(u64 val, unsigned int bits) { #if BITS_PER_LONG == 64 /* 64x64-bit multiply is efficient on all 64-bit processors */ - return val * GOLDEN_RATIO_64 >> (64 - bits); + return __hash_64(val) >> (64 - bits); #else /* Hash 64 bits using only 32x32-bit multiply. */ return hash_32((u32)val ^ __hash_32(val >> 32), bits); diff --git a/include/linux/stringhash.h b/include/linux/stringhash.h index e8f0f852968f..80bdf4bd9c8b 100644 --- a/include/linux/stringhash.h +++ b/include/linux/stringhash.h @@ -63,7 +63,11 @@ static inline unsigned long end_name_hash(unsigned long hash) * * If not set, this falls back to a wrapper around the preceding. */ -extern unsigned int __pure full_name_hash(const void *salt, const char *, unsigned int); +extern unsigned int __pure full_name_hash(const void *salt, const char *name, + unsigned int len); +extern unsigned long __pure full_name_long_hash(const void *salt, + const char *name, + unsigned int len); /* * A hash_len is a u64 with the hash of a string in the low -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe linux-unionfs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html