xref: /freebsd/sys/contrib/openzfs/tests/zfs-tests/cmd/manipulate_user_buffer.c (revision 22649d4dba730d46244fd2dff4fd174903c8379f)
1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3  * This file and its contents are supplied under the terms of the
4  * Common Development and Distribution License ("CDDL"), version 1.0.
5  * You may only use this file in accordance with the terms of version
6  * 1.0 of the CDDL.
7  *
8  * A full copy of the text of the CDDL should have accompanied this
9  * source.  A copy of the CDDL is also available via the Internet at
10  * https://opensource.org/license/CDDL-1.0.
11  */
12 
13 /*
14  * Copyright (c) 2024 by Triad National Security, LLC.
15  */
16 
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26 #include <pthread.h>
27 #include <assert.h>
28 
29 #ifndef MIN
30 #define	MIN(a, b)	((a) < (b)) ? (a) : (b)
31 #endif
32 
33 static char *filename = NULL;
34 static int blocksize = 131072; /* 128K */
35 static int err_expected = 0;
36 static int read_op = 0;
37 static int write_op = 0;
38 static int numblocks = 100;
39 static char *execname = NULL;
40 static int print_usage = 0;
41 static int randompattern = 0;
42 static int fd;
43 char *buf = NULL;
44 
45 typedef struct {
46 	int entire_file_completed;
47 } pthread_args_t;
48 
49 static void
usage(void)50 usage(void)
51 {
52 	(void) fprintf(stderr,
53 	    "usage %s -f filename [-b blocksize] [-e wr_error_expected]\n"
54 	    "         [-n numblocks] [-p randompattern] -r read_op \n"
55 	    "         -w write_op [-h help]\n"
56 	    "\n"
57 	    "Testing whether checksum verify works correctly for O_DIRECT.\n"
58 	    "when manipulating the contents of a userspace buffer.\n"
59 	    "\n"
60 	    "    filename:       File to read or write to.\n"
61 	    "    blocksize:      Size of each block to write (must be at \n"
62 	    "                    least >= 512).\n"
63 	    "    err_expected:   Whether write() is expected to return EIO\n"
64 	    "                    while manipulating the contents of the\n"
65 	    "                    buffer.\n"
66 	    "    numblocks:      Total number of blocksized blocks to\n"
67 	    "                    write.\n"
68 	    "    read_op:        Perform reads to the filename file while\n"
69 	    "                    while manipulating the buffer contents\n"
70 	    "    write_op:       Perform writes to the filename file while\n"
71 	    "                    manipulating the buffer contents\n"
72 	    "    randompattern:  Fill data buffer with random data for \n"
73 	    "                    writes. Default behavior is to fill the \n"
74 	    "                    buffer with known data pattern (0xdeadbeef)\n"
75 	    "    help:           Print usage information and exit.\n"
76 	    "\n"
77 	    "    Required parameters:\n"
78 	    "    filename\n"
79 	    "    read_op or write_op\n"
80 	    "\n"
81 	    "    Default Values:\n"
82 	    "    blocksize       -> 131072\n"
83 	    "    wr_err_expexted -> false\n"
84 	    "    numblocks       -> 100\n"
85 	    "    randompattern   -> false\n",
86 	    execname);
87 	(void) exit(1);
88 }
89 
90 static void
parse_options(int argc,char * argv[])91 parse_options(int argc, char *argv[])
92 {
93 	int c;
94 	int errflag = 0;
95 	extern char *optarg;
96 	extern int optind, optopt;
97 	execname = argv[0];
98 
99 	while ((c = getopt(argc, argv, "b:ef:hn:rw")) != -1) {
100 		switch (c) {
101 			case 'b':
102 				blocksize = atoi(optarg);
103 				break;
104 
105 			case 'e':
106 				err_expected = 1;
107 				break;
108 
109 			case 'f':
110 				filename = optarg;
111 				break;
112 
113 
114 			case 'h':
115 				print_usage = 1;
116 				break;
117 
118 			case 'n':
119 				numblocks = atoi(optarg);
120 				break;
121 
122 			case 'r':
123 				read_op = 1;
124 				break;
125 
126 			case 'w':
127 				write_op = 1;
128 				break;
129 
130 			case ':':
131 				(void) fprintf(stderr,
132 				    "Option -%c requires an opertand\n",
133 				    optopt);
134 				errflag++;
135 				break;
136 			case '?':
137 			default:
138 				(void) fprintf(stderr,
139 				    "Unrecognized option: -%c\n", optopt);
140 				errflag++;
141 				break;
142 		}
143 	}
144 
145 	if (errflag || print_usage == 1)
146 		(void) usage();
147 
148 	if (blocksize < 512 || filename == NULL || numblocks <= 0 ||
149 	    (read_op == 0 && write_op == 0)) {
150 		(void) fprintf(stderr,
151 		    "Required paramater(s) missing or invalid.\n");
152 		(void) usage();
153 	}
154 }
155 
156 /*
157  * Write blocksize * numblocks to the file using O_DIRECT.
158  */
159 static void *
write_thread(void * arg)160 write_thread(void *arg)
161 {
162 	size_t offset = 0;
163 	int total_data = blocksize * numblocks;
164 	int left = total_data;
165 	ssize_t wrote = 0;
166 	pthread_args_t *args = (pthread_args_t *)arg;
167 
168 	while (!args->entire_file_completed) {
169 		wrote = pwrite(fd, buf, blocksize, offset);
170 		if (wrote != blocksize) {
171 			if (err_expected)
172 				assert(errno == EIO);
173 			else
174 				exit(2);
175 		}
176 
177 		offset = ((offset + blocksize) % total_data);
178 		left -= blocksize;
179 
180 		if (left == 0)
181 			args->entire_file_completed = 1;
182 	}
183 
184 	pthread_exit(NULL);
185 }
186 
187 /*
188  * Read blocksize * numblocks to the file using O_DIRECT.
189  */
190 static void *
read_thread(void * arg)191 read_thread(void *arg)
192 {
193 	size_t offset = 0;
194 	int total_data = blocksize * numblocks;
195 	int left = total_data;
196 	ssize_t read = 0;
197 	pthread_args_t *args = (pthread_args_t *)arg;
198 
199 	while (!args->entire_file_completed) {
200 		read = pread(fd, buf, blocksize, offset);
201 		if (read != blocksize) {
202 			exit(2);
203 		}
204 
205 		offset = ((offset + blocksize) % total_data);
206 		left -= blocksize;
207 
208 		if (left == 0)
209 			args->entire_file_completed = 1;
210 	}
211 
212 	pthread_exit(NULL);
213 }
214 
215 /*
216  * Update the buffers contents with random data.
217  */
218 static void *
manipulate_buf_thread(void * arg)219 manipulate_buf_thread(void *arg)
220 {
221 	size_t rand_offset;
222 	char rand_char;
223 	pthread_args_t *args = (pthread_args_t *)arg;
224 
225 	while (!args->entire_file_completed) {
226 		rand_offset = (rand() % blocksize);
227 		rand_char = (rand() % (126 - 33) + 33);
228 		buf[rand_offset] = rand_char;
229 	}
230 
231 	pthread_exit(NULL);
232 }
233 
234 int
main(int argc,char * argv[])235 main(int argc, char *argv[])
236 {
237 	const char *datapattern = "0xdeadbeef";
238 	int fd_flags = O_DIRECT;
239 	mode_t mode = S_IRUSR | S_IWUSR;
240 	pthread_t io_thr;
241 	pthread_t manipul_thr;
242 	int left = blocksize;
243 	int offset = 0;
244 	int rc;
245 	pthread_args_t args = { 0 };
246 
247 	parse_options(argc, argv);
248 
249 	if (write_op) {
250 		fd_flags |= (O_WRONLY | O_CREAT);
251 	} else {
252 		fd_flags |= O_RDONLY;
253 	}
254 
255 	fd = open(filename, fd_flags, mode);
256 	if (fd == -1) {
257 		(void) fprintf(stderr, "%s, %s\n", execname, filename);
258 		perror("open");
259 		exit(2);
260 	}
261 
262 	int err = posix_memalign((void **)&buf, sysconf(_SC_PAGE_SIZE),
263 	    blocksize);
264 	if (err != 0) {
265 		(void) fprintf(stderr,
266 		    "%s: %s\n", execname, strerror(err));
267 		exit(2);
268 	}
269 
270 	if (write_op) {
271 		if (!randompattern) {
272 			/* Putting known data pattern in buffer */
273 			while (left) {
274 				size_t amt = MIN(strlen(datapattern), left);
275 				memcpy(&buf[offset], datapattern, amt);
276 				offset += amt;
277 				left -= amt;
278 			}
279 		} else {
280 			/* Putting random data in buffer */
281 			for (int i = 0; i < blocksize; i++)
282 				buf[i] = rand();
283 		}
284 	}
285 
286 	if ((rc = pthread_create(&manipul_thr, NULL, manipulate_buf_thread,
287 	    &args))) {
288 		fprintf(stderr, "error: pthreads_create, manipul_thr, "
289 		    "rc: %d\n", rc);
290 		exit(2);
291 	}
292 
293 	if (write_op) {
294 		/*
295 		 * Writing using O_DIRECT while manipulating the buffer contents
296 		 * until the entire file is written.
297 		 */
298 		if ((rc = pthread_create(&io_thr, NULL, write_thread, &args))) {
299 			fprintf(stderr, "error: pthreads_create, io_thr, "
300 			    "rc: %d\n", rc);
301 			exit(2);
302 		}
303 	} else {
304 		/*
305 		 * Reading using O_DIRECT while manipulating the buffer contents
306 		 * until the entire file is read.
307 		 */
308 		if ((rc = pthread_create(&io_thr, NULL, read_thread, &args))) {
309 			fprintf(stderr, "error: pthreads_create, io_thr, "
310 			    "rc: %d\n", rc);
311 			exit(2);
312 		}
313 	}
314 
315 	pthread_join(io_thr, NULL);
316 	pthread_join(manipul_thr, NULL);
317 
318 	assert(args.entire_file_completed == 1);
319 
320 	(void) close(fd);
321 
322 	free(buf);
323 
324 	return (0);
325 }
326