DF-5146 / fix.diff
diff --git a/sys/dev/drm/include/linux/hash.h b/sys/dev/drm/include/linux/hash.h index 1a2b3c4..5d6e7f8 100644 --- a/sys/dev/drm/include/linux/hash.h +++ b/sys/dev/drm/include/linux/hash.h @@ -29,17 +29,31 @@ #ifndef _LINUX_HASH_H_ #define _LINUX_HASH_H_ -#include <sys/hash.h> +/* + * DF-5146: hash32_buf()'s third argument is an initval (seed), NOT a + * bit count, and its result is a full 32-bit hash. Casting that result + * to u64 and right-shifting by (64 - bits) is identically 0 for every + * bits <= 32, so on 64-bit platforms hash_long(key, order) == 0 for all + * keys and every drm_ht table (drm_hashtab.c) collapses to bucket 0. + * + * Use the canonical 64-bit Fibonacci-hashing mixer (as in Linux) so the + * returned value really is `bits` bits wide. + */ +#include <linux/types.h> static inline u64 hash_64(u64 val, unsigned int bits) { - u64 ret; + u64 hash = val; + u64 n = hash; - ret = (uint64_t)hash32_buf(&val, sizeof(val), bits); + if (bits == 0) + return 0; + if (bits > 64) + bits = 64; - return ret >> (64 - bits); + n <<= 18; hash -= n; + n <<= 33; hash -= n; + n <<= 3; hash += n; + n <<= 3; hash -= n; + n <<= 4; hash += n; + n <<= 2; hash += n; + + /* High bits are more random, so use them. */ + return hash >> (64 - bits); } static inline u32 hash_32(u32 val, unsigned int bits) |