1*61145dc2SMartin Matuska // SPDX-License-Identifier: CDDL-1.0
27a7741afSMartin Matuska /*
37a7741afSMartin Matuska * CDDL HEADER START
47a7741afSMartin Matuska *
57a7741afSMartin Matuska * The contents of this file are subject to the terms of the
67a7741afSMartin Matuska * Common Development and Distribution License (the "License").
77a7741afSMartin Matuska * You may not use this file except in compliance with the License.
87a7741afSMartin Matuska *
97a7741afSMartin Matuska * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107a7741afSMartin Matuska * or https://opensource.org/licenses/CDDL-1.0.
117a7741afSMartin Matuska * See the License for the specific language governing permissions
127a7741afSMartin Matuska * and limitations under the License.
137a7741afSMartin Matuska *
147a7741afSMartin Matuska * When distributing Covered Code, include this CDDL HEADER in each
157a7741afSMartin Matuska * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167a7741afSMartin Matuska * If applicable, add the following below this CDDL HEADER, with the
177a7741afSMartin Matuska * fields enclosed by brackets "[]" replaced with your own identifying
187a7741afSMartin Matuska * information: Portions Copyright [yyyy] [name of copyright owner]
197a7741afSMartin Matuska *
207a7741afSMartin Matuska * CDDL HEADER END
217a7741afSMartin Matuska */
227a7741afSMartin Matuska
237a7741afSMartin Matuska /*
2487bf66d4SMartin Matuska * Copyright (c) 2024 by Triad National Security, LLC.
257a7741afSMartin Matuska */
267a7741afSMartin Matuska
277a7741afSMartin Matuska #include <sys/types.h>
287a7741afSMartin Matuska #include <sys/stat.h>
297a7741afSMartin Matuska #include <errno.h>
307a7741afSMartin Matuska #include <fcntl.h>
317a7741afSMartin Matuska #include <stdio.h>
327a7741afSMartin Matuska #include <unistd.h>
337a7741afSMartin Matuska #include <stdlib.h>
347a7741afSMartin Matuska #include <string.h>
357a7741afSMartin Matuska #include <time.h>
367a7741afSMartin Matuska #include <pthread.h>
377a7741afSMartin Matuska #include <assert.h>
387a7741afSMartin Matuska
397a7741afSMartin Matuska #ifndef MIN
407a7741afSMartin Matuska #define MIN(a, b) ((a) < (b)) ? (a) : (b)
417a7741afSMartin Matuska #endif
427a7741afSMartin Matuska
4387bf66d4SMartin Matuska static char *filename = NULL;
447a7741afSMartin Matuska static int blocksize = 131072; /* 128K */
4587bf66d4SMartin Matuska static int err_expected = 0;
4687bf66d4SMartin Matuska static int read_op = 0;
4787bf66d4SMartin Matuska static int write_op = 0;
487a7741afSMartin Matuska static int numblocks = 100;
497a7741afSMartin Matuska static char *execname = NULL;
507a7741afSMartin Matuska static int print_usage = 0;
517a7741afSMartin Matuska static int randompattern = 0;
5287bf66d4SMartin Matuska static int fd;
537a7741afSMartin Matuska char *buf = NULL;
547a7741afSMartin Matuska
557a7741afSMartin Matuska typedef struct {
5687bf66d4SMartin Matuska int entire_file_completed;
577a7741afSMartin Matuska } pthread_args_t;
587a7741afSMartin Matuska
597a7741afSMartin Matuska static void
usage(void)607a7741afSMartin Matuska usage(void)
617a7741afSMartin Matuska {
627a7741afSMartin Matuska (void) fprintf(stderr,
6387bf66d4SMartin Matuska "usage %s -f filename [-b blocksize] [-e wr_error_expected]\n"
6487bf66d4SMartin Matuska " [-n numblocks] [-p randompattern] -r read_op \n"
6587bf66d4SMartin Matuska " -w write_op [-h help]\n"
667a7741afSMartin Matuska "\n"
677a7741afSMartin Matuska "Testing whether checksum verify works correctly for O_DIRECT.\n"
687a7741afSMartin Matuska "when manipulating the contents of a userspace buffer.\n"
697a7741afSMartin Matuska "\n"
7087bf66d4SMartin Matuska " filename: File to read or write to.\n"
717a7741afSMartin Matuska " blocksize: Size of each block to write (must be at \n"
727a7741afSMartin Matuska " least >= 512).\n"
7387bf66d4SMartin Matuska " err_expected: Whether write() is expected to return EIO\n"
747a7741afSMartin Matuska " while manipulating the contents of the\n"
757a7741afSMartin Matuska " buffer.\n"
767a7741afSMartin Matuska " numblocks: Total number of blocksized blocks to\n"
777a7741afSMartin Matuska " write.\n"
7887bf66d4SMartin Matuska " read_op: Perform reads to the filename file while\n"
7987bf66d4SMartin Matuska " while manipulating the buffer contents\n"
8087bf66d4SMartin Matuska " write_op: Perform writes to the filename file while\n"
8187bf66d4SMartin Matuska " manipulating the buffer contents\n"
8287bf66d4SMartin Matuska " randompattern: Fill data buffer with random data for \n"
8387bf66d4SMartin Matuska " writes. Default behavior is to fill the \n"
8487bf66d4SMartin Matuska " buffer with known data pattern (0xdeadbeef)\n"
857a7741afSMartin Matuska " help: Print usage information and exit.\n"
867a7741afSMartin Matuska "\n"
877a7741afSMartin Matuska " Required parameters:\n"
8887bf66d4SMartin Matuska " filename\n"
8987bf66d4SMartin Matuska " read_op or write_op\n"
907a7741afSMartin Matuska "\n"
917a7741afSMartin Matuska " Default Values:\n"
927a7741afSMartin Matuska " blocksize -> 131072\n"
937a7741afSMartin Matuska " wr_err_expexted -> false\n"
947a7741afSMartin Matuska " numblocks -> 100\n"
9587bf66d4SMartin Matuska " randompattern -> false\n",
967a7741afSMartin Matuska execname);
977a7741afSMartin Matuska (void) exit(1);
987a7741afSMartin Matuska }
997a7741afSMartin Matuska
1007a7741afSMartin Matuska static void
parse_options(int argc,char * argv[])1017a7741afSMartin Matuska parse_options(int argc, char *argv[])
1027a7741afSMartin Matuska {
1037a7741afSMartin Matuska int c;
1047a7741afSMartin Matuska int errflag = 0;
1057a7741afSMartin Matuska extern char *optarg;
1067a7741afSMartin Matuska extern int optind, optopt;
1077a7741afSMartin Matuska execname = argv[0];
1087a7741afSMartin Matuska
10987bf66d4SMartin Matuska while ((c = getopt(argc, argv, "b:ef:hn:rw")) != -1) {
1107a7741afSMartin Matuska switch (c) {
1117a7741afSMartin Matuska case 'b':
1127a7741afSMartin Matuska blocksize = atoi(optarg);
1137a7741afSMartin Matuska break;
1147a7741afSMartin Matuska
1157a7741afSMartin Matuska case 'e':
11687bf66d4SMartin Matuska err_expected = 1;
1177a7741afSMartin Matuska break;
1187a7741afSMartin Matuska
11987bf66d4SMartin Matuska case 'f':
12087bf66d4SMartin Matuska filename = optarg;
12187bf66d4SMartin Matuska break;
12287bf66d4SMartin Matuska
12387bf66d4SMartin Matuska
1247a7741afSMartin Matuska case 'h':
1257a7741afSMartin Matuska print_usage = 1;
1267a7741afSMartin Matuska break;
1277a7741afSMartin Matuska
1287a7741afSMartin Matuska case 'n':
1297a7741afSMartin Matuska numblocks = atoi(optarg);
1307a7741afSMartin Matuska break;
1317a7741afSMartin Matuska
13287bf66d4SMartin Matuska case 'r':
13387bf66d4SMartin Matuska read_op = 1;
1347a7741afSMartin Matuska break;
1357a7741afSMartin Matuska
13687bf66d4SMartin Matuska case 'w':
13787bf66d4SMartin Matuska write_op = 1;
1387a7741afSMartin Matuska break;
1397a7741afSMartin Matuska
1407a7741afSMartin Matuska case ':':
1417a7741afSMartin Matuska (void) fprintf(stderr,
1427a7741afSMartin Matuska "Option -%c requires an opertand\n",
1437a7741afSMartin Matuska optopt);
1447a7741afSMartin Matuska errflag++;
1457a7741afSMartin Matuska break;
1467a7741afSMartin Matuska case '?':
1477a7741afSMartin Matuska default:
1487a7741afSMartin Matuska (void) fprintf(stderr,
1497a7741afSMartin Matuska "Unrecognized option: -%c\n", optopt);
1507a7741afSMartin Matuska errflag++;
1517a7741afSMartin Matuska break;
1527a7741afSMartin Matuska }
1537a7741afSMartin Matuska }
1547a7741afSMartin Matuska
1557a7741afSMartin Matuska if (errflag || print_usage == 1)
1567a7741afSMartin Matuska (void) usage();
1577a7741afSMartin Matuska
15887bf66d4SMartin Matuska if (blocksize < 512 || filename == NULL || numblocks <= 0 ||
15987bf66d4SMartin Matuska (read_op == 0 && write_op == 0)) {
1607a7741afSMartin Matuska (void) fprintf(stderr,
1617a7741afSMartin Matuska "Required paramater(s) missing or invalid.\n");
1627a7741afSMartin Matuska (void) usage();
1637a7741afSMartin Matuska }
1647a7741afSMartin Matuska }
1657a7741afSMartin Matuska
1667a7741afSMartin Matuska /*
1677a7741afSMartin Matuska * Write blocksize * numblocks to the file using O_DIRECT.
1687a7741afSMartin Matuska */
1697a7741afSMartin Matuska static void *
write_thread(void * arg)1707a7741afSMartin Matuska write_thread(void *arg)
1717a7741afSMartin Matuska {
1727a7741afSMartin Matuska size_t offset = 0;
1737a7741afSMartin Matuska int total_data = blocksize * numblocks;
1747a7741afSMartin Matuska int left = total_data;
1757a7741afSMartin Matuska ssize_t wrote = 0;
1767a7741afSMartin Matuska pthread_args_t *args = (pthread_args_t *)arg;
1777a7741afSMartin Matuska
17887bf66d4SMartin Matuska while (!args->entire_file_completed) {
17987bf66d4SMartin Matuska wrote = pwrite(fd, buf, blocksize, offset);
1807a7741afSMartin Matuska if (wrote != blocksize) {
18187bf66d4SMartin Matuska if (err_expected)
1827a7741afSMartin Matuska assert(errno == EIO);
1837a7741afSMartin Matuska else
1847a7741afSMartin Matuska exit(2);
1857a7741afSMartin Matuska }
1867a7741afSMartin Matuska
1877a7741afSMartin Matuska offset = ((offset + blocksize) % total_data);
1887a7741afSMartin Matuska left -= blocksize;
1897a7741afSMartin Matuska
1907a7741afSMartin Matuska if (left == 0)
19187bf66d4SMartin Matuska args->entire_file_completed = 1;
19287bf66d4SMartin Matuska }
19387bf66d4SMartin Matuska
19487bf66d4SMartin Matuska pthread_exit(NULL);
19587bf66d4SMartin Matuska }
19687bf66d4SMartin Matuska
19787bf66d4SMartin Matuska /*
19887bf66d4SMartin Matuska * Read blocksize * numblocks to the file using O_DIRECT.
19987bf66d4SMartin Matuska */
20087bf66d4SMartin Matuska static void *
read_thread(void * arg)20187bf66d4SMartin Matuska read_thread(void *arg)
20287bf66d4SMartin Matuska {
20387bf66d4SMartin Matuska size_t offset = 0;
20487bf66d4SMartin Matuska int total_data = blocksize * numblocks;
20587bf66d4SMartin Matuska int left = total_data;
20687bf66d4SMartin Matuska ssize_t read = 0;
20787bf66d4SMartin Matuska pthread_args_t *args = (pthread_args_t *)arg;
20887bf66d4SMartin Matuska
20987bf66d4SMartin Matuska while (!args->entire_file_completed) {
21087bf66d4SMartin Matuska read = pread(fd, buf, blocksize, offset);
21187bf66d4SMartin Matuska if (read != blocksize) {
21287bf66d4SMartin Matuska exit(2);
21387bf66d4SMartin Matuska }
21487bf66d4SMartin Matuska
21587bf66d4SMartin Matuska offset = ((offset + blocksize) % total_data);
21687bf66d4SMartin Matuska left -= blocksize;
21787bf66d4SMartin Matuska
21887bf66d4SMartin Matuska if (left == 0)
21987bf66d4SMartin Matuska args->entire_file_completed = 1;
2207a7741afSMartin Matuska }
2217a7741afSMartin Matuska
2227a7741afSMartin Matuska pthread_exit(NULL);
2237a7741afSMartin Matuska }
2247a7741afSMartin Matuska
2257a7741afSMartin Matuska /*
2267a7741afSMartin Matuska * Update the buffers contents with random data.
2277a7741afSMartin Matuska */
2287a7741afSMartin Matuska static void *
manipulate_buf_thread(void * arg)2297a7741afSMartin Matuska manipulate_buf_thread(void *arg)
2307a7741afSMartin Matuska {
2317a7741afSMartin Matuska size_t rand_offset;
2327a7741afSMartin Matuska char rand_char;
2337a7741afSMartin Matuska pthread_args_t *args = (pthread_args_t *)arg;
2347a7741afSMartin Matuska
23587bf66d4SMartin Matuska while (!args->entire_file_completed) {
2367a7741afSMartin Matuska rand_offset = (rand() % blocksize);
2377a7741afSMartin Matuska rand_char = (rand() % (126 - 33) + 33);
2387a7741afSMartin Matuska buf[rand_offset] = rand_char;
2397a7741afSMartin Matuska }
2407a7741afSMartin Matuska
2417a7741afSMartin Matuska pthread_exit(NULL);
2427a7741afSMartin Matuska }
2437a7741afSMartin Matuska
2447a7741afSMartin Matuska int
main(int argc,char * argv[])2457a7741afSMartin Matuska main(int argc, char *argv[])
2467a7741afSMartin Matuska {
2477a7741afSMartin Matuska const char *datapattern = "0xdeadbeef";
24887bf66d4SMartin Matuska int fd_flags = O_DIRECT;
2497a7741afSMartin Matuska mode_t mode = S_IRUSR | S_IWUSR;
25087bf66d4SMartin Matuska pthread_t io_thr;
2517a7741afSMartin Matuska pthread_t manipul_thr;
2527a7741afSMartin Matuska int left = blocksize;
2537a7741afSMartin Matuska int offset = 0;
2547a7741afSMartin Matuska int rc;
2557a7741afSMartin Matuska pthread_args_t args = { 0 };
2567a7741afSMartin Matuska
2577a7741afSMartin Matuska parse_options(argc, argv);
2587a7741afSMartin Matuska
25987bf66d4SMartin Matuska if (write_op) {
26087bf66d4SMartin Matuska fd_flags |= (O_WRONLY | O_CREAT);
26187bf66d4SMartin Matuska } else {
26287bf66d4SMartin Matuska fd_flags |= O_RDONLY;
26387bf66d4SMartin Matuska }
26487bf66d4SMartin Matuska
26587bf66d4SMartin Matuska fd = open(filename, fd_flags, mode);
26687bf66d4SMartin Matuska if (fd == -1) {
26787bf66d4SMartin Matuska (void) fprintf(stderr, "%s, %s\n", execname, filename);
2687a7741afSMartin Matuska perror("open");
2697a7741afSMartin Matuska exit(2);
2707a7741afSMartin Matuska }
2717a7741afSMartin Matuska
2727a7741afSMartin Matuska int err = posix_memalign((void **)&buf, sysconf(_SC_PAGE_SIZE),
2737a7741afSMartin Matuska blocksize);
2747a7741afSMartin Matuska if (err != 0) {
2757a7741afSMartin Matuska (void) fprintf(stderr,
2767a7741afSMartin Matuska "%s: %s\n", execname, strerror(err));
2777a7741afSMartin Matuska exit(2);
2787a7741afSMartin Matuska }
2797a7741afSMartin Matuska
28087bf66d4SMartin Matuska if (write_op) {
2817a7741afSMartin Matuska if (!randompattern) {
2827a7741afSMartin Matuska /* Putting known data pattern in buffer */
2837a7741afSMartin Matuska while (left) {
2847a7741afSMartin Matuska size_t amt = MIN(strlen(datapattern), left);
2857a7741afSMartin Matuska memcpy(&buf[offset], datapattern, amt);
2867a7741afSMartin Matuska offset += amt;
2877a7741afSMartin Matuska left -= amt;
2887a7741afSMartin Matuska }
2897a7741afSMartin Matuska } else {
2907a7741afSMartin Matuska /* Putting random data in buffer */
2917a7741afSMartin Matuska for (int i = 0; i < blocksize; i++)
2927a7741afSMartin Matuska buf[i] = rand();
2937a7741afSMartin Matuska }
29487bf66d4SMartin Matuska }
2957a7741afSMartin Matuska
2967a7741afSMartin Matuska if ((rc = pthread_create(&manipul_thr, NULL, manipulate_buf_thread,
2977a7741afSMartin Matuska &args))) {
2987a7741afSMartin Matuska fprintf(stderr, "error: pthreads_create, manipul_thr, "
2997a7741afSMartin Matuska "rc: %d\n", rc);
3007a7741afSMartin Matuska exit(2);
3017a7741afSMartin Matuska }
3027a7741afSMartin Matuska
30387bf66d4SMartin Matuska if (write_op) {
30487bf66d4SMartin Matuska /*
30587bf66d4SMartin Matuska * Writing using O_DIRECT while manipulating the buffer contents
30687bf66d4SMartin Matuska * until the entire file is written.
30787bf66d4SMartin Matuska */
30887bf66d4SMartin Matuska if ((rc = pthread_create(&io_thr, NULL, write_thread, &args))) {
30987bf66d4SMartin Matuska fprintf(stderr, "error: pthreads_create, io_thr, "
3107a7741afSMartin Matuska "rc: %d\n", rc);
3117a7741afSMartin Matuska exit(2);
3127a7741afSMartin Matuska }
31387bf66d4SMartin Matuska } else {
31487bf66d4SMartin Matuska /*
31587bf66d4SMartin Matuska * Reading using O_DIRECT while manipulating the buffer contents
31687bf66d4SMartin Matuska * until the entire file is read.
31787bf66d4SMartin Matuska */
31887bf66d4SMartin Matuska if ((rc = pthread_create(&io_thr, NULL, read_thread, &args))) {
31987bf66d4SMartin Matuska fprintf(stderr, "error: pthreads_create, io_thr, "
32087bf66d4SMartin Matuska "rc: %d\n", rc);
32187bf66d4SMartin Matuska exit(2);
32287bf66d4SMartin Matuska }
32387bf66d4SMartin Matuska }
3247a7741afSMartin Matuska
32587bf66d4SMartin Matuska pthread_join(io_thr, NULL);
3267a7741afSMartin Matuska pthread_join(manipul_thr, NULL);
3277a7741afSMartin Matuska
32887bf66d4SMartin Matuska assert(args.entire_file_completed == 1);
3297a7741afSMartin Matuska
33087bf66d4SMartin Matuska (void) close(fd);
3317a7741afSMartin Matuska
3327a7741afSMartin Matuska free(buf);
3337a7741afSMartin Matuska
3347a7741afSMartin Matuska return (0);
3357a7741afSMartin Matuska }
336