diff options
author | Mike Pattrick <mkp@redhat.com> | 2023-01-30 17:04:17 -0500 |
---|---|---|
committer | Ilya Maximets <i.maximets@ovn.org> | 2023-02-03 22:18:16 +0100 |
commit | e85e8a7541cbe7d9c57bd34ae99d47edad92f111 (patch) | |
tree | c600cb6f152101db7a0b4267076937f3cc5a5bdf /lib | |
parent | 4339e7b19f721d6f6813118effb47144f4a2aade (diff) | |
download | openvswitch-e85e8a7541cbe7d9c57bd34ae99d47edad92f111.tar.gz |
hash: Avoid 64bit crc intrinsics on 32bit aligned data.
UB Sanitizer report:
lib/hash.h:219:17: runtime error: load of misaligned address
0x7ffc164a88b4 for type 'const uint64_t', which requires 8 byte
alignment
#0 in hash_words_inline lib/hash.h:219
#1 in hash_words lib/hash.h:297
[...]
Signed-off-by: Mike Pattrick <mkp@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/hash.h | 65 |
1 files changed, 64 insertions, 1 deletions
diff --git a/lib/hash.h b/lib/hash.h index 60a39a40b..7b7f70c11 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -187,13 +187,72 @@ static inline uint32_t hash_finish(uint64_t hash, uint64_t final) return hash ^ (uint32_t)hash >> 16; /* Increase entropy in LSBs. */ } +static inline uint32_t +hash_finish32(uint64_t hash, uint32_t final, uint32_t semifinal) +{ + /* The finishing multiplier 0x805204f3 has been experimentally + * derived to pass the testsuite hash tests. */ + hash = _mm_crc32_u32(hash, semifinal); + hash = _mm_crc32_u32(hash, final) * 0x805204f3; + return hash ^ ((uint32_t) hash >> 16); /* Increase entropy in LSBs. */ +} + +static inline uint32_t +hash_words_32aligned(const uint32_t *p_, size_t n_words, uint32_t basis) +{ + const uint32_t *p = (const void *) p_; + uint32_t hash1 = basis; + uint32_t hash2 = 0; + uint32_t hash3 = n_words; + const uint32_t *endp = (const uint32_t *) p + n_words; + const uint32_t *limit = p + n_words - 6; + + while (p <= limit) { + hash1 = _mm_crc32_u32(hash1, p[0]); + hash1 = _mm_crc32_u32(hash1, p[1]); + hash2 = _mm_crc32_u32(hash2, p[2]); + hash2 = _mm_crc32_u32(hash2, p[3]); + hash3 = _mm_crc32_u32(hash3, p[4]); + hash3 = _mm_crc32_u32(hash3, p[5]); + p += 6; + } + switch (endp - (const uint32_t *) p) { + case 1: + hash1 = _mm_crc32_u32(hash1, p[0]); + break; + case 2: + hash1 = _mm_crc32_u32(hash1, p[0]); + hash1 = _mm_crc32_u32(hash1, p[1]); + break; + case 3: + hash1 = _mm_crc32_u32(hash1, p[0]); + hash1 = _mm_crc32_u32(hash1, p[1]); + hash2 = _mm_crc32_u32(hash2, p[2]); + break; + case 4: + hash1 = _mm_crc32_u32(hash1, p[0]); + hash1 = _mm_crc32_u32(hash1, p[1]); + hash2 = _mm_crc32_u32(hash2, p[2]); + hash2 = _mm_crc32_u32(hash2, p[3]); + break; + case 5: + hash1 = _mm_crc32_u32(hash1, p[0]); + hash1 = _mm_crc32_u32(hash1, p[1]); + hash2 = _mm_crc32_u32(hash2, p[2]); + hash2 = _mm_crc32_u32(hash2, p[3]); + hash3 = _mm_crc32_u32(hash3, p[4]); + break; + } + return hash_finish32(hash1, hash2, hash3); +} + /* Returns the hash of the 'n' 32-bit words at 'p_', starting from 'basis'. * We access 'p_' as a uint64_t pointer, which is fine for __SSE_4_2__. * * This is inlined for the compiler to have access to the 'n_words', which * in many cases is a constant. */ static inline uint32_t -hash_words_inline(const uint32_t p_[], size_t n_words, uint32_t basis) +hash_words_inline(const uint32_t *p_, size_t n_words, uint32_t basis) { const uint64_t *p = (const void *)p_; uint64_t hash1 = basis; @@ -202,6 +261,10 @@ hash_words_inline(const uint32_t p_[], size_t n_words, uint32_t basis) const uint32_t *endp = (const uint32_t *)p + n_words; const uint64_t *limit = p + n_words / 2 - 3; + if (OVS_UNLIKELY(((intptr_t) p & ((sizeof(uint64_t)) - 1)) != 0)) { + return hash_words_32aligned(p_, n_words, basis); + } + while (p <= limit) { hash1 = _mm_crc32_u64(hash1, p[0]); hash2 = _mm_crc32_u64(hash2, p[1]); |