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) 2024 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 *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; 53 54 typedef struct { 55 int entire_file_completed; 56 } pthread_args_t; 57 58 static void 59 usage(void) 60 { 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); 97 } 98 99 static void 100 parse_options(int argc, char *argv[]) 101 { 102 int c; 103 int errflag = 0; 104 extern char *optarg; 105 extern int optind, optopt; 106 execname = argv[0]; 107 108 while ((c = getopt(argc, argv, "b:ef:hn:rw")) != -1) { 109 switch (c) { 110 case 'b': 111 blocksize = atoi(optarg); 112 break; 113 114 case 'e': 115 err_expected = 1; 116 break; 117 118 case 'f': 119 filename = optarg; 120 break; 121 122 123 case 'h': 124 print_usage = 1; 125 break; 126 127 case 'n': 128 numblocks = atoi(optarg); 129 break; 130 131 case 'r': 132 read_op = 1; 133 break; 134 135 case 'w': 136 write_op = 1; 137 break; 138 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; 151 } 152 } 153 154 if (errflag || print_usage == 1) 155 (void) usage(); 156 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(); 162 } 163 } 164 165 /* 166 * Write blocksize * numblocks to the file using O_DIRECT. 167 */ 168 static void * 169 write_thread(void *arg) 170 { 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; 176 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); 184 } 185 186 offset = ((offset + blocksize) % total_data); 187 left -= blocksize; 188 189 if (left == 0) 190 args->entire_file_completed = 1; 191 } 192 193 pthread_exit(NULL); 194 } 195 196 /* 197 * Read blocksize * numblocks to the file using O_DIRECT. 198 */ 199 static void * 200 read_thread(void *arg) 201 { 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; 207 208 while (!args->entire_file_completed) { 209 read = pread(fd, buf, blocksize, offset); 210 if (read != blocksize) { 211 exit(2); 212 } 213 214 offset = ((offset + blocksize) % total_data); 215 left -= blocksize; 216 217 if (left == 0) 218 args->entire_file_completed = 1; 219 } 220 221 pthread_exit(NULL); 222 } 223 224 /* 225 * Update the buffers contents with random data. 226 */ 227 static void * 228 manipulate_buf_thread(void *arg) 229 { 230 size_t rand_offset; 231 char rand_char; 232 pthread_args_t *args = (pthread_args_t *)arg; 233 234 while (!args->entire_file_completed) { 235 rand_offset = (rand() % blocksize); 236 rand_char = (rand() % (126 - 33) + 33); 237 buf[rand_offset] = rand_char; 238 } 239 240 pthread_exit(NULL); 241 } 242 243 int 244 main(int argc, char *argv[]) 245 { 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 }; 255 256 parse_options(argc, argv); 257 258 if (write_op) { 259 fd_flags |= (O_WRONLY | O_CREAT); 260 } else { 261 fd_flags |= O_RDONLY; 262 } 263 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); 269 } 270 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); 277 } 278 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; 287 } 288 } else { 289 /* Putting random data in buffer */ 290 for (int i = 0; i < blocksize; i++) 291 buf[i] = rand(); 292 } 293 } 294 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); 300 } 301 302 if (write_op) { 303 /* 304 * Writing using O_DIRECT while manipulating the buffer contents 305 * until the entire file is written. 306 */ 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); 311 } 312 } else { 313 /* 314 * Reading using O_DIRECT while manipulating the buffer contents 315 * until the entire file is read. 316 */ 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); 321 } 322 } 323 324 pthread_join(io_thr, NULL); 325 pthread_join(manipul_thr, NULL); 326 327 assert(args.entire_file_completed == 1); 328 329 (void) close(fd); 330 331 free(buf); 332 333 return (0); 334 } 335