Always validate checksums for Direct I/O reads
[zfs.git] / tests / zfs-tests / cmd / manipulate_user_buffer.c
blob17358109444357894e4dc492fcf7c1445d9055fb
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 2024 by Triad National Security, LLC.
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>
38 #ifndef MIN
39 #define MIN(a, b) ((a) < (b)) ? (a) : (b)
40 #endif
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;
54 typedef struct {
55 int entire_file_completed;
56 } pthread_args_t;
58 static void
59 usage(void)
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);
99 static void
100 parse_options(int argc, char *argv[])
102 int c;
103 int errflag = 0;
104 extern char *optarg;
105 extern int optind, optopt;
106 execname = argv[0];
108 while ((c = getopt(argc, argv, "b:ef:hn:rw")) != -1) {
109 switch (c) {
110 case 'b':
111 blocksize = atoi(optarg);
112 break;
114 case 'e':
115 err_expected = 1;
116 break;
118 case 'f':
119 filename = optarg;
120 break;
123 case 'h':
124 print_usage = 1;
125 break;
127 case 'n':
128 numblocks = atoi(optarg);
129 break;
131 case 'r':
132 read_op = 1;
133 break;
135 case 'w':
136 write_op = 1;
137 break;
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;
154 if (errflag || print_usage == 1)
155 (void) usage();
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();
166 * Write blocksize * numblocks to the file using O_DIRECT.
168 static void *
169 write_thread(void *arg)
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;
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);
186 offset = ((offset + blocksize) % total_data);
187 left -= blocksize;
189 if (left == 0)
190 args->entire_file_completed = 1;
193 pthread_exit(NULL);
197 * Read blocksize * numblocks to the file using O_DIRECT.
199 static void *
200 read_thread(void *arg)
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;
208 while (!args->entire_file_completed) {
209 read = pread(fd, buf, blocksize, offset);
210 if (read != blocksize) {
211 exit(2);
214 offset = ((offset + blocksize) % total_data);
215 left -= blocksize;
217 if (left == 0)
218 args->entire_file_completed = 1;
221 pthread_exit(NULL);
225 * Update the buffers contents with random data.
227 static void *
228 manipulate_buf_thread(void *arg)
230 size_t rand_offset;
231 char rand_char;
232 pthread_args_t *args = (pthread_args_t *)arg;
234 while (!args->entire_file_completed) {
235 rand_offset = (rand() % blocksize);
236 rand_char = (rand() % (126 - 33) + 33);
237 buf[rand_offset] = rand_char;
240 pthread_exit(NULL);
244 main(int argc, char *argv[])
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 };
256 parse_options(argc, argv);
258 if (write_op) {
259 fd_flags |= (O_WRONLY | O_CREAT);
260 } else {
261 fd_flags |= O_RDONLY;
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);
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);
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;
288 } else {
289 /* Putting random data in buffer */
290 for (int i = 0; i < blocksize; i++)
291 buf[i] = rand();
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);
302 if (write_op) {
304 * Writing using O_DIRECT while manipulating the buffer contents
305 * until the entire file is written.
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);
312 } else {
314 * Reading using O_DIRECT while manipulating the buffer contents
315 * until the entire file is read.
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);
324 pthread_join(io_thr, NULL);
325 pthread_join(manipul_thr, NULL);
327 assert(args.entire_file_completed == 1);
329 (void) close(fd);
331 free(buf);
333 return (0);