• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2020 Google LLC
4  */
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <getopt.h>
8 #include <pthread.h>
9 #include <stdbool.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/mount.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17 
18 #include "utils.h"
19 
20 #define err_msg(...)                                                           \
21 	do {                                                                   \
22 		fprintf(stderr, "%s: (%d) ", TAG, __LINE__);                   \
23 		fprintf(stderr, __VA_ARGS__);                                  \
24 		fprintf(stderr, " (%s)\n", strerror(errno));                   \
25 	} while (false)
26 
27 #define TAG "incfs_stress"
28 
29 struct options {
30 	bool no_cleanup; /* -c */
31 	const char *test_dir; /* -d */
32 	unsigned int rng_seed; /* -g */
33 	int num_reads; /* -n */
34 	int readers; /* -r */
35 	int size; /* -s */
36 	int timeout; /* -t */
37 };
38 
39 struct read_data {
40 	const char *filename;
41 	int dir_fd;
42 	size_t filesize;
43 	int num_reads;
44 	unsigned int rng_seed;
45 };
46 
47 int cancel_threads;
48 
parse_options(int argc,char * const * argv,struct options * options)49 int parse_options(int argc, char *const *argv, struct options *options)
50 {
51 	signed char c;
52 
53 	/* Set defaults here */
54 	*options = (struct options){
55 		.test_dir = ".",
56 		.num_reads = 1000,
57 		.readers = 10,
58 		.size = 10,
59 	};
60 
61 	/* Load options from command line here */
62 	while ((c = getopt(argc, argv, "cd:g:n:r:s:t:")) != -1) {
63 		switch (c) {
64 		case 'c':
65 			options->no_cleanup = true;
66 			break;
67 
68 		case 'd':
69 			options->test_dir = optarg;
70 			break;
71 
72 		case 'g':
73 			options->rng_seed = strtol(optarg, NULL, 10);
74 			break;
75 
76 		case 'n':
77 			options->num_reads = strtol(optarg, NULL, 10);
78 			break;
79 
80 		case 'r':
81 			options->readers = strtol(optarg, NULL, 10);
82 			break;
83 
84 		case 's':
85 			options->size = strtol(optarg, NULL, 10);
86 			break;
87 
88 		case 't':
89 			options->timeout = strtol(optarg, NULL, 10);
90 			break;
91 		}
92 	}
93 
94 	return 0;
95 }
96 
reader(void * data)97 void *reader(void *data)
98 {
99 	struct read_data *read_data = (struct read_data *)data;
100 	int i;
101 	int fd = -1;
102 	void *buffer = malloc(read_data->filesize);
103 
104 	if (!buffer) {
105 		err_msg("Failed to alloc read buffer");
106 		goto out;
107 	}
108 
109 	fd = openat(read_data->dir_fd, read_data->filename,
110 		    O_RDONLY | O_CLOEXEC);
111 	if (fd == -1) {
112 		err_msg("Failed to open file");
113 		goto out;
114 	}
115 
116 	for (i = 0; i < read_data->num_reads && !cancel_threads; ++i) {
117 		off_t offset = rnd(read_data->filesize, &read_data->rng_seed);
118 		size_t count =
119 			rnd(read_data->filesize - offset, &read_data->rng_seed);
120 		ssize_t err = pread(fd, buffer, count, offset);
121 
122 		if (err != count)
123 			err_msg("failed to read with value %lu", err);
124 	}
125 
126 out:
127 	close(fd);
128 	free(read_data);
129 	free(buffer);
130 	return NULL;
131 }
132 
write_data(int cmd_fd,int dir_fd,const char * name,size_t size)133 int write_data(int cmd_fd, int dir_fd, const char *name, size_t size)
134 {
135 	int fd = openat(dir_fd, name, O_RDWR | O_CLOEXEC);
136 	struct incfs_permit_fill permit_fill = {
137 		.file_descriptor = fd,
138 	};
139 	int error = 0;
140 	int i;
141 	int block_count = 1 + (size - 1) / INCFS_DATA_FILE_BLOCK_SIZE;
142 
143 	if (fd == -1) {
144 		err_msg("Could not open file for writing %s", name);
145 		return -errno;
146 	}
147 
148 	if (ioctl(cmd_fd, INCFS_IOC_PERMIT_FILL, &permit_fill)) {
149 		err_msg("Failed to call PERMIT_FILL");
150 		error = -errno;
151 		goto out;
152 	}
153 
154 	for (i = 0; i < block_count; ++i) {
155 		uint8_t data[INCFS_DATA_FILE_BLOCK_SIZE] = {};
156 		size_t block_size =
157 			size > i * INCFS_DATA_FILE_BLOCK_SIZE ?
158 				INCFS_DATA_FILE_BLOCK_SIZE :
159 				size - (i * INCFS_DATA_FILE_BLOCK_SIZE);
160 		struct incfs_fill_block fill_block = {
161 			.compression = COMPRESSION_NONE,
162 			.block_index = i,
163 			.data_len = block_size,
164 			.data = ptr_to_u64(data),
165 		};
166 		struct incfs_fill_blocks fill_blocks = {
167 			.count = 1,
168 			.fill_blocks = ptr_to_u64(&fill_block),
169 		};
170 		int written = ioctl(fd, INCFS_IOC_FILL_BLOCKS, &fill_blocks);
171 
172 		if (written != 1) {
173 			error = -errno;
174 			err_msg("Failed to write block %d in file %s", i, name);
175 			break;
176 		}
177 	}
178 out:
179 	close(fd);
180 	return error;
181 }
182 
test_files(int src_dir,int dst_dir,struct options const * options)183 int test_files(int src_dir, int dst_dir, struct options const *options)
184 {
185 	unsigned int seed = options->rng_seed;
186 	int cmd_file = openat(dst_dir, INCFS_PENDING_READS_FILENAME,
187 			      O_RDONLY | O_CLOEXEC);
188 	int err;
189 	const char *name = "001";
190 	incfs_uuid_t id;
191 	size_t size;
192 	int i;
193 	pthread_t *threads = NULL;
194 
195 	size = 1 << (rnd(options->size, &seed) + 12);
196 	size += rnd(size, &seed);
197 
198 	if (cmd_file == -1) {
199 		err_msg("Could not open command file");
200 		return -errno;
201 	}
202 
203 	err = emit_file(cmd_file, NULL, name, &id, size, NULL);
204 	if (err) {
205 		err_msg("Failed to create file %s", name);
206 		return err;
207 	}
208 
209 	threads = malloc(sizeof(pthread_t) * options->readers);
210 	if (!threads) {
211 		err_msg("Could not allocate memory for threads");
212 		return -ENOMEM;
213 	}
214 
215 	for (i = 0; i < options->readers; ++i) {
216 		struct read_data *read_data = malloc(sizeof(*read_data));
217 
218 		if (!read_data) {
219 			err_msg("Failed to allocate read_data");
220 			err = -ENOMEM;
221 			break;
222 		}
223 
224 		*read_data = (struct read_data){
225 			.filename = name,
226 			.dir_fd = dst_dir,
227 			.filesize = size,
228 			.num_reads = options->num_reads,
229 			.rng_seed = seed,
230 		};
231 
232 		rnd(0, &seed);
233 
234 		err = pthread_create(threads + i, 0, reader, read_data);
235 		if (err) {
236 			err_msg("Failed to create thread");
237 			free(read_data);
238 			break;
239 		}
240 	}
241 
242 	if (err)
243 		cancel_threads = 1;
244 	else
245 		err = write_data(cmd_file, dst_dir, name, size);
246 
247 	for (; i > 0; --i) {
248 		if (pthread_join(threads[i - 1], NULL)) {
249 			err_msg("FATAL: failed to join thread");
250 			exit(-errno);
251 		}
252 	}
253 
254 	free(threads);
255 	close(cmd_file);
256 	return err;
257 }
258 
main(int argc,char * const * argv)259 int main(int argc, char *const *argv)
260 {
261 	struct options options;
262 	int err;
263 	const char *src_dir = "src";
264 	const char *dst_dir = "dst";
265 	int src_dir_fd = -1;
266 	int dst_dir_fd = -1;
267 
268 	err = parse_options(argc, argv, &options);
269 	if (err)
270 		return err;
271 
272 	err = chdir(options.test_dir);
273 	if (err) {
274 		err_msg("Failed to change to %s", options.test_dir);
275 		return -errno;
276 	}
277 
278 	err = remove_dir(src_dir) || remove_dir(dst_dir);
279 	if (err)
280 		return err;
281 
282 	err = mkdir(src_dir, 0700);
283 	if (err) {
284 		err_msg("Failed to make directory %s", src_dir);
285 		err = -errno;
286 		goto cleanup;
287 	}
288 
289 	err = mkdir(dst_dir, 0700);
290 	if (err) {
291 		err_msg("Failed to make directory %s", src_dir);
292 		err = -errno;
293 		goto cleanup;
294 	}
295 
296 	err = mount_fs(dst_dir, src_dir, options.timeout);
297 	if (err) {
298 		err_msg("Failed to mount incfs");
299 		goto cleanup;
300 	}
301 
302 	src_dir_fd = open(src_dir, O_RDONLY | O_CLOEXEC);
303 	dst_dir_fd = open(dst_dir, O_RDONLY | O_CLOEXEC);
304 	if (src_dir_fd == -1 || dst_dir_fd == -1) {
305 		err_msg("Failed to open src or dst dir");
306 		err = -errno;
307 		goto cleanup;
308 	}
309 
310 	err = test_files(src_dir_fd, dst_dir_fd, &options);
311 
312 cleanup:
313 	close(src_dir_fd);
314 	close(dst_dir_fd);
315 	if (!options.no_cleanup) {
316 		umount(dst_dir);
317 		remove_dir(dst_dir);
318 		remove_dir(src_dir);
319 	}
320 
321 	return err;
322 }
323