Path: blob/main/sys/contrib/openzfs/tests/zfs-tests/cmd/manipulate_user_buffer.c
48529 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* 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.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/2122/*23* Copyright (c) 2024 by Triad National Security, LLC.24*/2526#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>3738#ifndef MIN39#define MIN(a, b) ((a) < (b)) ? (a) : (b)40#endif4142static char *filename = NULL;43static int blocksize = 131072; /* 128K */44static int err_expected = 0;45static int read_op = 0;46static int write_op = 0;47static int numblocks = 100;48static char *execname = NULL;49static int print_usage = 0;50static int randompattern = 0;51static int fd;52char *buf = NULL;5354typedef struct {55int entire_file_completed;56} pthread_args_t;5758static void59usage(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",95execname);96(void) exit(1);97}9899static void100parse_options(int argc, char *argv[])101{102int c;103int errflag = 0;104extern char *optarg;105extern int optind, optopt;106execname = argv[0];107108while ((c = getopt(argc, argv, "b:ef:hn:rw")) != -1) {109switch (c) {110case 'b':111blocksize = atoi(optarg);112break;113114case 'e':115err_expected = 1;116break;117118case 'f':119filename = optarg;120break;121122123case 'h':124print_usage = 1;125break;126127case 'n':128numblocks = atoi(optarg);129break;130131case 'r':132read_op = 1;133break;134135case 'w':136write_op = 1;137break;138139case ':':140(void) fprintf(stderr,141"Option -%c requires an opertand\n",142optopt);143errflag++;144break;145case '?':146default:147(void) fprintf(stderr,148"Unrecognized option: -%c\n", optopt);149errflag++;150break;151}152}153154if (errflag || print_usage == 1)155(void) usage();156157if (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}164165/*166* Write blocksize * numblocks to the file using O_DIRECT.167*/168static void *169write_thread(void *arg)170{171size_t offset = 0;172int total_data = blocksize * numblocks;173int left = total_data;174ssize_t wrote = 0;175pthread_args_t *args = (pthread_args_t *)arg;176177while (!args->entire_file_completed) {178wrote = pwrite(fd, buf, blocksize, offset);179if (wrote != blocksize) {180if (err_expected)181assert(errno == EIO);182else183exit(2);184}185186offset = ((offset + blocksize) % total_data);187left -= blocksize;188189if (left == 0)190args->entire_file_completed = 1;191}192193pthread_exit(NULL);194}195196/*197* Read blocksize * numblocks to the file using O_DIRECT.198*/199static void *200read_thread(void *arg)201{202size_t offset = 0;203int total_data = blocksize * numblocks;204int left = total_data;205ssize_t read = 0;206pthread_args_t *args = (pthread_args_t *)arg;207208while (!args->entire_file_completed) {209read = pread(fd, buf, blocksize, offset);210if (read != blocksize) {211exit(2);212}213214offset = ((offset + blocksize) % total_data);215left -= blocksize;216217if (left == 0)218args->entire_file_completed = 1;219}220221pthread_exit(NULL);222}223224/*225* Update the buffers contents with random data.226*/227static void *228manipulate_buf_thread(void *arg)229{230size_t rand_offset;231char rand_char;232pthread_args_t *args = (pthread_args_t *)arg;233234while (!args->entire_file_completed) {235rand_offset = (rand() % blocksize);236rand_char = (rand() % (126 - 33) + 33);237buf[rand_offset] = rand_char;238}239240pthread_exit(NULL);241}242243int244main(int argc, char *argv[])245{246const char *datapattern = "0xdeadbeef";247int fd_flags = O_DIRECT;248mode_t mode = S_IRUSR | S_IWUSR;249pthread_t io_thr;250pthread_t manipul_thr;251int left = blocksize;252int offset = 0;253int rc;254pthread_args_t args = { 0 };255256parse_options(argc, argv);257258if (write_op) {259fd_flags |= (O_WRONLY | O_CREAT);260} else {261fd_flags |= O_RDONLY;262}263264fd = open(filename, fd_flags, mode);265if (fd == -1) {266(void) fprintf(stderr, "%s, %s\n", execname, filename);267perror("open");268exit(2);269}270271int err = posix_memalign((void **)&buf, sysconf(_SC_PAGE_SIZE),272blocksize);273if (err != 0) {274(void) fprintf(stderr,275"%s: %s\n", execname, strerror(err));276exit(2);277}278279if (write_op) {280if (!randompattern) {281/* Putting known data pattern in buffer */282while (left) {283size_t amt = MIN(strlen(datapattern), left);284memcpy(&buf[offset], datapattern, amt);285offset += amt;286left -= amt;287}288} else {289/* Putting random data in buffer */290for (int i = 0; i < blocksize; i++)291buf[i] = rand();292}293}294295if ((rc = pthread_create(&manipul_thr, NULL, manipulate_buf_thread,296&args))) {297fprintf(stderr, "error: pthreads_create, manipul_thr, "298"rc: %d\n", rc);299exit(2);300}301302if (write_op) {303/*304* Writing using O_DIRECT while manipulating the buffer contents305* until the entire file is written.306*/307if ((rc = pthread_create(&io_thr, NULL, write_thread, &args))) {308fprintf(stderr, "error: pthreads_create, io_thr, "309"rc: %d\n", rc);310exit(2);311}312} else {313/*314* Reading using O_DIRECT while manipulating the buffer contents315* until the entire file is read.316*/317if ((rc = pthread_create(&io_thr, NULL, read_thread, &args))) {318fprintf(stderr, "error: pthreads_create, io_thr, "319"rc: %d\n", rc);320exit(2);321}322}323324pthread_join(io_thr, NULL);325pthread_join(manipul_thr, NULL);326327assert(args.entire_file_completed == 1);328329(void) close(fd);330331free(buf);332333return (0);334}335336337