summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMike Pattrick <mkp@redhat.com>2023-01-30 17:04:17 -0500
committerIlya Maximets <i.maximets@ovn.org>2023-02-03 22:18:16 +0100
commite85e8a7541cbe7d9c57bd34ae99d47edad92f111 (patch)
treec600cb6f152101db7a0b4267076937f3cc5a5bdf /lib
parent4339e7b19f721d6f6813118effb47144f4a2aade (diff)
downloadopenvswitch-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.h65
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]);