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