xref: /OK3568_Linux_fs/kernel/tools/testing/selftests/filesystems/incfs/incfs_perf.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright 2020 Google LLC
4*4882a593Smuzhiyun  */
5*4882a593Smuzhiyun #include <errno.h>
6*4882a593Smuzhiyun #include <fcntl.h>
7*4882a593Smuzhiyun #include <getopt.h>
8*4882a593Smuzhiyun #include <lz4.h>
9*4882a593Smuzhiyun #include <stdbool.h>
10*4882a593Smuzhiyun #include <stdint.h>
11*4882a593Smuzhiyun #include <stdio.h>
12*4882a593Smuzhiyun #include <stdlib.h>
13*4882a593Smuzhiyun #include <string.h>
14*4882a593Smuzhiyun #include <sys/mount.h>
15*4882a593Smuzhiyun #include <sys/stat.h>
16*4882a593Smuzhiyun #include <time.h>
17*4882a593Smuzhiyun #include <ctype.h>
18*4882a593Smuzhiyun #include <unistd.h>
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #include "utils.h"
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun #define err_msg(...)                                                           \
23*4882a593Smuzhiyun 	do {                                                                   \
24*4882a593Smuzhiyun 		fprintf(stderr, "%s: (%d) ", TAG, __LINE__);                   \
25*4882a593Smuzhiyun 		fprintf(stderr, __VA_ARGS__);                                  \
26*4882a593Smuzhiyun 		fprintf(stderr, " (%s)\n", strerror(errno));                   \
27*4882a593Smuzhiyun 	} while (false)
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun #define TAG "incfs_perf"
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun struct options {
32*4882a593Smuzhiyun 	int blocks; /* -b number of diff block sizes */
33*4882a593Smuzhiyun 	bool no_cleanup; /* -c don't clean up after */
34*4882a593Smuzhiyun 	const char *test_dir; /* -d working directory */
35*4882a593Smuzhiyun 	const char *file_types; /* -f sScCvV */
36*4882a593Smuzhiyun 	bool no_native; /* -n don't test native files */
37*4882a593Smuzhiyun 	bool no_random; /* -r don't do random reads*/
38*4882a593Smuzhiyun 	bool no_linear; /* -R random reads only */
39*4882a593Smuzhiyun 	size_t size; /* -s file size as power of 2 */
40*4882a593Smuzhiyun 	int tries; /* -t times to run test*/
41*4882a593Smuzhiyun };
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun enum flags {
44*4882a593Smuzhiyun 	SHUFFLE = 1,
45*4882a593Smuzhiyun 	COMPRESS = 2,
46*4882a593Smuzhiyun 	VERIFY = 4,
47*4882a593Smuzhiyun 	LAST_FLAG = 8,
48*4882a593Smuzhiyun };
49*4882a593Smuzhiyun 
print_help(void)50*4882a593Smuzhiyun void print_help(void)
51*4882a593Smuzhiyun {
52*4882a593Smuzhiyun 	puts(
53*4882a593Smuzhiyun 	"incfs_perf. Performance test tool for incfs\n"
54*4882a593Smuzhiyun 	"\tTests read performance of incfs by creating files of various types\n"
55*4882a593Smuzhiyun 	"\tflushing caches and then reading them back.\n"
56*4882a593Smuzhiyun 	"\tEach file is read with different block sizes and average\n"
57*4882a593Smuzhiyun 	"\tthroughput in megabytes/second and memory usage are reported for\n"
58*4882a593Smuzhiyun 	"\teach block size\n"
59*4882a593Smuzhiyun 	"\tNative files are tested for comparison\n"
60*4882a593Smuzhiyun 	"\tNative files are created in native folder, incfs files are created\n"
61*4882a593Smuzhiyun 	"\tin src folder which is mounted on dst folder\n"
62*4882a593Smuzhiyun 	"\n"
63*4882a593Smuzhiyun 	"\t-bn (default 8) number of different block sizes, starting at 4096\n"
64*4882a593Smuzhiyun 	"\t                and doubling\n"
65*4882a593Smuzhiyun 	"\t-c		   don't Clean up - leave files and mount point\n"
66*4882a593Smuzhiyun 	"\t-d dir          create directories in dir\n"
67*4882a593Smuzhiyun 	"\t-fs|Sc|Cv|V     restrict which files are created.\n"
68*4882a593Smuzhiyun 	"\t                s blocks not shuffled, S blocks shuffled\n"
69*4882a593Smuzhiyun 	"\t                c blocks not compress, C blocks compressed\n"
70*4882a593Smuzhiyun 	"\t                v files not verified, V files verified\n"
71*4882a593Smuzhiyun 	"\t                If a letter is omitted, both options are tested\n"
72*4882a593Smuzhiyun 	"\t                If no letter are given, incfs is not tested\n"
73*4882a593Smuzhiyun 	"\t-n              Don't test native files\n"
74*4882a593Smuzhiyun 	"\t-r              No random reads (sequential only)\n"
75*4882a593Smuzhiyun 	"\t-R              Random reads only (no sequential)\n"
76*4882a593Smuzhiyun 	"\t-sn (default 30)File size as power of 2\n"
77*4882a593Smuzhiyun 	"\t-tn (default 5) Number of tries per file. Results are averaged\n"
78*4882a593Smuzhiyun 	);
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun 
parse_options(int argc,char * const * argv,struct options * options)81*4882a593Smuzhiyun int parse_options(int argc, char *const *argv, struct options *options)
82*4882a593Smuzhiyun {
83*4882a593Smuzhiyun 	signed char c;
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	/* Set defaults here */
86*4882a593Smuzhiyun 	*options = (struct options){
87*4882a593Smuzhiyun 		.blocks = 8,
88*4882a593Smuzhiyun 		.test_dir = ".",
89*4882a593Smuzhiyun 		.tries = 5,
90*4882a593Smuzhiyun 		.size = 30,
91*4882a593Smuzhiyun 	};
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun 	/* Load options from command line here */
94*4882a593Smuzhiyun 	while ((c = getopt(argc, argv, "b:cd:f::hnrRs:t:")) != -1) {
95*4882a593Smuzhiyun 		switch (c) {
96*4882a593Smuzhiyun 		case 'b':
97*4882a593Smuzhiyun 			options->blocks = strtol(optarg, NULL, 10);
98*4882a593Smuzhiyun 			break;
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 		case 'c':
101*4882a593Smuzhiyun 			options->no_cleanup = true;
102*4882a593Smuzhiyun 			break;
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 		case 'd':
105*4882a593Smuzhiyun 			options->test_dir = optarg;
106*4882a593Smuzhiyun 			break;
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 		case 'f':
109*4882a593Smuzhiyun 			if (optarg)
110*4882a593Smuzhiyun 				options->file_types = optarg;
111*4882a593Smuzhiyun 			else
112*4882a593Smuzhiyun 				options->file_types = "sS";
113*4882a593Smuzhiyun 			break;
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 		case 'h':
116*4882a593Smuzhiyun 			print_help();
117*4882a593Smuzhiyun 			exit(0);
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 		case 'n':
120*4882a593Smuzhiyun 			options->no_native = true;
121*4882a593Smuzhiyun 			break;
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 		case 'r':
124*4882a593Smuzhiyun 			options->no_random = true;
125*4882a593Smuzhiyun 			break;
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 		case 'R':
128*4882a593Smuzhiyun 			options->no_linear = true;
129*4882a593Smuzhiyun 			break;
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 		case 's':
132*4882a593Smuzhiyun 			options->size = strtol(optarg, NULL, 10);
133*4882a593Smuzhiyun 			break;
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 		case 't':
136*4882a593Smuzhiyun 			options->tries = strtol(optarg, NULL, 10);
137*4882a593Smuzhiyun 			break;
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 		default:
140*4882a593Smuzhiyun 			print_help();
141*4882a593Smuzhiyun 			return -EINVAL;
142*4882a593Smuzhiyun 		}
143*4882a593Smuzhiyun 	}
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	options->size = 1L << options->size;
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	return 0;
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun 
shuffle(size_t * buffer,size_t size)150*4882a593Smuzhiyun void shuffle(size_t *buffer, size_t size)
151*4882a593Smuzhiyun {
152*4882a593Smuzhiyun 	size_t i;
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun 	for (i = 0; i < size; ++i) {
155*4882a593Smuzhiyun 		size_t j = random() * (size - i - 1) / RAND_MAX;
156*4882a593Smuzhiyun 		size_t temp = buffer[i];
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 		buffer[i] = buffer[j];
159*4882a593Smuzhiyun 		buffer[j] = temp;
160*4882a593Smuzhiyun 	}
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun 
get_free_memory(void)163*4882a593Smuzhiyun int get_free_memory(void)
164*4882a593Smuzhiyun {
165*4882a593Smuzhiyun 	FILE *meminfo = fopen("/proc/meminfo", "re");
166*4882a593Smuzhiyun 	char field[256];
167*4882a593Smuzhiyun 	char value[256] = {};
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	if (!meminfo)
170*4882a593Smuzhiyun 		return -ENOENT;
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	while (fscanf(meminfo, "%[^:]: %s kB\n", field, value) == 2) {
173*4882a593Smuzhiyun 		if (!strcmp(field, "MemFree"))
174*4882a593Smuzhiyun 			break;
175*4882a593Smuzhiyun 		*value = 0;
176*4882a593Smuzhiyun 	}
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	fclose(meminfo);
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	if (!*value)
181*4882a593Smuzhiyun 		return -ENOENT;
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 	return strtol(value, NULL, 10);
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun 
write_data(int cmd_fd,int dir_fd,const char * name,size_t size,int flags)186*4882a593Smuzhiyun int write_data(int cmd_fd, int dir_fd, const char *name, size_t size, int flags)
187*4882a593Smuzhiyun {
188*4882a593Smuzhiyun 	int fd = openat(dir_fd, name, O_RDWR | O_CLOEXEC);
189*4882a593Smuzhiyun 	struct incfs_permit_fill permit_fill = {
190*4882a593Smuzhiyun 		.file_descriptor = fd,
191*4882a593Smuzhiyun 	};
192*4882a593Smuzhiyun 	int block_count = 1 + (size - 1) / INCFS_DATA_FILE_BLOCK_SIZE;
193*4882a593Smuzhiyun 	size_t *blocks = malloc(sizeof(size_t) * block_count);
194*4882a593Smuzhiyun 	int error = 0;
195*4882a593Smuzhiyun 	size_t i;
196*4882a593Smuzhiyun 	uint8_t data[INCFS_DATA_FILE_BLOCK_SIZE] = {};
197*4882a593Smuzhiyun 	uint8_t compressed_data[INCFS_DATA_FILE_BLOCK_SIZE] = {};
198*4882a593Smuzhiyun 	struct incfs_fill_block fill_block = {
199*4882a593Smuzhiyun 		.compression = COMPRESSION_NONE,
200*4882a593Smuzhiyun 		.data_len = sizeof(data),
201*4882a593Smuzhiyun 		.data = ptr_to_u64(data),
202*4882a593Smuzhiyun 	};
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	if (!blocks) {
205*4882a593Smuzhiyun 		err_msg("Out of memory");
206*4882a593Smuzhiyun 		error = -errno;
207*4882a593Smuzhiyun 		goto out;
208*4882a593Smuzhiyun 	}
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	if (fd == -1) {
211*4882a593Smuzhiyun 		err_msg("Could not open file for writing %s", name);
212*4882a593Smuzhiyun 		error = -errno;
213*4882a593Smuzhiyun 		goto out;
214*4882a593Smuzhiyun 	}
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	if (ioctl(cmd_fd, INCFS_IOC_PERMIT_FILL, &permit_fill)) {
217*4882a593Smuzhiyun 		err_msg("Failed to call PERMIT_FILL");
218*4882a593Smuzhiyun 		error = -errno;
219*4882a593Smuzhiyun 		goto out;
220*4882a593Smuzhiyun 	}
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	for (i = 0; i < block_count; ++i)
223*4882a593Smuzhiyun 		blocks[i] = i;
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	if (flags & SHUFFLE)
226*4882a593Smuzhiyun 		shuffle(blocks, block_count);
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	if (flags & COMPRESS) {
229*4882a593Smuzhiyun 		size_t comp_size = LZ4_compress_default(
230*4882a593Smuzhiyun 			(char *)data, (char *)compressed_data, sizeof(data),
231*4882a593Smuzhiyun 			ARRAY_SIZE(compressed_data));
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 		if (comp_size <= 0) {
234*4882a593Smuzhiyun 			error = -EBADMSG;
235*4882a593Smuzhiyun 			goto out;
236*4882a593Smuzhiyun 		}
237*4882a593Smuzhiyun 		fill_block.compression = COMPRESSION_LZ4;
238*4882a593Smuzhiyun 		fill_block.data = ptr_to_u64(compressed_data);
239*4882a593Smuzhiyun 		fill_block.data_len = comp_size;
240*4882a593Smuzhiyun 	}
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	for (i = 0; i < block_count; ++i) {
243*4882a593Smuzhiyun 		struct incfs_fill_blocks fill_blocks = {
244*4882a593Smuzhiyun 			.count = 1,
245*4882a593Smuzhiyun 			.fill_blocks = ptr_to_u64(&fill_block),
246*4882a593Smuzhiyun 		};
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 		fill_block.block_index = blocks[i];
249*4882a593Smuzhiyun 		int written = ioctl(fd, INCFS_IOC_FILL_BLOCKS, &fill_blocks);
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 		if (written != 1) {
252*4882a593Smuzhiyun 			error = -errno;
253*4882a593Smuzhiyun 			err_msg("Failed to write block %lu in file %s", i,
254*4882a593Smuzhiyun 				name);
255*4882a593Smuzhiyun 			break;
256*4882a593Smuzhiyun 		}
257*4882a593Smuzhiyun 	}
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun out:
260*4882a593Smuzhiyun 	free(blocks);
261*4882a593Smuzhiyun 	close(fd);
262*4882a593Smuzhiyun 	sync();
263*4882a593Smuzhiyun 	return error;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun 
measure_read_throughput_internal(const char * tag,int dir,const char * name,const struct options * options,bool random)266*4882a593Smuzhiyun int measure_read_throughput_internal(const char *tag, int dir, const char *name,
267*4882a593Smuzhiyun 				     const struct options *options, bool random)
268*4882a593Smuzhiyun {
269*4882a593Smuzhiyun 	int block;
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	if (random)
272*4882a593Smuzhiyun 		printf("%32s(random)", tag);
273*4882a593Smuzhiyun 	else
274*4882a593Smuzhiyun 		printf("%40s", tag);
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun 	for (block = 0; block < options->blocks; ++block) {
277*4882a593Smuzhiyun 		size_t buffer_size;
278*4882a593Smuzhiyun 		char *buffer;
279*4882a593Smuzhiyun 		int try;
280*4882a593Smuzhiyun 		double time = 0;
281*4882a593Smuzhiyun 		double throughput;
282*4882a593Smuzhiyun 		int memory = 0;
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 		buffer_size = 1 << (block + 12);
285*4882a593Smuzhiyun 		buffer = malloc(buffer_size);
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 		for (try = 0; try < options->tries; ++try) {
288*4882a593Smuzhiyun 			int err;
289*4882a593Smuzhiyun 			struct timespec start_time, end_time;
290*4882a593Smuzhiyun 			off_t i;
291*4882a593Smuzhiyun 			int fd;
292*4882a593Smuzhiyun 			size_t offsets_size = options->size / buffer_size;
293*4882a593Smuzhiyun 			size_t *offsets =
294*4882a593Smuzhiyun 				malloc(offsets_size * sizeof(*offsets));
295*4882a593Smuzhiyun 			int start_memory, end_memory;
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 			if (!offsets) {
298*4882a593Smuzhiyun 				err_msg("Not enough memory");
299*4882a593Smuzhiyun 				return -ENOMEM;
300*4882a593Smuzhiyun 			}
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 			for (i = 0; i < offsets_size; ++i)
303*4882a593Smuzhiyun 				offsets[i] = i * buffer_size;
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 			if (random)
306*4882a593Smuzhiyun 				shuffle(offsets, offsets_size);
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 			err = drop_caches();
309*4882a593Smuzhiyun 			if (err) {
310*4882a593Smuzhiyun 				err_msg("Failed to drop caches");
311*4882a593Smuzhiyun 				return err;
312*4882a593Smuzhiyun 			}
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 			start_memory = get_free_memory();
315*4882a593Smuzhiyun 			if (start_memory < 0) {
316*4882a593Smuzhiyun 				err_msg("Failed to get start memory");
317*4882a593Smuzhiyun 				return start_memory;
318*4882a593Smuzhiyun 			}
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 			fd = openat(dir, name, O_RDONLY | O_CLOEXEC);
321*4882a593Smuzhiyun 			if (fd == -1) {
322*4882a593Smuzhiyun 				err_msg("Failed to open file");
323*4882a593Smuzhiyun 				return err;
324*4882a593Smuzhiyun 			}
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 			err = clock_gettime(CLOCK_MONOTONIC, &start_time);
327*4882a593Smuzhiyun 			if (err) {
328*4882a593Smuzhiyun 				err_msg("Failed to get start time");
329*4882a593Smuzhiyun 				return err;
330*4882a593Smuzhiyun 			}
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 			for (i = 0; i < offsets_size; ++i)
333*4882a593Smuzhiyun 				if (pread(fd, buffer, buffer_size,
334*4882a593Smuzhiyun 					  offsets[i]) != buffer_size) {
335*4882a593Smuzhiyun 					err_msg("Failed to read file");
336*4882a593Smuzhiyun 					err = -errno;
337*4882a593Smuzhiyun 					goto fail;
338*4882a593Smuzhiyun 				}
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 			err = clock_gettime(CLOCK_MONOTONIC, &end_time);
341*4882a593Smuzhiyun 			if (err) {
342*4882a593Smuzhiyun 				err_msg("Failed to get start time");
343*4882a593Smuzhiyun 				goto fail;
344*4882a593Smuzhiyun 			}
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 			end_memory = get_free_memory();
347*4882a593Smuzhiyun 			if (end_memory < 0) {
348*4882a593Smuzhiyun 				err_msg("Failed to get end memory");
349*4882a593Smuzhiyun 				return end_memory;
350*4882a593Smuzhiyun 			}
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun 			time += end_time.tv_sec - start_time.tv_sec;
353*4882a593Smuzhiyun 			time += (end_time.tv_nsec - start_time.tv_nsec) / 1e9;
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 			close(fd);
356*4882a593Smuzhiyun 			fd = -1;
357*4882a593Smuzhiyun 			memory += start_memory - end_memory;
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun fail:
360*4882a593Smuzhiyun 			free(offsets);
361*4882a593Smuzhiyun 			close(fd);
362*4882a593Smuzhiyun 			if (err)
363*4882a593Smuzhiyun 				return err;
364*4882a593Smuzhiyun 		}
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 		throughput = options->size * options->tries / time;
367*4882a593Smuzhiyun 		printf("%10.3e %10d", throughput, memory / options->tries);
368*4882a593Smuzhiyun 		free(buffer);
369*4882a593Smuzhiyun 	}
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	printf("\n");
372*4882a593Smuzhiyun 	return 0;
373*4882a593Smuzhiyun }
374*4882a593Smuzhiyun 
measure_read_throughput(const char * tag,int dir,const char * name,const struct options * options)375*4882a593Smuzhiyun int measure_read_throughput(const char *tag, int dir, const char *name,
376*4882a593Smuzhiyun 			    const struct options *options)
377*4882a593Smuzhiyun {
378*4882a593Smuzhiyun 	int err = 0;
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun 	if (!options->no_linear)
381*4882a593Smuzhiyun 		err = measure_read_throughput_internal(tag, dir, name, options,
382*4882a593Smuzhiyun 						       false);
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 	if (!err && !options->no_random)
385*4882a593Smuzhiyun 		err = measure_read_throughput_internal(tag, dir, name, options,
386*4882a593Smuzhiyun 						       true);
387*4882a593Smuzhiyun 	return err;
388*4882a593Smuzhiyun }
389*4882a593Smuzhiyun 
test_native_file(int dir,const struct options * options)390*4882a593Smuzhiyun int test_native_file(int dir, const struct options *options)
391*4882a593Smuzhiyun {
392*4882a593Smuzhiyun 	const char *name = "file";
393*4882a593Smuzhiyun 	int fd;
394*4882a593Smuzhiyun 	char buffer[4096] = {};
395*4882a593Smuzhiyun 	off_t i;
396*4882a593Smuzhiyun 	int err;
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun 	fd = openat(dir, name, O_CREAT | O_WRONLY | O_CLOEXEC, 0600);
399*4882a593Smuzhiyun 	if (fd == -1) {
400*4882a593Smuzhiyun 		err_msg("Could not open native file");
401*4882a593Smuzhiyun 		return -errno;
402*4882a593Smuzhiyun 	}
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun 	for (i = 0; i < options->size; i += sizeof(buffer))
405*4882a593Smuzhiyun 		if (pwrite(fd, buffer, sizeof(buffer), i) != sizeof(buffer)) {
406*4882a593Smuzhiyun 			err_msg("Failed to write file");
407*4882a593Smuzhiyun 			err = -errno;
408*4882a593Smuzhiyun 			goto fail;
409*4882a593Smuzhiyun 		}
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	close(fd);
412*4882a593Smuzhiyun 	sync();
413*4882a593Smuzhiyun 	fd = -1;
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	err = measure_read_throughput("native", dir, name, options);
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun fail:
418*4882a593Smuzhiyun 	close(fd);
419*4882a593Smuzhiyun 	return err;
420*4882a593Smuzhiyun }
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun struct hash_block {
423*4882a593Smuzhiyun 	char data[INCFS_DATA_FILE_BLOCK_SIZE];
424*4882a593Smuzhiyun };
425*4882a593Smuzhiyun 
build_mtree(size_t size,char * root_hash,int * mtree_block_count)426*4882a593Smuzhiyun static struct hash_block *build_mtree(size_t size, char *root_hash,
427*4882a593Smuzhiyun 				      int *mtree_block_count)
428*4882a593Smuzhiyun {
429*4882a593Smuzhiyun 	char data[INCFS_DATA_FILE_BLOCK_SIZE] = {};
430*4882a593Smuzhiyun 	const int digest_size = SHA256_DIGEST_SIZE;
431*4882a593Smuzhiyun 	const int hash_per_block = INCFS_DATA_FILE_BLOCK_SIZE / digest_size;
432*4882a593Smuzhiyun 	int block_count = 0;
433*4882a593Smuzhiyun 	int hash_block_count = 0;
434*4882a593Smuzhiyun 	int total_tree_block_count = 0;
435*4882a593Smuzhiyun 	int tree_lvl_index[INCFS_MAX_MTREE_LEVELS] = {};
436*4882a593Smuzhiyun 	int tree_lvl_count[INCFS_MAX_MTREE_LEVELS] = {};
437*4882a593Smuzhiyun 	int levels_count = 0;
438*4882a593Smuzhiyun 	int i, level;
439*4882a593Smuzhiyun 	struct hash_block *mtree;
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 	if (size == 0)
442*4882a593Smuzhiyun 		return 0;
443*4882a593Smuzhiyun 
444*4882a593Smuzhiyun 	block_count = 1 + (size - 1) / INCFS_DATA_FILE_BLOCK_SIZE;
445*4882a593Smuzhiyun 	hash_block_count = block_count;
446*4882a593Smuzhiyun 	for (i = 0; hash_block_count > 1; i++) {
447*4882a593Smuzhiyun 		hash_block_count = (hash_block_count + hash_per_block - 1) /
448*4882a593Smuzhiyun 				   hash_per_block;
449*4882a593Smuzhiyun 		tree_lvl_count[i] = hash_block_count;
450*4882a593Smuzhiyun 		total_tree_block_count += hash_block_count;
451*4882a593Smuzhiyun 	}
452*4882a593Smuzhiyun 	levels_count = i;
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun 	for (i = 0; i < levels_count; i++) {
455*4882a593Smuzhiyun 		int prev_lvl_base = (i == 0) ? total_tree_block_count :
456*4882a593Smuzhiyun 					       tree_lvl_index[i - 1];
457*4882a593Smuzhiyun 
458*4882a593Smuzhiyun 		tree_lvl_index[i] = prev_lvl_base - tree_lvl_count[i];
459*4882a593Smuzhiyun 	}
460*4882a593Smuzhiyun 
461*4882a593Smuzhiyun 	*mtree_block_count = total_tree_block_count;
462*4882a593Smuzhiyun 	mtree = calloc(total_tree_block_count, sizeof(*mtree));
463*4882a593Smuzhiyun 	/* Build level 0 hashes. */
464*4882a593Smuzhiyun 	for (i = 0; i < block_count; i++) {
465*4882a593Smuzhiyun 		int block_index = tree_lvl_index[0] + i / hash_per_block;
466*4882a593Smuzhiyun 		int block_off = (i % hash_per_block) * digest_size;
467*4882a593Smuzhiyun 		char *hash_ptr = mtree[block_index].data + block_off;
468*4882a593Smuzhiyun 
469*4882a593Smuzhiyun 		sha256(data, INCFS_DATA_FILE_BLOCK_SIZE, hash_ptr);
470*4882a593Smuzhiyun 	}
471*4882a593Smuzhiyun 
472*4882a593Smuzhiyun 	/* Build higher levels of hash tree. */
473*4882a593Smuzhiyun 	for (level = 1; level < levels_count; level++) {
474*4882a593Smuzhiyun 		int prev_lvl_base = tree_lvl_index[level - 1];
475*4882a593Smuzhiyun 		int prev_lvl_count = tree_lvl_count[level - 1];
476*4882a593Smuzhiyun 
477*4882a593Smuzhiyun 		for (i = 0; i < prev_lvl_count; i++) {
478*4882a593Smuzhiyun 			int block_index =
479*4882a593Smuzhiyun 				i / hash_per_block + tree_lvl_index[level];
480*4882a593Smuzhiyun 			int block_off = (i % hash_per_block) * digest_size;
481*4882a593Smuzhiyun 			char *hash_ptr = mtree[block_index].data + block_off;
482*4882a593Smuzhiyun 
483*4882a593Smuzhiyun 			sha256(mtree[i + prev_lvl_base].data,
484*4882a593Smuzhiyun 			       INCFS_DATA_FILE_BLOCK_SIZE, hash_ptr);
485*4882a593Smuzhiyun 		}
486*4882a593Smuzhiyun 	}
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun 	/* Calculate root hash from the top block */
489*4882a593Smuzhiyun 	sha256(mtree[0].data, INCFS_DATA_FILE_BLOCK_SIZE, root_hash);
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 	return mtree;
492*4882a593Smuzhiyun }
493*4882a593Smuzhiyun 
load_hash_tree(int cmd_fd,int dir,const char * name,struct hash_block * mtree,int mtree_block_count)494*4882a593Smuzhiyun static int load_hash_tree(int cmd_fd, int dir, const char *name,
495*4882a593Smuzhiyun 			  struct hash_block *mtree, int mtree_block_count)
496*4882a593Smuzhiyun {
497*4882a593Smuzhiyun 	int err;
498*4882a593Smuzhiyun 	int i;
499*4882a593Smuzhiyun 	int fd;
500*4882a593Smuzhiyun 	struct incfs_fill_block *fill_block_array =
501*4882a593Smuzhiyun 		calloc(mtree_block_count, sizeof(struct incfs_fill_block));
502*4882a593Smuzhiyun 	struct incfs_fill_blocks fill_blocks = {
503*4882a593Smuzhiyun 		.count = mtree_block_count,
504*4882a593Smuzhiyun 		.fill_blocks = ptr_to_u64(fill_block_array),
505*4882a593Smuzhiyun 	};
506*4882a593Smuzhiyun 	struct incfs_permit_fill permit_fill;
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun 	if (!fill_block_array)
509*4882a593Smuzhiyun 		return -ENOMEM;
510*4882a593Smuzhiyun 
511*4882a593Smuzhiyun 	for (i = 0; i < fill_blocks.count; i++) {
512*4882a593Smuzhiyun 		fill_block_array[i] = (struct incfs_fill_block){
513*4882a593Smuzhiyun 			.block_index = i,
514*4882a593Smuzhiyun 			.data_len = INCFS_DATA_FILE_BLOCK_SIZE,
515*4882a593Smuzhiyun 			.data = ptr_to_u64(mtree[i].data),
516*4882a593Smuzhiyun 			.flags = INCFS_BLOCK_FLAGS_HASH
517*4882a593Smuzhiyun 		};
518*4882a593Smuzhiyun 	}
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun 	fd = openat(dir, name, O_RDONLY | O_CLOEXEC);
521*4882a593Smuzhiyun 	if (fd < 0) {
522*4882a593Smuzhiyun 		err = errno;
523*4882a593Smuzhiyun 		goto failure;
524*4882a593Smuzhiyun 	}
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun 	permit_fill.file_descriptor = fd;
527*4882a593Smuzhiyun 	if (ioctl(cmd_fd, INCFS_IOC_PERMIT_FILL, &permit_fill)) {
528*4882a593Smuzhiyun 		err_msg("Failed to call PERMIT_FILL");
529*4882a593Smuzhiyun 		err = -errno;
530*4882a593Smuzhiyun 		goto failure;
531*4882a593Smuzhiyun 	}
532*4882a593Smuzhiyun 
533*4882a593Smuzhiyun 	err = ioctl(fd, INCFS_IOC_FILL_BLOCKS, &fill_blocks);
534*4882a593Smuzhiyun 	close(fd);
535*4882a593Smuzhiyun 	if (err < fill_blocks.count)
536*4882a593Smuzhiyun 		err = errno;
537*4882a593Smuzhiyun 	else
538*4882a593Smuzhiyun 		err = 0;
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun failure:
541*4882a593Smuzhiyun 	free(fill_block_array);
542*4882a593Smuzhiyun 	return err;
543*4882a593Smuzhiyun }
544*4882a593Smuzhiyun 
test_incfs_file(int dst_dir,const struct options * options,int flags)545*4882a593Smuzhiyun int test_incfs_file(int dst_dir, const struct options *options, int flags)
546*4882a593Smuzhiyun {
547*4882a593Smuzhiyun 	int cmd_file = openat(dst_dir, INCFS_PENDING_READS_FILENAME,
548*4882a593Smuzhiyun 			      O_RDONLY | O_CLOEXEC);
549*4882a593Smuzhiyun 	int err;
550*4882a593Smuzhiyun 	char name[4];
551*4882a593Smuzhiyun 	incfs_uuid_t id;
552*4882a593Smuzhiyun 	char tag[256];
553*4882a593Smuzhiyun 
554*4882a593Smuzhiyun 	snprintf(name, sizeof(name), "%c%c%c",
555*4882a593Smuzhiyun 		 flags & SHUFFLE ? 'S' : 's',
556*4882a593Smuzhiyun 		 flags & COMPRESS ? 'C' : 'c',
557*4882a593Smuzhiyun 		 flags & VERIFY ? 'V' : 'v');
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun 	if (cmd_file == -1) {
560*4882a593Smuzhiyun 		err_msg("Could not open command file");
561*4882a593Smuzhiyun 		return -errno;
562*4882a593Smuzhiyun 	}
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 	if (flags & VERIFY) {
565*4882a593Smuzhiyun 		char root_hash[INCFS_MAX_HASH_SIZE];
566*4882a593Smuzhiyun 		int mtree_block_count;
567*4882a593Smuzhiyun 		struct hash_block *mtree = build_mtree(options->size, root_hash,
568*4882a593Smuzhiyun 						       &mtree_block_count);
569*4882a593Smuzhiyun 
570*4882a593Smuzhiyun 		if (!mtree) {
571*4882a593Smuzhiyun 			err_msg("Failed to build hash tree");
572*4882a593Smuzhiyun 			err = -ENOMEM;
573*4882a593Smuzhiyun 			goto fail;
574*4882a593Smuzhiyun 		}
575*4882a593Smuzhiyun 
576*4882a593Smuzhiyun 		err = crypto_emit_file(cmd_file, NULL, name, &id, options->size,
577*4882a593Smuzhiyun 				       root_hash, "add_data");
578*4882a593Smuzhiyun 
579*4882a593Smuzhiyun 		if (!err)
580*4882a593Smuzhiyun 			err = load_hash_tree(cmd_file, dst_dir, name, mtree,
581*4882a593Smuzhiyun 					     mtree_block_count);
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun 		free(mtree);
584*4882a593Smuzhiyun 	} else
585*4882a593Smuzhiyun 		err = emit_file(cmd_file, NULL, name, &id, options->size, NULL);
586*4882a593Smuzhiyun 
587*4882a593Smuzhiyun 	if (err) {
588*4882a593Smuzhiyun 		err_msg("Failed to create file %s", name);
589*4882a593Smuzhiyun 		goto fail;
590*4882a593Smuzhiyun 	}
591*4882a593Smuzhiyun 
592*4882a593Smuzhiyun 	if (write_data(cmd_file, dst_dir, name, options->size, flags))
593*4882a593Smuzhiyun 		goto fail;
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun 	snprintf(tag, sizeof(tag), "incfs%s%s%s",
596*4882a593Smuzhiyun 		 flags & SHUFFLE ? "(shuffle)" : "",
597*4882a593Smuzhiyun 		 flags & COMPRESS ? "(compress)" : "",
598*4882a593Smuzhiyun 		 flags & VERIFY ? "(verify)" : "");
599*4882a593Smuzhiyun 
600*4882a593Smuzhiyun 	err = measure_read_throughput(tag, dst_dir, name, options);
601*4882a593Smuzhiyun 
602*4882a593Smuzhiyun fail:
603*4882a593Smuzhiyun 	close(cmd_file);
604*4882a593Smuzhiyun 	return err;
605*4882a593Smuzhiyun }
606*4882a593Smuzhiyun 
skip(struct options const * options,int flag,char c)607*4882a593Smuzhiyun bool skip(struct options const *options, int flag, char c)
608*4882a593Smuzhiyun {
609*4882a593Smuzhiyun 	if (!options->file_types)
610*4882a593Smuzhiyun 		return false;
611*4882a593Smuzhiyun 
612*4882a593Smuzhiyun 	if (flag && strchr(options->file_types, tolower(c)))
613*4882a593Smuzhiyun 		return true;
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun 	if (!flag && strchr(options->file_types, toupper(c)))
616*4882a593Smuzhiyun 		return true;
617*4882a593Smuzhiyun 
618*4882a593Smuzhiyun 	return false;
619*4882a593Smuzhiyun }
620*4882a593Smuzhiyun 
main(int argc,char * const * argv)621*4882a593Smuzhiyun int main(int argc, char *const *argv)
622*4882a593Smuzhiyun {
623*4882a593Smuzhiyun 	struct options options;
624*4882a593Smuzhiyun 	int err;
625*4882a593Smuzhiyun 	const char *native_dir = "native";
626*4882a593Smuzhiyun 	const char *src_dir = "src";
627*4882a593Smuzhiyun 	const char *dst_dir = "dst";
628*4882a593Smuzhiyun 	int native_dir_fd = -1;
629*4882a593Smuzhiyun 	int src_dir_fd = -1;
630*4882a593Smuzhiyun 	int dst_dir_fd = -1;
631*4882a593Smuzhiyun 	int block;
632*4882a593Smuzhiyun 	int flags;
633*4882a593Smuzhiyun 
634*4882a593Smuzhiyun 	err = parse_options(argc, argv, &options);
635*4882a593Smuzhiyun 	if (err)
636*4882a593Smuzhiyun 		return err;
637*4882a593Smuzhiyun 
638*4882a593Smuzhiyun 	err = chdir(options.test_dir);
639*4882a593Smuzhiyun 	if (err) {
640*4882a593Smuzhiyun 		err_msg("Failed to change to %s", options.test_dir);
641*4882a593Smuzhiyun 		return -errno;
642*4882a593Smuzhiyun 	}
643*4882a593Smuzhiyun 
644*4882a593Smuzhiyun 	/* Clean up any interrupted previous runs */
645*4882a593Smuzhiyun 	while (!umount(dst_dir))
646*4882a593Smuzhiyun 		;
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun 	err = remove_dir(native_dir) || remove_dir(src_dir) ||
649*4882a593Smuzhiyun 	      remove_dir(dst_dir);
650*4882a593Smuzhiyun 	if (err)
651*4882a593Smuzhiyun 		return err;
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun 	err = mkdir(native_dir, 0700);
654*4882a593Smuzhiyun 	if (err) {
655*4882a593Smuzhiyun 		err_msg("Failed to make directory %s", src_dir);
656*4882a593Smuzhiyun 		err = -errno;
657*4882a593Smuzhiyun 		goto cleanup;
658*4882a593Smuzhiyun 	}
659*4882a593Smuzhiyun 
660*4882a593Smuzhiyun 	err = mkdir(src_dir, 0700);
661*4882a593Smuzhiyun 	if (err) {
662*4882a593Smuzhiyun 		err_msg("Failed to make directory %s", src_dir);
663*4882a593Smuzhiyun 		err = -errno;
664*4882a593Smuzhiyun 		goto cleanup;
665*4882a593Smuzhiyun 	}
666*4882a593Smuzhiyun 
667*4882a593Smuzhiyun 	err = mkdir(dst_dir, 0700);
668*4882a593Smuzhiyun 	if (err) {
669*4882a593Smuzhiyun 		err_msg("Failed to make directory %s", src_dir);
670*4882a593Smuzhiyun 		err = -errno;
671*4882a593Smuzhiyun 		goto cleanup;
672*4882a593Smuzhiyun 	}
673*4882a593Smuzhiyun 
674*4882a593Smuzhiyun 	err = mount_fs_opt(dst_dir, src_dir, "readahead=0,rlog_pages=0", 0);
675*4882a593Smuzhiyun 	if (err) {
676*4882a593Smuzhiyun 		err_msg("Failed to mount incfs");
677*4882a593Smuzhiyun 		goto cleanup;
678*4882a593Smuzhiyun 	}
679*4882a593Smuzhiyun 
680*4882a593Smuzhiyun 	native_dir_fd = open(native_dir, O_RDONLY | O_CLOEXEC);
681*4882a593Smuzhiyun 	src_dir_fd = open(src_dir, O_RDONLY | O_CLOEXEC);
682*4882a593Smuzhiyun 	dst_dir_fd = open(dst_dir, O_RDONLY | O_CLOEXEC);
683*4882a593Smuzhiyun 	if (native_dir_fd == -1 || src_dir_fd == -1 || dst_dir_fd == -1) {
684*4882a593Smuzhiyun 		err_msg("Failed to open native, src or dst dir");
685*4882a593Smuzhiyun 		err = -errno;
686*4882a593Smuzhiyun 		goto cleanup;
687*4882a593Smuzhiyun 	}
688*4882a593Smuzhiyun 
689*4882a593Smuzhiyun 	printf("%40s", "");
690*4882a593Smuzhiyun 	for (block = 0; block < options.blocks; ++block)
691*4882a593Smuzhiyun 		printf("%21d", 1 << (block + 12));
692*4882a593Smuzhiyun 	printf("\n");
693*4882a593Smuzhiyun 
694*4882a593Smuzhiyun 	if (!err && !options.no_native)
695*4882a593Smuzhiyun 		err = test_native_file(native_dir_fd, &options);
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun 	for (flags = 0; flags < LAST_FLAG && !err; ++flags) {
698*4882a593Smuzhiyun 		if (skip(&options, flags & SHUFFLE, 's') ||
699*4882a593Smuzhiyun 		    skip(&options, flags & COMPRESS, 'c') ||
700*4882a593Smuzhiyun 		    skip(&options, flags & VERIFY, 'v'))
701*4882a593Smuzhiyun 			continue;
702*4882a593Smuzhiyun 		err = test_incfs_file(dst_dir_fd, &options, flags);
703*4882a593Smuzhiyun 	}
704*4882a593Smuzhiyun 
705*4882a593Smuzhiyun cleanup:
706*4882a593Smuzhiyun 	close(native_dir_fd);
707*4882a593Smuzhiyun 	close(src_dir_fd);
708*4882a593Smuzhiyun 	close(dst_dir_fd);
709*4882a593Smuzhiyun 	if (!options.no_cleanup) {
710*4882a593Smuzhiyun 		umount(dst_dir);
711*4882a593Smuzhiyun 		remove_dir(native_dir);
712*4882a593Smuzhiyun 		remove_dir(dst_dir);
713*4882a593Smuzhiyun 		remove_dir(src_dir);
714*4882a593Smuzhiyun 	}
715*4882a593Smuzhiyun 
716*4882a593Smuzhiyun 	return err;
717*4882a593Smuzhiyun }
718