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) 2022 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 *outputfile = NULL; 43 static int blocksize = 131072; /* 128K */ 44 static int wr_err_expected = 0; 45 static int numblocks = 100; 46 static char *execname = NULL; 47 static int print_usage = 0; 48 static int randompattern = 0; 49 static int ofd; 50 char *buf = NULL; 51 52 typedef struct { 53 int entire_file_written; 54 } pthread_args_t; 55 56 static void 57 usage(void) 58 { 59 (void) fprintf(stderr, 60 "usage %s -o outputfile [-b blocksize] [-e wr_error_expected]\n" 61 " [-n numblocks] [-p randpattern] [-h help]\n" 62 "\n" 63 "Testing whether checksum verify works correctly for O_DIRECT.\n" 64 "when manipulating the contents of a userspace buffer.\n" 65 "\n" 66 " outputfile: File to write to.\n" 67 " blocksize: Size of each block to write (must be at \n" 68 " least >= 512).\n" 69 " wr_err_expected: Whether pwrite() is expected to return EIO\n" 70 " while manipulating the contents of the\n" 71 " buffer.\n" 72 " numblocks: Total number of blocksized blocks to\n" 73 " write.\n" 74 " randpattern: Fill data buffer with random data. Default\n" 75 " behavior is to fill the buffer with the \n" 76 " known data pattern (0xdeadbeef).\n" 77 " help: Print usage information and exit.\n" 78 "\n" 79 " Required parameters:\n" 80 " outputfile\n" 81 "\n" 82 " Default Values:\n" 83 " blocksize -> 131072\n" 84 " wr_err_expexted -> false\n" 85 " numblocks -> 100\n" 86 " randpattern -> false\n", 87 execname); 88 (void) exit(1); 89 } 90 91 static void 92 parse_options(int argc, char *argv[]) 93 { 94 int c; 95 int errflag = 0; 96 extern char *optarg; 97 extern int optind, optopt; 98 execname = argv[0]; 99 100 while ((c = getopt(argc, argv, "b:ehn:o:p")) != -1) { 101 switch (c) { 102 case 'b': 103 blocksize = atoi(optarg); 104 break; 105 106 case 'e': 107 wr_err_expected = 1; 108 break; 109 110 case 'h': 111 print_usage = 1; 112 break; 113 114 case 'n': 115 numblocks = atoi(optarg); 116 break; 117 118 case 'o': 119 outputfile = optarg; 120 break; 121 122 case 'p': 123 randompattern = 1; 124 break; 125 126 case ':': 127 (void) fprintf(stderr, 128 "Option -%c requires an opertand\n", 129 optopt); 130 errflag++; 131 break; 132 case '?': 133 default: 134 (void) fprintf(stderr, 135 "Unrecognized option: -%c\n", optopt); 136 errflag++; 137 break; 138 } 139 } 140 141 if (errflag || print_usage == 1) 142 (void) usage(); 143 144 if (blocksize < 512 || outputfile == NULL || numblocks <= 0) { 145 (void) fprintf(stderr, 146 "Required paramater(s) missing or invalid.\n"); 147 (void) usage(); 148 } 149 } 150 151 /* 152 * Write blocksize * numblocks to the file using O_DIRECT. 153 */ 154 static void * 155 write_thread(void *arg) 156 { 157 size_t offset = 0; 158 int total_data = blocksize * numblocks; 159 int left = total_data; 160 ssize_t wrote = 0; 161 pthread_args_t *args = (pthread_args_t *)arg; 162 163 while (!args->entire_file_written) { 164 wrote = pwrite(ofd, buf, blocksize, offset); 165 if (wrote != blocksize) { 166 if (wr_err_expected) 167 assert(errno == EIO); 168 else 169 exit(2); 170 } 171 172 offset = ((offset + blocksize) % total_data); 173 left -= blocksize; 174 175 if (left == 0) 176 args->entire_file_written = 1; 177 } 178 179 pthread_exit(NULL); 180 } 181 182 /* 183 * Update the buffers contents with random data. 184 */ 185 static void * 186 manipulate_buf_thread(void *arg) 187 { 188 size_t rand_offset; 189 char rand_char; 190 pthread_args_t *args = (pthread_args_t *)arg; 191 192 while (!args->entire_file_written) { 193 rand_offset = (rand() % blocksize); 194 rand_char = (rand() % (126 - 33) + 33); 195 buf[rand_offset] = rand_char; 196 } 197 198 pthread_exit(NULL); 199 } 200 201 int 202 main(int argc, char *argv[]) 203 { 204 const char *datapattern = "0xdeadbeef"; 205 int ofd_flags = O_WRONLY | O_CREAT | O_DIRECT; 206 mode_t mode = S_IRUSR | S_IWUSR; 207 pthread_t write_thr; 208 pthread_t manipul_thr; 209 int left = blocksize; 210 int offset = 0; 211 int rc; 212 pthread_args_t args = { 0 }; 213 214 parse_options(argc, argv); 215 216 ofd = open(outputfile, ofd_flags, mode); 217 if (ofd == -1) { 218 (void) fprintf(stderr, "%s, %s\n", execname, outputfile); 219 perror("open"); 220 exit(2); 221 } 222 223 int err = posix_memalign((void **)&buf, sysconf(_SC_PAGE_SIZE), 224 blocksize); 225 if (err != 0) { 226 (void) fprintf(stderr, 227 "%s: %s\n", execname, strerror(err)); 228 exit(2); 229 } 230 231 if (!randompattern) { 232 /* Putting known data pattern in buffer */ 233 while (left) { 234 size_t amt = MIN(strlen(datapattern), left); 235 memcpy(&buf[offset], datapattern, amt); 236 offset += amt; 237 left -= amt; 238 } 239 } else { 240 /* Putting random data in buffer */ 241 for (int i = 0; i < blocksize; i++) 242 buf[i] = rand(); 243 } 244 245 /* 246 * Writing using O_DIRECT while manipulating the buffer contents until 247 * the entire file is written. 248 */ 249 if ((rc = pthread_create(&manipul_thr, NULL, manipulate_buf_thread, 250 &args))) { 251 fprintf(stderr, "error: pthreads_create, manipul_thr, " 252 "rc: %d\n", rc); 253 exit(2); 254 } 255 256 if ((rc = pthread_create(&write_thr, NULL, write_thread, &args))) { 257 fprintf(stderr, "error: pthreads_create, write_thr, " 258 "rc: %d\n", rc); 259 exit(2); 260 } 261 262 pthread_join(write_thr, NULL); 263 pthread_join(manipul_thr, NULL); 264 265 assert(args.entire_file_written == 1); 266 267 (void) close(ofd); 268 269 free(buf); 270 271 return (0); 272 } 273