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