xref: /freebsd/sys/contrib/openzfs/tests/zfs-tests/cmd/manipulate_user_buffer.c (revision 96190b4fef3b4a0cc3ca0606b0c4e3e69a5e6717)
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) 2022 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 *outputfile = NULL;
43 static int blocksize = 131072; /* 128K */
44 static int wr_err_expected = 0;
45 static int numblocks = 100;
46 static char *execname = NULL;
47 static int print_usage = 0;
48 static int randompattern = 0;
49 static int ofd;
50 char *buf = NULL;
51 
52 typedef struct {
53 	int entire_file_written;
54 } pthread_args_t;
55 
56 static void
57 usage(void)
58 {
59 	(void) fprintf(stderr,
60 	    "usage %s -o outputfile [-b blocksize] [-e wr_error_expected]\n"
61 	    "         [-n numblocks] [-p randpattern] [-h help]\n"
62 	    "\n"
63 	    "Testing whether checksum verify works correctly for O_DIRECT.\n"
64 	    "when manipulating the contents of a userspace buffer.\n"
65 	    "\n"
66 	    "    outputfile:      File to write to.\n"
67 	    "    blocksize:       Size of each block to write (must be at \n"
68 	    "                     least >= 512).\n"
69 	    "    wr_err_expected: Whether pwrite() is expected to return EIO\n"
70 	    "                     while manipulating the contents of the\n"
71 	    "                     buffer.\n"
72 	    "    numblocks:       Total number of blocksized blocks to\n"
73 	    "                     write.\n"
74 	    "    randpattern:     Fill data buffer with random data. Default\n"
75 	    "                     behavior is to fill the buffer with the \n"
76 	    "                     known data pattern (0xdeadbeef).\n"
77 	    "    help:           Print usage information and exit.\n"
78 	    "\n"
79 	    "    Required parameters:\n"
80 	    "    outputfile\n"
81 	    "\n"
82 	    "    Default Values:\n"
83 	    "    blocksize       -> 131072\n"
84 	    "    wr_err_expexted -> false\n"
85 	    "    numblocks       -> 100\n"
86 	    "    randpattern     -> false\n",
87 	    execname);
88 	(void) exit(1);
89 }
90 
91 static void
92 parse_options(int argc, char *argv[])
93 {
94 	int c;
95 	int errflag = 0;
96 	extern char *optarg;
97 	extern int optind, optopt;
98 	execname = argv[0];
99 
100 	while ((c = getopt(argc, argv, "b:ehn:o:p")) != -1) {
101 		switch (c) {
102 			case 'b':
103 				blocksize = atoi(optarg);
104 				break;
105 
106 			case 'e':
107 				wr_err_expected = 1;
108 				break;
109 
110 			case 'h':
111 				print_usage = 1;
112 				break;
113 
114 			case 'n':
115 				numblocks = atoi(optarg);
116 				break;
117 
118 			case 'o':
119 				outputfile = optarg;
120 				break;
121 
122 			case 'p':
123 				randompattern = 1;
124 				break;
125 
126 			case ':':
127 				(void) fprintf(stderr,
128 				    "Option -%c requires an opertand\n",
129 				    optopt);
130 				errflag++;
131 				break;
132 			case '?':
133 			default:
134 				(void) fprintf(stderr,
135 				    "Unrecognized option: -%c\n", optopt);
136 				errflag++;
137 				break;
138 		}
139 	}
140 
141 	if (errflag || print_usage == 1)
142 		(void) usage();
143 
144 	if (blocksize < 512 || outputfile == NULL || numblocks <= 0) {
145 		(void) fprintf(stderr,
146 		    "Required paramater(s) missing or invalid.\n");
147 		(void) usage();
148 	}
149 }
150 
151 /*
152  * Write blocksize * numblocks to the file using O_DIRECT.
153  */
154 static void *
155 write_thread(void *arg)
156 {
157 	size_t offset = 0;
158 	int total_data = blocksize * numblocks;
159 	int left = total_data;
160 	ssize_t wrote = 0;
161 	pthread_args_t *args = (pthread_args_t *)arg;
162 
163 	while (!args->entire_file_written) {
164 		wrote = pwrite(ofd, buf, blocksize, offset);
165 		if (wrote != blocksize) {
166 			if (wr_err_expected)
167 				assert(errno == EIO);
168 			else
169 				exit(2);
170 		}
171 
172 		offset = ((offset + blocksize) % total_data);
173 		left -= blocksize;
174 
175 		if (left == 0)
176 			args->entire_file_written = 1;
177 	}
178 
179 	pthread_exit(NULL);
180 }
181 
182 /*
183  * Update the buffers contents with random data.
184  */
185 static void *
186 manipulate_buf_thread(void *arg)
187 {
188 	size_t rand_offset;
189 	char rand_char;
190 	pthread_args_t *args = (pthread_args_t *)arg;
191 
192 	while (!args->entire_file_written) {
193 		rand_offset = (rand() % blocksize);
194 		rand_char = (rand() % (126 - 33) + 33);
195 		buf[rand_offset] = rand_char;
196 	}
197 
198 	pthread_exit(NULL);
199 }
200 
201 int
202 main(int argc, char *argv[])
203 {
204 	const char *datapattern = "0xdeadbeef";
205 	int ofd_flags = O_WRONLY | O_CREAT | O_DIRECT;
206 	mode_t mode = S_IRUSR | S_IWUSR;
207 	pthread_t write_thr;
208 	pthread_t manipul_thr;
209 	int left = blocksize;
210 	int offset = 0;
211 	int rc;
212 	pthread_args_t args = { 0 };
213 
214 	parse_options(argc, argv);
215 
216 	ofd = open(outputfile, ofd_flags, mode);
217 	if (ofd == -1) {
218 		(void) fprintf(stderr, "%s, %s\n", execname, outputfile);
219 		perror("open");
220 		exit(2);
221 	}
222 
223 	int err = posix_memalign((void **)&buf, sysconf(_SC_PAGE_SIZE),
224 	    blocksize);
225 	if (err != 0) {
226 		(void) fprintf(stderr,
227 		    "%s: %s\n", execname, strerror(err));
228 		exit(2);
229 	}
230 
231 	if (!randompattern) {
232 		/* Putting known data pattern in buffer */
233 		while (left) {
234 			size_t amt = MIN(strlen(datapattern), left);
235 			memcpy(&buf[offset], datapattern, amt);
236 			offset += amt;
237 			left -= amt;
238 		}
239 	} else {
240 		/* Putting random data in buffer */
241 		for (int i = 0; i < blocksize; i++)
242 			buf[i] = rand();
243 	}
244 
245 	/*
246 	 * Writing using O_DIRECT while manipulating the buffer contents until
247 	 * the entire file is written.
248 	 */
249 	if ((rc = pthread_create(&manipul_thr, NULL, manipulate_buf_thread,
250 	    &args))) {
251 		fprintf(stderr, "error: pthreads_create, manipul_thr, "
252 		    "rc: %d\n", rc);
253 		exit(2);
254 	}
255 
256 	if ((rc = pthread_create(&write_thr, NULL, write_thread, &args))) {
257 		fprintf(stderr, "error: pthreads_create, write_thr, "
258 		    "rc: %d\n", rc);
259 		exit(2);
260 	}
261 
262 	pthread_join(write_thr, NULL);
263 	pthread_join(manipul_thr, NULL);
264 
265 	assert(args.entire_file_written == 1);
266 
267 	(void) close(ofd);
268 
269 	free(buf);
270 
271 	return (0);
272 }
273