util/crc32c: add SSE 4.2 CRC32 instruction support

Use the CRC32 CPU instruction, if available, to optimize the iSCSI
CRC-32C calculation.

Change-Id: Ifb706528c28f5e6921ebf525274b959d8cac85a0
Signed-off-by: Daniel Verkamp <daniel.verkamp@intel.com>
Reviewed-on: https://review.gerrithub.io/370766
Tested-by: SPDK Automated Test System <sys_sgsw@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
This commit is contained in:
Daniel Verkamp 2017-07-21 16:51:25 -07:00
parent ae60710ab8
commit b483027aca
2 changed files with 113 additions and 1 deletions

View File

@ -33,6 +33,45 @@
#include "spdk/crc32.h"
#if defined(__x86_64__) && defined(__SSE4_2__)
#include <x86intrin.h>
uint32_t
spdk_crc32c_update(const void *buf, size_t len, uint32_t crc)
{
uint64_t crc_tmp64;
size_t count;
/* _mm_crc32_u64() needs a 64-bit intermediate value */
crc_tmp64 = crc;
/* Process as much of the buffer as possible in 64-bit blocks. */
count = len / 8;
while (count--) {
uint64_t block;
/*
* Use memcpy() to avoid unaligned loads, which are undefined behavior in C.
* The compiler will optimize out the memcpy() in release builds.
*/
memcpy(&block, buf, sizeof(block));
crc_tmp64 = _mm_crc32_u64(crc_tmp64, block);
buf += sizeof(block);
}
crc = (uint32_t)crc_tmp64;
/* Handle any trailing bytes. */
count = len & 7;
while (count--) {
crc = _mm_crc32_u8(crc, *(const uint8_t *)buf);
buf++;
}
return crc;
}
#else /* SSE 4.2 (CRC32 instruction) not available */
static struct spdk_crc32_table g_crc32c_table;
__attribute__((constructor)) static void
@ -46,3 +85,5 @@ spdk_crc32c_update(const void *buf, size_t len, uint32_t crc)
{
return spdk_crc32_update(&g_crc32c_table, buf, len, crc);
}
#endif

View File

@ -42,12 +42,83 @@ static void
test_crc32c(void)
{
uint32_t crc;
char buf[] = "Hello world!";
char buf[1024];
/* Verify a string's CRC32-C value against the known correct result. */
snprintf(buf, sizeof(buf), "%s", "Hello world!");
crc = 0xFFFFFFFFu;
crc = spdk_crc32c_update(buf, strlen(buf), crc);
crc ^= 0xFFFFFFFFu;
CU_ASSERT(crc == 0x7b98e751);
/*
* The main loop of the optimized CRC32-C implementation processes data in 8-byte blocks,
* followed by a loop to handle the 0-7 trailing bytes.
* Test all buffer sizes from 0 to 7 in order to hit all possible trailing byte counts.
*/
/* 0-byte buffer should not modify CRC at all, so final result should be ~0 ^ ~0 == 0 */
snprintf(buf, sizeof(buf), "%s", "");
crc = 0xFFFFFFFFu;
crc = spdk_crc32c_update(buf, strlen(buf), crc);
crc ^= 0xFFFFFFFFu;
CU_ASSERT(crc == 0);
/* 1-byte buffer */
snprintf(buf, sizeof(buf), "%s", "1");
crc = 0xFFFFFFFFu;
crc = spdk_crc32c_update(buf, strlen(buf), crc);
crc ^= 0xFFFFFFFFu;
CU_ASSERT(crc == 0x90F599E3);
/* 2-byte buffer */
snprintf(buf, sizeof(buf), "%s", "12");
crc = 0xFFFFFFFFu;
crc = spdk_crc32c_update(buf, strlen(buf), crc);
crc ^= 0xFFFFFFFFu;
CU_ASSERT(crc == 0x7355C460);
/* 3-byte buffer */
snprintf(buf, sizeof(buf), "%s", "123");
crc = 0xFFFFFFFFu;
crc = spdk_crc32c_update(buf, strlen(buf), crc);
crc ^= 0xFFFFFFFFu;
CU_ASSERT(crc == 0x107B2FB2);
/* 4-byte buffer */
snprintf(buf, sizeof(buf), "%s", "1234");
crc = 0xFFFFFFFFu;
crc = spdk_crc32c_update(buf, strlen(buf), crc);
crc ^= 0xFFFFFFFFu;
CU_ASSERT(crc == 0xF63AF4EE);
/* 5-byte buffer */
snprintf(buf, sizeof(buf), "%s", "12345");
crc = 0xFFFFFFFFu;
crc = spdk_crc32c_update(buf, strlen(buf), crc);
crc ^= 0xFFFFFFFFu;
CU_ASSERT(crc == 0x18D12335);
/* 6-byte buffer */
snprintf(buf, sizeof(buf), "%s", "123456");
crc = 0xFFFFFFFFu;
crc = spdk_crc32c_update(buf, strlen(buf), crc);
crc ^= 0xFFFFFFFFu;
CU_ASSERT(crc == 0x41357186);
/* 7-byte buffer */
snprintf(buf, sizeof(buf), "%s", "1234567");
crc = 0xFFFFFFFFu;
crc = spdk_crc32c_update(buf, strlen(buf), crc);
crc ^= 0xFFFFFFFFu;
CU_ASSERT(crc == 0x124297EA);
/* Test a buffer of exactly 8 bytes (one block in the main CRC32-C loop). */
snprintf(buf, sizeof(buf), "%s", "12345678");
crc = 0xFFFFFFFFu;
crc = spdk_crc32c_update(buf, strlen(buf), crc);
crc ^= 0xFFFFFFFFu;
CU_ASSERT(crc == 0x6087809A);
}
int