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