1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4
5#include "../fio.h"
6#include "../gettime.h"
7#include "../fio_time.h"
8#include "../verify.h"
9
10#include "../crc/md5.h"
11#include "../crc/crc64.h"
12#include "../crc/crc32.h"
13#include "../crc/crc32c.h"
14#include "../crc/crc16.h"
15#include "../crc/crc7.h"
16#include "../crc/sha1.h"
17#include "../crc/sha256.h"
18#include "../crc/sha512.h"
19#include "../crc/sha3.h"
20#include "../crc/xxhash.h"
21#include "../crc/murmur3.h"
22#include "../crc/fnv.h"
23#include "../hash.h"
24
25#include "test.h"
26
27#define CHUNK		131072U
28#define NR_CHUNKS	  2048U
29
30struct test_type {
31	const char *name;
32	unsigned int mask;
33	void (*fn)(struct test_type *, void *, size_t);
34	uint32_t output;
35};
36
37enum {
38	T_MD5		= 1U << 0,
39	T_CRC64		= 1U << 1,
40	T_CRC32		= 1U << 2,
41	T_CRC32C	= 1U << 3,
42	T_CRC16		= 1U << 4,
43	T_CRC7		= 1U << 5,
44	T_SHA1		= 1U << 6,
45	T_SHA256	= 1U << 7,
46	T_SHA512	= 1U << 8,
47	T_XXHASH	= 1U << 9,
48	T_MURMUR3	= 1U << 10,
49	T_JHASH		= 1U << 11,
50	T_FNV		= 1U << 12,
51	T_SHA3_224	= 1U << 13,
52	T_SHA3_256	= 1U << 14,
53	T_SHA3_384	= 1U << 15,
54	T_SHA3_512	= 1U << 16,
55};
56
57static void t_md5(struct test_type *t, void *buf, size_t size)
58{
59	uint32_t digest[4];
60	struct fio_md5_ctx ctx = { .hash = digest };
61	int i;
62
63	fio_md5_init(&ctx);
64
65	for (i = 0; i < NR_CHUNKS; i++) {
66		fio_md5_update(&ctx, buf, size);
67		fio_md5_final(&ctx);
68	}
69}
70
71static void t_crc64(struct test_type *t, void *buf, size_t size)
72{
73	int i;
74
75	for (i = 0; i < NR_CHUNKS; i++)
76		t->output += fio_crc64(buf, size);
77}
78
79static void t_crc32(struct test_type *t, void *buf, size_t size)
80{
81	int i;
82
83	for (i = 0; i < NR_CHUNKS; i++)
84		t->output += fio_crc32(buf, size);
85}
86
87static void t_crc32c(struct test_type *t, void *buf, size_t size)
88{
89	int i;
90
91	for (i = 0; i < NR_CHUNKS; i++)
92		t->output += fio_crc32c(buf, size);
93}
94
95static void t_crc16(struct test_type *t, void *buf, size_t size)
96{
97	int i;
98
99	for (i = 0; i < NR_CHUNKS; i++)
100		t->output += fio_crc16(buf, size);
101}
102
103static void t_crc7(struct test_type *t, void *buf, size_t size)
104{
105	int i;
106
107	for (i = 0; i < NR_CHUNKS; i++)
108		t->output += fio_crc7(buf, size);
109}
110
111static void t_sha1(struct test_type *t, void *buf, size_t size)
112{
113	uint32_t sha[5];
114	struct fio_sha1_ctx ctx = { .H = sha };
115	int i;
116
117	fio_sha1_init(&ctx);
118
119	for (i = 0; i < NR_CHUNKS; i++) {
120		fio_sha1_update(&ctx, buf, size);
121		fio_sha1_final(&ctx);
122	}
123}
124
125static void t_sha256(struct test_type *t, void *buf, size_t size)
126{
127	uint8_t sha[64];
128	struct fio_sha256_ctx ctx = { .buf = sha };
129	int i;
130
131	fio_sha256_init(&ctx);
132
133	for (i = 0; i < NR_CHUNKS; i++) {
134		fio_sha256_update(&ctx, buf, size);
135		fio_sha256_final(&ctx);
136	}
137}
138
139static void t_sha512(struct test_type *t, void *buf, size_t size)
140{
141	uint8_t sha[128];
142	struct fio_sha512_ctx ctx = { .buf = sha };
143	int i;
144
145	fio_sha512_init(&ctx);
146
147	for (i = 0; i < NR_CHUNKS; i++)
148		fio_sha512_update(&ctx, buf, size);
149}
150
151static void t_sha3_224(struct test_type *t, void *buf, size_t size)
152{
153	uint8_t sha[SHA3_224_DIGEST_SIZE];
154	struct fio_sha3_ctx ctx = { .sha = sha };
155	int i;
156
157	fio_sha3_224_init(&ctx);
158
159	for (i = 0; i < NR_CHUNKS; i++) {
160		fio_sha3_update(&ctx, buf, size);
161		fio_sha3_final(&ctx);
162	}
163}
164
165static void t_sha3_256(struct test_type *t, void *buf, size_t size)
166{
167	uint8_t sha[SHA3_256_DIGEST_SIZE];
168	struct fio_sha3_ctx ctx = { .sha = sha };
169	int i;
170
171	fio_sha3_256_init(&ctx);
172
173	for (i = 0; i < NR_CHUNKS; i++) {
174		fio_sha3_update(&ctx, buf, size);
175		fio_sha3_final(&ctx);
176	}
177}
178
179static void t_sha3_384(struct test_type *t, void *buf, size_t size)
180{
181	uint8_t sha[SHA3_384_DIGEST_SIZE];
182	struct fio_sha3_ctx ctx = { .sha = sha };
183	int i;
184
185	fio_sha3_384_init(&ctx);
186
187	for (i = 0; i < NR_CHUNKS; i++) {
188		fio_sha3_update(&ctx, buf, size);
189		fio_sha3_final(&ctx);
190	}
191}
192
193static void t_sha3_512(struct test_type *t, void *buf, size_t size)
194{
195	uint8_t sha[SHA3_512_DIGEST_SIZE];
196	struct fio_sha3_ctx ctx = { .sha = sha };
197	int i;
198
199	fio_sha3_512_init(&ctx);
200
201	for (i = 0; i < NR_CHUNKS; i++) {
202		fio_sha3_update(&ctx, buf, size);
203		fio_sha3_final(&ctx);
204	}
205}
206
207static void t_murmur3(struct test_type *t, void *buf, size_t size)
208{
209	int i;
210
211	for (i = 0; i < NR_CHUNKS; i++)
212		t->output += murmurhash3(buf, size, 0x8989);
213}
214
215static void t_jhash(struct test_type *t, void *buf, size_t size)
216{
217	int i;
218
219	for (i = 0; i < NR_CHUNKS; i++)
220		t->output += jhash(buf, size, 0x8989);
221}
222
223static void t_fnv(struct test_type *t, void *buf, size_t size)
224{
225	int i;
226
227	for (i = 0; i < NR_CHUNKS; i++)
228		t->output += fnv(buf, size, 0x8989);
229}
230
231static void t_xxhash(struct test_type *t, void *buf, size_t size)
232{
233	void *state;
234	int i;
235
236	state = XXH32_init(0x8989);
237
238	for (i = 0; i < NR_CHUNKS; i++)
239		XXH32_update(state, buf, size);
240
241	t->output = XXH32_digest(state);
242}
243
244static struct test_type t[] = {
245	{
246		.name = "md5",
247		.mask = T_MD5,
248		.fn = t_md5,
249	},
250	{
251		.name = "crc64",
252		.mask = T_CRC64,
253		.fn = t_crc64,
254	},
255	{
256		.name = "crc32",
257		.mask = T_CRC32,
258		.fn = t_crc32,
259	},
260	{
261		.name = "crc32c",
262		.mask = T_CRC32C,
263		.fn = t_crc32c,
264	},
265	{
266		.name = "crc16",
267		.mask = T_CRC16,
268		.fn = t_crc16,
269	},
270	{
271		.name = "crc7",
272		.mask = T_CRC7,
273		.fn = t_crc7,
274	},
275	{
276		.name = "sha1",
277		.mask = T_SHA1,
278		.fn = t_sha1,
279	},
280	{
281		.name = "sha256",
282		.mask = T_SHA256,
283		.fn = t_sha256,
284	},
285	{
286		.name = "sha512",
287		.mask = T_SHA512,
288		.fn = t_sha512,
289	},
290	{
291		.name = "xxhash",
292		.mask = T_XXHASH,
293		.fn = t_xxhash,
294	},
295	{
296		.name = "murmur3",
297		.mask = T_MURMUR3,
298		.fn = t_murmur3,
299	},
300	{
301		.name = "jhash",
302		.mask = T_JHASH,
303		.fn = t_jhash,
304	},
305	{
306		.name = "fnv",
307		.mask = T_FNV,
308		.fn = t_fnv,
309	},
310	{
311		.name = "sha3-224",
312		.mask = T_SHA3_224,
313		.fn = t_sha3_224,
314	},
315	{
316		.name = "sha3-256",
317		.mask = T_SHA3_256,
318		.fn = t_sha3_256,
319	},
320	{
321		.name = "sha3-384",
322		.mask = T_SHA3_384,
323		.fn = t_sha3_384,
324	},
325	{
326		.name = "sha3-512",
327		.mask = T_SHA3_512,
328		.fn = t_sha3_512,
329	},
330	{
331		.name = NULL,
332	},
333};
334
335static unsigned int get_test_mask(const char *type)
336{
337	char *ostr, *str = strdup(type);
338	unsigned int mask;
339	char *name;
340	int i;
341
342	ostr = str;
343	mask = 0;
344	while ((name = strsep(&str, ",")) != NULL) {
345		for (i = 0; t[i].name; i++) {
346			if (!strcmp(t[i].name, name)) {
347				mask |= t[i].mask;
348				break;
349			}
350		}
351	}
352
353	free(ostr);
354	return mask;
355}
356
357static int list_types(void)
358{
359	int i;
360
361	for (i = 0; t[i].name; i++)
362		printf("%s\n", t[i].name);
363
364	return 1;
365}
366
367int fio_crctest(const char *type)
368{
369	unsigned int test_mask = 0;
370	uint64_t mb = CHUNK * NR_CHUNKS;
371	struct frand_state state;
372	int i, first = 1;
373	void *buf;
374
375	crc32c_arm64_probe();
376	crc32c_intel_probe();
377
378	if (!type)
379		test_mask = ~0U;
380	else if (!strcmp(type, "help") || !strcmp(type, "list"))
381		return list_types();
382	else
383		test_mask = get_test_mask(type);
384
385	if (!test_mask) {
386		fprintf(stderr, "fio: unknown hash `%s`. Available:\n", type);
387		return list_types();
388	}
389
390	buf = malloc(CHUNK);
391	init_rand_seed(&state, 0x8989, 0);
392	fill_random_buf(&state, buf, CHUNK);
393
394	for (i = 0; t[i].name; i++) {
395		struct timeval tv;
396		double mb_sec;
397		uint64_t usec;
398		char pre[3];
399
400		if (!(t[i].mask & test_mask))
401			continue;
402
403		/*
404		 * For first run, make sure CPUs are spun up and that
405		 * we've touched the data.
406		 */
407		if (first) {
408			usec_spin(100000);
409			t[i].fn(&t[i], buf, CHUNK);
410		}
411
412		fio_gettime(&tv, NULL);
413		t[i].fn(&t[i], buf, CHUNK);
414		usec = utime_since_now(&tv);
415
416		if (usec) {
417			mb_sec = (double) mb / (double) usec;
418			mb_sec /= (1.024 * 1.024);
419			if (strlen(t[i].name) >= 7)
420				sprintf(pre, "\t");
421			else
422				sprintf(pre, "\t\t");
423			printf("%s:%s%8.2f MiB/sec\n", t[i].name, pre, mb_sec);
424		} else
425			printf("%s:inf MiB/sec\n", t[i].name);
426		first = 0;
427	}
428
429	free(buf);
430	return 0;
431}
432