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