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