17a7741afSMartin Matuska /* 27a7741afSMartin Matuska * CDDL HEADER START 37a7741afSMartin Matuska * 47a7741afSMartin Matuska * The contents of this file are subject to the terms of the 57a7741afSMartin Matuska * Common Development and Distribution License (the "License"). 67a7741afSMartin Matuska * You may not use this file except in compliance with the License. 77a7741afSMartin Matuska * 87a7741afSMartin Matuska * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97a7741afSMartin Matuska * or https://opensource.org/licenses/CDDL-1.0. 107a7741afSMartin Matuska * See the License for the specific language governing permissions 117a7741afSMartin Matuska * and limitations under the License. 127a7741afSMartin Matuska * 137a7741afSMartin Matuska * When distributing Covered Code, include this CDDL HEADER in each 147a7741afSMartin Matuska * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157a7741afSMartin Matuska * If applicable, add the following below this CDDL HEADER, with the 167a7741afSMartin Matuska * fields enclosed by brackets "[]" replaced with your own identifying 177a7741afSMartin Matuska * information: Portions Copyright [yyyy] [name of copyright owner] 187a7741afSMartin Matuska * 197a7741afSMartin Matuska * CDDL HEADER END 207a7741afSMartin Matuska */ 217a7741afSMartin Matuska 227a7741afSMartin Matuska /* 23*87bf66d4SMartin Matuska * Copyright (c) 2024 by Triad National Security, LLC. 247a7741afSMartin Matuska */ 257a7741afSMartin Matuska 267a7741afSMartin Matuska #include <sys/types.h> 277a7741afSMartin Matuska #include <sys/stat.h> 287a7741afSMartin Matuska #include <errno.h> 297a7741afSMartin Matuska #include <fcntl.h> 307a7741afSMartin Matuska #include <stdio.h> 317a7741afSMartin Matuska #include <unistd.h> 327a7741afSMartin Matuska #include <stdlib.h> 337a7741afSMartin Matuska #include <string.h> 347a7741afSMartin Matuska #include <time.h> 357a7741afSMartin Matuska #include <pthread.h> 367a7741afSMartin Matuska #include <assert.h> 377a7741afSMartin Matuska 387a7741afSMartin Matuska #ifndef MIN 397a7741afSMartin Matuska #define MIN(a, b) ((a) < (b)) ? (a) : (b) 407a7741afSMartin Matuska #endif 417a7741afSMartin Matuska 42*87bf66d4SMartin Matuska static char *filename = NULL; 437a7741afSMartin Matuska static int blocksize = 131072; /* 128K */ 44*87bf66d4SMartin Matuska static int err_expected = 0; 45*87bf66d4SMartin Matuska static int read_op = 0; 46*87bf66d4SMartin Matuska static int write_op = 0; 477a7741afSMartin Matuska static int numblocks = 100; 487a7741afSMartin Matuska static char *execname = NULL; 497a7741afSMartin Matuska static int print_usage = 0; 507a7741afSMartin Matuska static int randompattern = 0; 51*87bf66d4SMartin Matuska static int fd; 527a7741afSMartin Matuska char *buf = NULL; 537a7741afSMartin Matuska 547a7741afSMartin Matuska typedef struct { 55*87bf66d4SMartin Matuska int entire_file_completed; 567a7741afSMartin Matuska } pthread_args_t; 577a7741afSMartin Matuska 587a7741afSMartin Matuska static void 597a7741afSMartin Matuska usage(void) 607a7741afSMartin Matuska { 617a7741afSMartin Matuska (void) fprintf(stderr, 62*87bf66d4SMartin Matuska "usage %s -f filename [-b blocksize] [-e wr_error_expected]\n" 63*87bf66d4SMartin Matuska " [-n numblocks] [-p randompattern] -r read_op \n" 64*87bf66d4SMartin Matuska " -w write_op [-h help]\n" 657a7741afSMartin Matuska "\n" 667a7741afSMartin Matuska "Testing whether checksum verify works correctly for O_DIRECT.\n" 677a7741afSMartin Matuska "when manipulating the contents of a userspace buffer.\n" 687a7741afSMartin Matuska "\n" 69*87bf66d4SMartin Matuska " filename: File to read or write to.\n" 707a7741afSMartin Matuska " blocksize: Size of each block to write (must be at \n" 717a7741afSMartin Matuska " least >= 512).\n" 72*87bf66d4SMartin Matuska " err_expected: Whether write() is expected to return EIO\n" 737a7741afSMartin Matuska " while manipulating the contents of the\n" 747a7741afSMartin Matuska " buffer.\n" 757a7741afSMartin Matuska " numblocks: Total number of blocksized blocks to\n" 767a7741afSMartin Matuska " write.\n" 77*87bf66d4SMartin Matuska " read_op: Perform reads to the filename file while\n" 78*87bf66d4SMartin Matuska " while manipulating the buffer contents\n" 79*87bf66d4SMartin Matuska " write_op: Perform writes to the filename file while\n" 80*87bf66d4SMartin Matuska " manipulating the buffer contents\n" 81*87bf66d4SMartin Matuska " randompattern: Fill data buffer with random data for \n" 82*87bf66d4SMartin Matuska " writes. Default behavior is to fill the \n" 83*87bf66d4SMartin Matuska " buffer with known data pattern (0xdeadbeef)\n" 847a7741afSMartin Matuska " help: Print usage information and exit.\n" 857a7741afSMartin Matuska "\n" 867a7741afSMartin Matuska " Required parameters:\n" 87*87bf66d4SMartin Matuska " filename\n" 88*87bf66d4SMartin Matuska " read_op or write_op\n" 897a7741afSMartin Matuska "\n" 907a7741afSMartin Matuska " Default Values:\n" 917a7741afSMartin Matuska " blocksize -> 131072\n" 927a7741afSMartin Matuska " wr_err_expexted -> false\n" 937a7741afSMartin Matuska " numblocks -> 100\n" 94*87bf66d4SMartin Matuska " randompattern -> false\n", 957a7741afSMartin Matuska execname); 967a7741afSMartin Matuska (void) exit(1); 977a7741afSMartin Matuska } 987a7741afSMartin Matuska 997a7741afSMartin Matuska static void 1007a7741afSMartin Matuska parse_options(int argc, char *argv[]) 1017a7741afSMartin Matuska { 1027a7741afSMartin Matuska int c; 1037a7741afSMartin Matuska int errflag = 0; 1047a7741afSMartin Matuska extern char *optarg; 1057a7741afSMartin Matuska extern int optind, optopt; 1067a7741afSMartin Matuska execname = argv[0]; 1077a7741afSMartin Matuska 108*87bf66d4SMartin Matuska while ((c = getopt(argc, argv, "b:ef:hn:rw")) != -1) { 1097a7741afSMartin Matuska switch (c) { 1107a7741afSMartin Matuska case 'b': 1117a7741afSMartin Matuska blocksize = atoi(optarg); 1127a7741afSMartin Matuska break; 1137a7741afSMartin Matuska 1147a7741afSMartin Matuska case 'e': 115*87bf66d4SMartin Matuska err_expected = 1; 1167a7741afSMartin Matuska break; 1177a7741afSMartin Matuska 118*87bf66d4SMartin Matuska case 'f': 119*87bf66d4SMartin Matuska filename = optarg; 120*87bf66d4SMartin Matuska break; 121*87bf66d4SMartin Matuska 122*87bf66d4SMartin Matuska 1237a7741afSMartin Matuska case 'h': 1247a7741afSMartin Matuska print_usage = 1; 1257a7741afSMartin Matuska break; 1267a7741afSMartin Matuska 1277a7741afSMartin Matuska case 'n': 1287a7741afSMartin Matuska numblocks = atoi(optarg); 1297a7741afSMartin Matuska break; 1307a7741afSMartin Matuska 131*87bf66d4SMartin Matuska case 'r': 132*87bf66d4SMartin Matuska read_op = 1; 1337a7741afSMartin Matuska break; 1347a7741afSMartin Matuska 135*87bf66d4SMartin Matuska case 'w': 136*87bf66d4SMartin Matuska write_op = 1; 1377a7741afSMartin Matuska break; 1387a7741afSMartin Matuska 1397a7741afSMartin Matuska case ':': 1407a7741afSMartin Matuska (void) fprintf(stderr, 1417a7741afSMartin Matuska "Option -%c requires an opertand\n", 1427a7741afSMartin Matuska optopt); 1437a7741afSMartin Matuska errflag++; 1447a7741afSMartin Matuska break; 1457a7741afSMartin Matuska case '?': 1467a7741afSMartin Matuska default: 1477a7741afSMartin Matuska (void) fprintf(stderr, 1487a7741afSMartin Matuska "Unrecognized option: -%c\n", optopt); 1497a7741afSMartin Matuska errflag++; 1507a7741afSMartin Matuska break; 1517a7741afSMartin Matuska } 1527a7741afSMartin Matuska } 1537a7741afSMartin Matuska 1547a7741afSMartin Matuska if (errflag || print_usage == 1) 1557a7741afSMartin Matuska (void) usage(); 1567a7741afSMartin Matuska 157*87bf66d4SMartin Matuska if (blocksize < 512 || filename == NULL || numblocks <= 0 || 158*87bf66d4SMartin Matuska (read_op == 0 && write_op == 0)) { 1597a7741afSMartin Matuska (void) fprintf(stderr, 1607a7741afSMartin Matuska "Required paramater(s) missing or invalid.\n"); 1617a7741afSMartin Matuska (void) usage(); 1627a7741afSMartin Matuska } 1637a7741afSMartin Matuska } 1647a7741afSMartin Matuska 1657a7741afSMartin Matuska /* 1667a7741afSMartin Matuska * Write blocksize * numblocks to the file using O_DIRECT. 1677a7741afSMartin Matuska */ 1687a7741afSMartin Matuska static void * 1697a7741afSMartin Matuska write_thread(void *arg) 1707a7741afSMartin Matuska { 1717a7741afSMartin Matuska size_t offset = 0; 1727a7741afSMartin Matuska int total_data = blocksize * numblocks; 1737a7741afSMartin Matuska int left = total_data; 1747a7741afSMartin Matuska ssize_t wrote = 0; 1757a7741afSMartin Matuska pthread_args_t *args = (pthread_args_t *)arg; 1767a7741afSMartin Matuska 177*87bf66d4SMartin Matuska while (!args->entire_file_completed) { 178*87bf66d4SMartin Matuska wrote = pwrite(fd, buf, blocksize, offset); 1797a7741afSMartin Matuska if (wrote != blocksize) { 180*87bf66d4SMartin Matuska if (err_expected) 1817a7741afSMartin Matuska assert(errno == EIO); 1827a7741afSMartin Matuska else 1837a7741afSMartin Matuska exit(2); 1847a7741afSMartin Matuska } 1857a7741afSMartin Matuska 1867a7741afSMartin Matuska offset = ((offset + blocksize) % total_data); 1877a7741afSMartin Matuska left -= blocksize; 1887a7741afSMartin Matuska 1897a7741afSMartin Matuska if (left == 0) 190*87bf66d4SMartin Matuska args->entire_file_completed = 1; 191*87bf66d4SMartin Matuska } 192*87bf66d4SMartin Matuska 193*87bf66d4SMartin Matuska pthread_exit(NULL); 194*87bf66d4SMartin Matuska } 195*87bf66d4SMartin Matuska 196*87bf66d4SMartin Matuska /* 197*87bf66d4SMartin Matuska * Read blocksize * numblocks to the file using O_DIRECT. 198*87bf66d4SMartin Matuska */ 199*87bf66d4SMartin Matuska static void * 200*87bf66d4SMartin Matuska read_thread(void *arg) 201*87bf66d4SMartin Matuska { 202*87bf66d4SMartin Matuska size_t offset = 0; 203*87bf66d4SMartin Matuska int total_data = blocksize * numblocks; 204*87bf66d4SMartin Matuska int left = total_data; 205*87bf66d4SMartin Matuska ssize_t read = 0; 206*87bf66d4SMartin Matuska pthread_args_t *args = (pthread_args_t *)arg; 207*87bf66d4SMartin Matuska 208*87bf66d4SMartin Matuska while (!args->entire_file_completed) { 209*87bf66d4SMartin Matuska read = pread(fd, buf, blocksize, offset); 210*87bf66d4SMartin Matuska if (read != blocksize) { 211*87bf66d4SMartin Matuska exit(2); 212*87bf66d4SMartin Matuska } 213*87bf66d4SMartin Matuska 214*87bf66d4SMartin Matuska offset = ((offset + blocksize) % total_data); 215*87bf66d4SMartin Matuska left -= blocksize; 216*87bf66d4SMartin Matuska 217*87bf66d4SMartin Matuska if (left == 0) 218*87bf66d4SMartin Matuska args->entire_file_completed = 1; 2197a7741afSMartin Matuska } 2207a7741afSMartin Matuska 2217a7741afSMartin Matuska pthread_exit(NULL); 2227a7741afSMartin Matuska } 2237a7741afSMartin Matuska 2247a7741afSMartin Matuska /* 2257a7741afSMartin Matuska * Update the buffers contents with random data. 2267a7741afSMartin Matuska */ 2277a7741afSMartin Matuska static void * 2287a7741afSMartin Matuska manipulate_buf_thread(void *arg) 2297a7741afSMartin Matuska { 2307a7741afSMartin Matuska size_t rand_offset; 2317a7741afSMartin Matuska char rand_char; 2327a7741afSMartin Matuska pthread_args_t *args = (pthread_args_t *)arg; 2337a7741afSMartin Matuska 234*87bf66d4SMartin Matuska while (!args->entire_file_completed) { 2357a7741afSMartin Matuska rand_offset = (rand() % blocksize); 2367a7741afSMartin Matuska rand_char = (rand() % (126 - 33) + 33); 2377a7741afSMartin Matuska buf[rand_offset] = rand_char; 2387a7741afSMartin Matuska } 2397a7741afSMartin Matuska 2407a7741afSMartin Matuska pthread_exit(NULL); 2417a7741afSMartin Matuska } 2427a7741afSMartin Matuska 2437a7741afSMartin Matuska int 2447a7741afSMartin Matuska main(int argc, char *argv[]) 2457a7741afSMartin Matuska { 2467a7741afSMartin Matuska const char *datapattern = "0xdeadbeef"; 247*87bf66d4SMartin Matuska int fd_flags = O_DIRECT; 2487a7741afSMartin Matuska mode_t mode = S_IRUSR | S_IWUSR; 249*87bf66d4SMartin Matuska pthread_t io_thr; 2507a7741afSMartin Matuska pthread_t manipul_thr; 2517a7741afSMartin Matuska int left = blocksize; 2527a7741afSMartin Matuska int offset = 0; 2537a7741afSMartin Matuska int rc; 2547a7741afSMartin Matuska pthread_args_t args = { 0 }; 2557a7741afSMartin Matuska 2567a7741afSMartin Matuska parse_options(argc, argv); 2577a7741afSMartin Matuska 258*87bf66d4SMartin Matuska if (write_op) { 259*87bf66d4SMartin Matuska fd_flags |= (O_WRONLY | O_CREAT); 260*87bf66d4SMartin Matuska } else { 261*87bf66d4SMartin Matuska fd_flags |= O_RDONLY; 262*87bf66d4SMartin Matuska } 263*87bf66d4SMartin Matuska 264*87bf66d4SMartin Matuska fd = open(filename, fd_flags, mode); 265*87bf66d4SMartin Matuska if (fd == -1) { 266*87bf66d4SMartin Matuska (void) fprintf(stderr, "%s, %s\n", execname, filename); 2677a7741afSMartin Matuska perror("open"); 2687a7741afSMartin Matuska exit(2); 2697a7741afSMartin Matuska } 2707a7741afSMartin Matuska 2717a7741afSMartin Matuska int err = posix_memalign((void **)&buf, sysconf(_SC_PAGE_SIZE), 2727a7741afSMartin Matuska blocksize); 2737a7741afSMartin Matuska if (err != 0) { 2747a7741afSMartin Matuska (void) fprintf(stderr, 2757a7741afSMartin Matuska "%s: %s\n", execname, strerror(err)); 2767a7741afSMartin Matuska exit(2); 2777a7741afSMartin Matuska } 2787a7741afSMartin Matuska 279*87bf66d4SMartin Matuska if (write_op) { 2807a7741afSMartin Matuska if (!randompattern) { 2817a7741afSMartin Matuska /* Putting known data pattern in buffer */ 2827a7741afSMartin Matuska while (left) { 2837a7741afSMartin Matuska size_t amt = MIN(strlen(datapattern), left); 2847a7741afSMartin Matuska memcpy(&buf[offset], datapattern, amt); 2857a7741afSMartin Matuska offset += amt; 2867a7741afSMartin Matuska left -= amt; 2877a7741afSMartin Matuska } 2887a7741afSMartin Matuska } else { 2897a7741afSMartin Matuska /* Putting random data in buffer */ 2907a7741afSMartin Matuska for (int i = 0; i < blocksize; i++) 2917a7741afSMartin Matuska buf[i] = rand(); 2927a7741afSMartin Matuska } 293*87bf66d4SMartin Matuska } 2947a7741afSMartin Matuska 2957a7741afSMartin Matuska if ((rc = pthread_create(&manipul_thr, NULL, manipulate_buf_thread, 2967a7741afSMartin Matuska &args))) { 2977a7741afSMartin Matuska fprintf(stderr, "error: pthreads_create, manipul_thr, " 2987a7741afSMartin Matuska "rc: %d\n", rc); 2997a7741afSMartin Matuska exit(2); 3007a7741afSMartin Matuska } 3017a7741afSMartin Matuska 302*87bf66d4SMartin Matuska if (write_op) { 303*87bf66d4SMartin Matuska /* 304*87bf66d4SMartin Matuska * Writing using O_DIRECT while manipulating the buffer contents 305*87bf66d4SMartin Matuska * until the entire file is written. 306*87bf66d4SMartin Matuska */ 307*87bf66d4SMartin Matuska if ((rc = pthread_create(&io_thr, NULL, write_thread, &args))) { 308*87bf66d4SMartin Matuska fprintf(stderr, "error: pthreads_create, io_thr, " 3097a7741afSMartin Matuska "rc: %d\n", rc); 3107a7741afSMartin Matuska exit(2); 3117a7741afSMartin Matuska } 312*87bf66d4SMartin Matuska } else { 313*87bf66d4SMartin Matuska /* 314*87bf66d4SMartin Matuska * Reading using O_DIRECT while manipulating the buffer contents 315*87bf66d4SMartin Matuska * until the entire file is read. 316*87bf66d4SMartin Matuska */ 317*87bf66d4SMartin Matuska if ((rc = pthread_create(&io_thr, NULL, read_thread, &args))) { 318*87bf66d4SMartin Matuska fprintf(stderr, "error: pthreads_create, io_thr, " 319*87bf66d4SMartin Matuska "rc: %d\n", rc); 320*87bf66d4SMartin Matuska exit(2); 321*87bf66d4SMartin Matuska } 322*87bf66d4SMartin Matuska } 3237a7741afSMartin Matuska 324*87bf66d4SMartin Matuska pthread_join(io_thr, NULL); 3257a7741afSMartin Matuska pthread_join(manipul_thr, NULL); 3267a7741afSMartin Matuska 327*87bf66d4SMartin Matuska assert(args.entire_file_completed == 1); 3287a7741afSMartin Matuska 329*87bf66d4SMartin Matuska (void) close(fd); 3307a7741afSMartin Matuska 3317a7741afSMartin Matuska free(buf); 3327a7741afSMartin Matuska 3337a7741afSMartin Matuska return (0); 3347a7741afSMartin Matuska } 335