Commit b483027a authored by Daniel Verkamp's avatar Daniel Verkamp
Browse files

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: default avatarDaniel Verkamp <daniel.verkamp@intel.com>
Reviewed-on: https://review.gerrithub.io/370766


Tested-by: default avatarSPDK Automated Test System <sys_sgsw@intel.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
parent ae60710a
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
@@ -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
+72 −1
Original line number Diff line number Diff line
@@ -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