diff options
Diffstat (limited to 'sys/contrib/openzfs/tests/zfs-tests/cmd/manipulate_user_buffer.c')
-rw-r--r-- | sys/contrib/openzfs/tests/zfs-tests/cmd/manipulate_user_buffer.c | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/manipulate_user_buffer.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/manipulate_user_buffer.c new file mode 100644 index 000000000000..712acf6829e3 --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/manipulate_user_buffer.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: CDDL-1.0 +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or https://opensource.org/licenses/CDDL-1.0. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2024 by Triad National Security, LLC. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <pthread.h> +#include <assert.h> + +#ifndef MIN +#define MIN(a, b) ((a) < (b)) ? (a) : (b) +#endif + +static char *filename = NULL; +static int blocksize = 131072; /* 128K */ +static int err_expected = 0; +static int read_op = 0; +static int write_op = 0; +static int numblocks = 100; +static char *execname = NULL; +static int print_usage = 0; +static int randompattern = 0; +static int fd; +char *buf = NULL; + +typedef struct { + int entire_file_completed; +} pthread_args_t; + +static void +usage(void) +{ + (void) fprintf(stderr, + "usage %s -f filename [-b blocksize] [-e wr_error_expected]\n" + " [-n numblocks] [-p randompattern] -r read_op \n" + " -w write_op [-h help]\n" + "\n" + "Testing whether checksum verify works correctly for O_DIRECT.\n" + "when manipulating the contents of a userspace buffer.\n" + "\n" + " filename: File to read or write to.\n" + " blocksize: Size of each block to write (must be at \n" + " least >= 512).\n" + " err_expected: Whether write() is expected to return EIO\n" + " while manipulating the contents of the\n" + " buffer.\n" + " numblocks: Total number of blocksized blocks to\n" + " write.\n" + " read_op: Perform reads to the filename file while\n" + " while manipulating the buffer contents\n" + " write_op: Perform writes to the filename file while\n" + " manipulating the buffer contents\n" + " randompattern: Fill data buffer with random data for \n" + " writes. Default behavior is to fill the \n" + " buffer with known data pattern (0xdeadbeef)\n" + " help: Print usage information and exit.\n" + "\n" + " Required parameters:\n" + " filename\n" + " read_op or write_op\n" + "\n" + " Default Values:\n" + " blocksize -> 131072\n" + " wr_err_expexted -> false\n" + " numblocks -> 100\n" + " randompattern -> false\n", + execname); + (void) exit(1); +} + +static void +parse_options(int argc, char *argv[]) +{ + int c; + int errflag = 0; + extern char *optarg; + extern int optind, optopt; + execname = argv[0]; + + while ((c = getopt(argc, argv, "b:ef:hn:rw")) != -1) { + switch (c) { + case 'b': + blocksize = atoi(optarg); + break; + + case 'e': + err_expected = 1; + break; + + case 'f': + filename = optarg; + break; + + + case 'h': + print_usage = 1; + break; + + case 'n': + numblocks = atoi(optarg); + break; + + case 'r': + read_op = 1; + break; + + case 'w': + write_op = 1; + break; + + case ':': + (void) fprintf(stderr, + "Option -%c requires an opertand\n", + optopt); + errflag++; + break; + case '?': + default: + (void) fprintf(stderr, + "Unrecognized option: -%c\n", optopt); + errflag++; + break; + } + } + + if (errflag || print_usage == 1) + (void) usage(); + + if (blocksize < 512 || filename == NULL || numblocks <= 0 || + (read_op == 0 && write_op == 0)) { + (void) fprintf(stderr, + "Required paramater(s) missing or invalid.\n"); + (void) usage(); + } +} + +/* + * Write blocksize * numblocks to the file using O_DIRECT. + */ +static void * +write_thread(void *arg) +{ + size_t offset = 0; + int total_data = blocksize * numblocks; + int left = total_data; + ssize_t wrote = 0; + pthread_args_t *args = (pthread_args_t *)arg; + + while (!args->entire_file_completed) { + wrote = pwrite(fd, buf, blocksize, offset); + if (wrote != blocksize) { + if (err_expected) + assert(errno == EIO); + else + exit(2); + } + + offset = ((offset + blocksize) % total_data); + left -= blocksize; + + if (left == 0) + args->entire_file_completed = 1; + } + + pthread_exit(NULL); +} + +/* + * Read blocksize * numblocks to the file using O_DIRECT. + */ +static void * +read_thread(void *arg) +{ + size_t offset = 0; + int total_data = blocksize * numblocks; + int left = total_data; + ssize_t read = 0; + pthread_args_t *args = (pthread_args_t *)arg; + + while (!args->entire_file_completed) { + read = pread(fd, buf, blocksize, offset); + if (read != blocksize) { + exit(2); + } + + offset = ((offset + blocksize) % total_data); + left -= blocksize; + + if (left == 0) + args->entire_file_completed = 1; + } + + pthread_exit(NULL); +} + +/* + * Update the buffers contents with random data. + */ +static void * +manipulate_buf_thread(void *arg) +{ + size_t rand_offset; + char rand_char; + pthread_args_t *args = (pthread_args_t *)arg; + + while (!args->entire_file_completed) { + rand_offset = (rand() % blocksize); + rand_char = (rand() % (126 - 33) + 33); + buf[rand_offset] = rand_char; + } + + pthread_exit(NULL); +} + +int +main(int argc, char *argv[]) +{ + const char *datapattern = "0xdeadbeef"; + int fd_flags = O_DIRECT; + mode_t mode = S_IRUSR | S_IWUSR; + pthread_t io_thr; + pthread_t manipul_thr; + int left = blocksize; + int offset = 0; + int rc; + pthread_args_t args = { 0 }; + + parse_options(argc, argv); + + if (write_op) { + fd_flags |= (O_WRONLY | O_CREAT); + } else { + fd_flags |= O_RDONLY; + } + + fd = open(filename, fd_flags, mode); + if (fd == -1) { + (void) fprintf(stderr, "%s, %s\n", execname, filename); + perror("open"); + exit(2); + } + + int err = posix_memalign((void **)&buf, sysconf(_SC_PAGE_SIZE), + blocksize); + if (err != 0) { + (void) fprintf(stderr, + "%s: %s\n", execname, strerror(err)); + exit(2); + } + + if (write_op) { + if (!randompattern) { + /* Putting known data pattern in buffer */ + while (left) { + size_t amt = MIN(strlen(datapattern), left); + memcpy(&buf[offset], datapattern, amt); + offset += amt; + left -= amt; + } + } else { + /* Putting random data in buffer */ + for (int i = 0; i < blocksize; i++) + buf[i] = rand(); + } + } + + if ((rc = pthread_create(&manipul_thr, NULL, manipulate_buf_thread, + &args))) { + fprintf(stderr, "error: pthreads_create, manipul_thr, " + "rc: %d\n", rc); + exit(2); + } + + if (write_op) { + /* + * Writing using O_DIRECT while manipulating the buffer contents + * until the entire file is written. + */ + if ((rc = pthread_create(&io_thr, NULL, write_thread, &args))) { + fprintf(stderr, "error: pthreads_create, io_thr, " + "rc: %d\n", rc); + exit(2); + } + } else { + /* + * Reading using O_DIRECT while manipulating the buffer contents + * until the entire file is read. + */ + if ((rc = pthread_create(&io_thr, NULL, read_thread, &args))) { + fprintf(stderr, "error: pthreads_create, io_thr, " + "rc: %d\n", rc); + exit(2); + } + } + + pthread_join(io_thr, NULL); + pthread_join(manipul_thr, NULL); + + assert(args.entire_file_completed == 1); + + (void) close(fd); + + free(buf); + + return (0); +} |