xref: /freebsd-src/sys/contrib/openzfs/tests/zfs-tests/cmd/manipulate_user_buffer.c (revision 87bf66d4a7488c496af110d4d05cc0273d49f82e)
17a7741afSMartin Matuska /*
27a7741afSMartin Matuska  * CDDL HEADER START
37a7741afSMartin Matuska  *
47a7741afSMartin Matuska  * The contents of this file are subject to the terms of the
57a7741afSMartin Matuska  * Common Development and Distribution License (the "License").
67a7741afSMartin Matuska  * You may not use this file except in compliance with the License.
77a7741afSMartin Matuska  *
87a7741afSMartin Matuska  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97a7741afSMartin Matuska  * or https://opensource.org/licenses/CDDL-1.0.
107a7741afSMartin Matuska  * See the License for the specific language governing permissions
117a7741afSMartin Matuska  * and limitations under the License.
127a7741afSMartin Matuska  *
137a7741afSMartin Matuska  * When distributing Covered Code, include this CDDL HEADER in each
147a7741afSMartin Matuska  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157a7741afSMartin Matuska  * If applicable, add the following below this CDDL HEADER, with the
167a7741afSMartin Matuska  * fields enclosed by brackets "[]" replaced with your own identifying
177a7741afSMartin Matuska  * information: Portions Copyright [yyyy] [name of copyright owner]
187a7741afSMartin Matuska  *
197a7741afSMartin Matuska  * CDDL HEADER END
207a7741afSMartin Matuska  */
217a7741afSMartin Matuska 
227a7741afSMartin Matuska /*
23*87bf66d4SMartin Matuska  * Copyright (c) 2024 by Triad National Security, LLC.
247a7741afSMartin Matuska  */
257a7741afSMartin Matuska 
267a7741afSMartin Matuska #include <sys/types.h>
277a7741afSMartin Matuska #include <sys/stat.h>
287a7741afSMartin Matuska #include <errno.h>
297a7741afSMartin Matuska #include <fcntl.h>
307a7741afSMartin Matuska #include <stdio.h>
317a7741afSMartin Matuska #include <unistd.h>
327a7741afSMartin Matuska #include <stdlib.h>
337a7741afSMartin Matuska #include <string.h>
347a7741afSMartin Matuska #include <time.h>
357a7741afSMartin Matuska #include <pthread.h>
367a7741afSMartin Matuska #include <assert.h>
377a7741afSMartin Matuska 
387a7741afSMartin Matuska #ifndef MIN
397a7741afSMartin Matuska #define	MIN(a, b)	((a) < (b)) ? (a) : (b)
407a7741afSMartin Matuska #endif
417a7741afSMartin Matuska 
42*87bf66d4SMartin Matuska static char *filename = NULL;
437a7741afSMartin Matuska static int blocksize = 131072; /* 128K */
44*87bf66d4SMartin Matuska static int err_expected = 0;
45*87bf66d4SMartin Matuska static int read_op = 0;
46*87bf66d4SMartin Matuska static int write_op = 0;
477a7741afSMartin Matuska static int numblocks = 100;
487a7741afSMartin Matuska static char *execname = NULL;
497a7741afSMartin Matuska static int print_usage = 0;
507a7741afSMartin Matuska static int randompattern = 0;
51*87bf66d4SMartin Matuska static int fd;
527a7741afSMartin Matuska char *buf = NULL;
537a7741afSMartin Matuska 
547a7741afSMartin Matuska typedef struct {
55*87bf66d4SMartin Matuska 	int entire_file_completed;
567a7741afSMartin Matuska } pthread_args_t;
577a7741afSMartin Matuska 
587a7741afSMartin Matuska static void
597a7741afSMartin Matuska usage(void)
607a7741afSMartin Matuska {
617a7741afSMartin Matuska 	(void) fprintf(stderr,
62*87bf66d4SMartin Matuska 	    "usage %s -f filename [-b blocksize] [-e wr_error_expected]\n"
63*87bf66d4SMartin Matuska 	    "         [-n numblocks] [-p randompattern] -r read_op \n"
64*87bf66d4SMartin Matuska 	    "         -w write_op [-h help]\n"
657a7741afSMartin Matuska 	    "\n"
667a7741afSMartin Matuska 	    "Testing whether checksum verify works correctly for O_DIRECT.\n"
677a7741afSMartin Matuska 	    "when manipulating the contents of a userspace buffer.\n"
687a7741afSMartin Matuska 	    "\n"
69*87bf66d4SMartin Matuska 	    "    filename:       File to read or write to.\n"
707a7741afSMartin Matuska 	    "    blocksize:      Size of each block to write (must be at \n"
717a7741afSMartin Matuska 	    "                    least >= 512).\n"
72*87bf66d4SMartin Matuska 	    "    err_expected:   Whether write() is expected to return EIO\n"
737a7741afSMartin Matuska 	    "                    while manipulating the contents of the\n"
747a7741afSMartin Matuska 	    "                    buffer.\n"
757a7741afSMartin Matuska 	    "    numblocks:      Total number of blocksized blocks to\n"
767a7741afSMartin Matuska 	    "                    write.\n"
77*87bf66d4SMartin Matuska 	    "    read_op:        Perform reads to the filename file while\n"
78*87bf66d4SMartin Matuska 	    "                    while manipulating the buffer contents\n"
79*87bf66d4SMartin Matuska 	    "    write_op:       Perform writes to the filename file while\n"
80*87bf66d4SMartin Matuska 	    "                    manipulating the buffer contents\n"
81*87bf66d4SMartin Matuska 	    "    randompattern:  Fill data buffer with random data for \n"
82*87bf66d4SMartin Matuska 	    "                    writes. Default behavior is to fill the \n"
83*87bf66d4SMartin Matuska 	    "                    buffer with known data pattern (0xdeadbeef)\n"
847a7741afSMartin Matuska 	    "    help:           Print usage information and exit.\n"
857a7741afSMartin Matuska 	    "\n"
867a7741afSMartin Matuska 	    "    Required parameters:\n"
87*87bf66d4SMartin Matuska 	    "    filename\n"
88*87bf66d4SMartin Matuska 	    "    read_op or write_op\n"
897a7741afSMartin Matuska 	    "\n"
907a7741afSMartin Matuska 	    "    Default Values:\n"
917a7741afSMartin Matuska 	    "    blocksize       -> 131072\n"
927a7741afSMartin Matuska 	    "    wr_err_expexted -> false\n"
937a7741afSMartin Matuska 	    "    numblocks       -> 100\n"
94*87bf66d4SMartin Matuska 	    "    randompattern   -> false\n",
957a7741afSMartin Matuska 	    execname);
967a7741afSMartin Matuska 	(void) exit(1);
977a7741afSMartin Matuska }
987a7741afSMartin Matuska 
997a7741afSMartin Matuska static void
1007a7741afSMartin Matuska parse_options(int argc, char *argv[])
1017a7741afSMartin Matuska {
1027a7741afSMartin Matuska 	int c;
1037a7741afSMartin Matuska 	int errflag = 0;
1047a7741afSMartin Matuska 	extern char *optarg;
1057a7741afSMartin Matuska 	extern int optind, optopt;
1067a7741afSMartin Matuska 	execname = argv[0];
1077a7741afSMartin Matuska 
108*87bf66d4SMartin Matuska 	while ((c = getopt(argc, argv, "b:ef:hn:rw")) != -1) {
1097a7741afSMartin Matuska 		switch (c) {
1107a7741afSMartin Matuska 			case 'b':
1117a7741afSMartin Matuska 				blocksize = atoi(optarg);
1127a7741afSMartin Matuska 				break;
1137a7741afSMartin Matuska 
1147a7741afSMartin Matuska 			case 'e':
115*87bf66d4SMartin Matuska 				err_expected = 1;
1167a7741afSMartin Matuska 				break;
1177a7741afSMartin Matuska 
118*87bf66d4SMartin Matuska 			case 'f':
119*87bf66d4SMartin Matuska 				filename = optarg;
120*87bf66d4SMartin Matuska 				break;
121*87bf66d4SMartin Matuska 
122*87bf66d4SMartin Matuska 
1237a7741afSMartin Matuska 			case 'h':
1247a7741afSMartin Matuska 				print_usage = 1;
1257a7741afSMartin Matuska 				break;
1267a7741afSMartin Matuska 
1277a7741afSMartin Matuska 			case 'n':
1287a7741afSMartin Matuska 				numblocks = atoi(optarg);
1297a7741afSMartin Matuska 				break;
1307a7741afSMartin Matuska 
131*87bf66d4SMartin Matuska 			case 'r':
132*87bf66d4SMartin Matuska 				read_op = 1;
1337a7741afSMartin Matuska 				break;
1347a7741afSMartin Matuska 
135*87bf66d4SMartin Matuska 			case 'w':
136*87bf66d4SMartin Matuska 				write_op = 1;
1377a7741afSMartin Matuska 				break;
1387a7741afSMartin Matuska 
1397a7741afSMartin Matuska 			case ':':
1407a7741afSMartin Matuska 				(void) fprintf(stderr,
1417a7741afSMartin Matuska 				    "Option -%c requires an opertand\n",
1427a7741afSMartin Matuska 				    optopt);
1437a7741afSMartin Matuska 				errflag++;
1447a7741afSMartin Matuska 				break;
1457a7741afSMartin Matuska 			case '?':
1467a7741afSMartin Matuska 			default:
1477a7741afSMartin Matuska 				(void) fprintf(stderr,
1487a7741afSMartin Matuska 				    "Unrecognized option: -%c\n", optopt);
1497a7741afSMartin Matuska 				errflag++;
1507a7741afSMartin Matuska 				break;
1517a7741afSMartin Matuska 		}
1527a7741afSMartin Matuska 	}
1537a7741afSMartin Matuska 
1547a7741afSMartin Matuska 	if (errflag || print_usage == 1)
1557a7741afSMartin Matuska 		(void) usage();
1567a7741afSMartin Matuska 
157*87bf66d4SMartin Matuska 	if (blocksize < 512 || filename == NULL || numblocks <= 0 ||
158*87bf66d4SMartin Matuska 	    (read_op == 0 && write_op == 0)) {
1597a7741afSMartin Matuska 		(void) fprintf(stderr,
1607a7741afSMartin Matuska 		    "Required paramater(s) missing or invalid.\n");
1617a7741afSMartin Matuska 		(void) usage();
1627a7741afSMartin Matuska 	}
1637a7741afSMartin Matuska }
1647a7741afSMartin Matuska 
1657a7741afSMartin Matuska /*
1667a7741afSMartin Matuska  * Write blocksize * numblocks to the file using O_DIRECT.
1677a7741afSMartin Matuska  */
1687a7741afSMartin Matuska static void *
1697a7741afSMartin Matuska write_thread(void *arg)
1707a7741afSMartin Matuska {
1717a7741afSMartin Matuska 	size_t offset = 0;
1727a7741afSMartin Matuska 	int total_data = blocksize * numblocks;
1737a7741afSMartin Matuska 	int left = total_data;
1747a7741afSMartin Matuska 	ssize_t wrote = 0;
1757a7741afSMartin Matuska 	pthread_args_t *args = (pthread_args_t *)arg;
1767a7741afSMartin Matuska 
177*87bf66d4SMartin Matuska 	while (!args->entire_file_completed) {
178*87bf66d4SMartin Matuska 		wrote = pwrite(fd, buf, blocksize, offset);
1797a7741afSMartin Matuska 		if (wrote != blocksize) {
180*87bf66d4SMartin Matuska 			if (err_expected)
1817a7741afSMartin Matuska 				assert(errno == EIO);
1827a7741afSMartin Matuska 			else
1837a7741afSMartin Matuska 				exit(2);
1847a7741afSMartin Matuska 		}
1857a7741afSMartin Matuska 
1867a7741afSMartin Matuska 		offset = ((offset + blocksize) % total_data);
1877a7741afSMartin Matuska 		left -= blocksize;
1887a7741afSMartin Matuska 
1897a7741afSMartin Matuska 		if (left == 0)
190*87bf66d4SMartin Matuska 			args->entire_file_completed = 1;
191*87bf66d4SMartin Matuska 	}
192*87bf66d4SMartin Matuska 
193*87bf66d4SMartin Matuska 	pthread_exit(NULL);
194*87bf66d4SMartin Matuska }
195*87bf66d4SMartin Matuska 
196*87bf66d4SMartin Matuska /*
197*87bf66d4SMartin Matuska  * Read blocksize * numblocks to the file using O_DIRECT.
198*87bf66d4SMartin Matuska  */
199*87bf66d4SMartin Matuska static void *
200*87bf66d4SMartin Matuska read_thread(void *arg)
201*87bf66d4SMartin Matuska {
202*87bf66d4SMartin Matuska 	size_t offset = 0;
203*87bf66d4SMartin Matuska 	int total_data = blocksize * numblocks;
204*87bf66d4SMartin Matuska 	int left = total_data;
205*87bf66d4SMartin Matuska 	ssize_t read = 0;
206*87bf66d4SMartin Matuska 	pthread_args_t *args = (pthread_args_t *)arg;
207*87bf66d4SMartin Matuska 
208*87bf66d4SMartin Matuska 	while (!args->entire_file_completed) {
209*87bf66d4SMartin Matuska 		read = pread(fd, buf, blocksize, offset);
210*87bf66d4SMartin Matuska 		if (read != blocksize) {
211*87bf66d4SMartin Matuska 			exit(2);
212*87bf66d4SMartin Matuska 		}
213*87bf66d4SMartin Matuska 
214*87bf66d4SMartin Matuska 		offset = ((offset + blocksize) % total_data);
215*87bf66d4SMartin Matuska 		left -= blocksize;
216*87bf66d4SMartin Matuska 
217*87bf66d4SMartin Matuska 		if (left == 0)
218*87bf66d4SMartin Matuska 			args->entire_file_completed = 1;
2197a7741afSMartin Matuska 	}
2207a7741afSMartin Matuska 
2217a7741afSMartin Matuska 	pthread_exit(NULL);
2227a7741afSMartin Matuska }
2237a7741afSMartin Matuska 
2247a7741afSMartin Matuska /*
2257a7741afSMartin Matuska  * Update the buffers contents with random data.
2267a7741afSMartin Matuska  */
2277a7741afSMartin Matuska static void *
2287a7741afSMartin Matuska manipulate_buf_thread(void *arg)
2297a7741afSMartin Matuska {
2307a7741afSMartin Matuska 	size_t rand_offset;
2317a7741afSMartin Matuska 	char rand_char;
2327a7741afSMartin Matuska 	pthread_args_t *args = (pthread_args_t *)arg;
2337a7741afSMartin Matuska 
234*87bf66d4SMartin Matuska 	while (!args->entire_file_completed) {
2357a7741afSMartin Matuska 		rand_offset = (rand() % blocksize);
2367a7741afSMartin Matuska 		rand_char = (rand() % (126 - 33) + 33);
2377a7741afSMartin Matuska 		buf[rand_offset] = rand_char;
2387a7741afSMartin Matuska 	}
2397a7741afSMartin Matuska 
2407a7741afSMartin Matuska 	pthread_exit(NULL);
2417a7741afSMartin Matuska }
2427a7741afSMartin Matuska 
2437a7741afSMartin Matuska int
2447a7741afSMartin Matuska main(int argc, char *argv[])
2457a7741afSMartin Matuska {
2467a7741afSMartin Matuska 	const char *datapattern = "0xdeadbeef";
247*87bf66d4SMartin Matuska 	int fd_flags = O_DIRECT;
2487a7741afSMartin Matuska 	mode_t mode = S_IRUSR | S_IWUSR;
249*87bf66d4SMartin Matuska 	pthread_t io_thr;
2507a7741afSMartin Matuska 	pthread_t manipul_thr;
2517a7741afSMartin Matuska 	int left = blocksize;
2527a7741afSMartin Matuska 	int offset = 0;
2537a7741afSMartin Matuska 	int rc;
2547a7741afSMartin Matuska 	pthread_args_t args = { 0 };
2557a7741afSMartin Matuska 
2567a7741afSMartin Matuska 	parse_options(argc, argv);
2577a7741afSMartin Matuska 
258*87bf66d4SMartin Matuska 	if (write_op) {
259*87bf66d4SMartin Matuska 		fd_flags |= (O_WRONLY | O_CREAT);
260*87bf66d4SMartin Matuska 	} else {
261*87bf66d4SMartin Matuska 		fd_flags |= O_RDONLY;
262*87bf66d4SMartin Matuska 	}
263*87bf66d4SMartin Matuska 
264*87bf66d4SMartin Matuska 	fd = open(filename, fd_flags, mode);
265*87bf66d4SMartin Matuska 	if (fd == -1) {
266*87bf66d4SMartin Matuska 		(void) fprintf(stderr, "%s, %s\n", execname, filename);
2677a7741afSMartin Matuska 		perror("open");
2687a7741afSMartin Matuska 		exit(2);
2697a7741afSMartin Matuska 	}
2707a7741afSMartin Matuska 
2717a7741afSMartin Matuska 	int err = posix_memalign((void **)&buf, sysconf(_SC_PAGE_SIZE),
2727a7741afSMartin Matuska 	    blocksize);
2737a7741afSMartin Matuska 	if (err != 0) {
2747a7741afSMartin Matuska 		(void) fprintf(stderr,
2757a7741afSMartin Matuska 		    "%s: %s\n", execname, strerror(err));
2767a7741afSMartin Matuska 		exit(2);
2777a7741afSMartin Matuska 	}
2787a7741afSMartin Matuska 
279*87bf66d4SMartin Matuska 	if (write_op) {
2807a7741afSMartin Matuska 		if (!randompattern) {
2817a7741afSMartin Matuska 			/* Putting known data pattern in buffer */
2827a7741afSMartin Matuska 			while (left) {
2837a7741afSMartin Matuska 				size_t amt = MIN(strlen(datapattern), left);
2847a7741afSMartin Matuska 				memcpy(&buf[offset], datapattern, amt);
2857a7741afSMartin Matuska 				offset += amt;
2867a7741afSMartin Matuska 				left -= amt;
2877a7741afSMartin Matuska 			}
2887a7741afSMartin Matuska 		} else {
2897a7741afSMartin Matuska 			/* Putting random data in buffer */
2907a7741afSMartin Matuska 			for (int i = 0; i < blocksize; i++)
2917a7741afSMartin Matuska 				buf[i] = rand();
2927a7741afSMartin Matuska 		}
293*87bf66d4SMartin Matuska 	}
2947a7741afSMartin Matuska 
2957a7741afSMartin Matuska 	if ((rc = pthread_create(&manipul_thr, NULL, manipulate_buf_thread,
2967a7741afSMartin Matuska 	    &args))) {
2977a7741afSMartin Matuska 		fprintf(stderr, "error: pthreads_create, manipul_thr, "
2987a7741afSMartin Matuska 		    "rc: %d\n", rc);
2997a7741afSMartin Matuska 		exit(2);
3007a7741afSMartin Matuska 	}
3017a7741afSMartin Matuska 
302*87bf66d4SMartin Matuska 	if (write_op) {
303*87bf66d4SMartin Matuska 		/*
304*87bf66d4SMartin Matuska 		 * Writing using O_DIRECT while manipulating the buffer contents
305*87bf66d4SMartin Matuska 		 * until the entire file is written.
306*87bf66d4SMartin Matuska 		 */
307*87bf66d4SMartin Matuska 		if ((rc = pthread_create(&io_thr, NULL, write_thread, &args))) {
308*87bf66d4SMartin Matuska 			fprintf(stderr, "error: pthreads_create, io_thr, "
3097a7741afSMartin Matuska 			    "rc: %d\n", rc);
3107a7741afSMartin Matuska 			exit(2);
3117a7741afSMartin Matuska 		}
312*87bf66d4SMartin Matuska 	} else {
313*87bf66d4SMartin Matuska 		/*
314*87bf66d4SMartin Matuska 		 * Reading using O_DIRECT while manipulating the buffer contents
315*87bf66d4SMartin Matuska 		 * until the entire file is read.
316*87bf66d4SMartin Matuska 		 */
317*87bf66d4SMartin Matuska 		if ((rc = pthread_create(&io_thr, NULL, read_thread, &args))) {
318*87bf66d4SMartin Matuska 			fprintf(stderr, "error: pthreads_create, io_thr, "
319*87bf66d4SMartin Matuska 			    "rc: %d\n", rc);
320*87bf66d4SMartin Matuska 			exit(2);
321*87bf66d4SMartin Matuska 		}
322*87bf66d4SMartin Matuska 	}
3237a7741afSMartin Matuska 
324*87bf66d4SMartin Matuska 	pthread_join(io_thr, NULL);
3257a7741afSMartin Matuska 	pthread_join(manipul_thr, NULL);
3267a7741afSMartin Matuska 
327*87bf66d4SMartin Matuska 	assert(args.entire_file_completed == 1);
3287a7741afSMartin Matuska 
329*87bf66d4SMartin Matuska 	(void) close(fd);
3307a7741afSMartin Matuska 
3317a7741afSMartin Matuska 	free(buf);
3327a7741afSMartin Matuska 
3337a7741afSMartin Matuska 	return (0);
3347a7741afSMartin Matuska }
335