// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 IBM. * All Rights Reserved. * * simple mmap write multithreaded test for testing enospc */ #include #include #include #include #include #include #include #include #include #include #define pr_debug(fmt, ...) do { \ if (verbose) { \ printf (fmt, ##__VA_ARGS__); \ } \ } while (0) struct thread_s { int id; }; static float file_ratio[2] = {0.5, 0.5}; static unsigned long fsize = 0; static char *base_dir = "."; static char *ratio = "1"; static bool use_fallocate = false; static bool verbose = false; pthread_barrier_t bar; void handle_sigbus(int sig) { pr_debug("Enospc test failed with SIGBUS\n"); exit(7); } void enospc_test(int id) { int i; int fd; char fpath[255] = {0}; char *addr; unsigned long size = 0; /* * Comma separated values against -r option indicates that the file * should be divided into two small files. * The file_ratio specifies the proportion in which the file sizes must * be divided into. * * Half of the files will be divided into size of the first ratio and the * rest of the following ratio */ if (id % 2 == 0) size = fsize * file_ratio[0]; else size = fsize * file_ratio[1]; pthread_barrier_wait(&bar); sprintf(fpath, "%s/mmap-file-%d", base_dir, id); pr_debug("Test write phase starting file %s fsize %lu, id %d\n", fpath, size, id); signal(SIGBUS, handle_sigbus); fd = open(fpath, O_RDWR | O_CREAT, 0644); if (fd < 0) { pr_debug("Open failed\n"); exit(1); } if (use_fallocate) assert(fallocate(fd, 0, 0, size) == 0); else assert(ftruncate(fd, size) == 0); addr = mmap(NULL, size, PROT_WRITE, MAP_SHARED, fd, 0); assert(addr != MAP_FAILED); for (i = 0; i < size; i++) { addr[i] = 0xAB; } assert(munmap(addr, size) != -1); close(fd); pr_debug("Test write phase completed...file %s, fsize %lu, id %d\n", fpath, size, id); } void *spawn_test_thread(void *arg) { struct thread_s *thread_info = (struct thread_s *)arg; enospc_test(thread_info->id); return NULL; } void _run_test(int threads) { int i; pthread_t tid[threads]; pthread_barrier_init(&bar, NULL, threads+1); for (i = 0; i < threads; i++) { struct thread_s *thread_info = (struct thread_s *) malloc(sizeof(struct thread_s)); thread_info->id = i; assert(pthread_create(&tid[i], NULL, spawn_test_thread, thread_info) == 0); } pthread_barrier_wait(&bar); for (i = 0; i < threads; i++) { assert(pthread_join(tid[i], NULL) == 0); } pthread_barrier_destroy(&bar); return; } static void usage(void) { printf("\nUsage: t_enospc [options]\n\n" " -t thread count\n" " -s size file size\n" " -p path set base path\n" " -f fallocate use fallocate instead of ftruncate\n" " -v verbose print debug information\n" " -r ratio ratio of file sizes, ',' separated values (def: 0.5,0.5)\n"); } int main(int argc, char *argv[]) { int opt; int threads = 0; char *ratio_ptr; while ((opt = getopt(argc, argv, ":r:p:t:s:vf")) != -1) { switch(opt) { case 't': threads = atoi(optarg); pr_debug("Testing with (%d) threads\n", threads); break; case 's': fsize = atoi(optarg); pr_debug("size: %lu Bytes\n", fsize); break; case 'p': base_dir = optarg; break; case 'r': ratio = optarg; ratio_ptr = strtok(ratio, ","); if (ratio_ptr[0] == '1') { file_ratio[0] = 1; file_ratio[1] = 1; break; } if (ratio_ptr != NULL) file_ratio[0] = strtod(ratio_ptr, NULL); ratio_ptr = strtok(NULL, ","); if (ratio_ptr != NULL) file_ratio[1] = strtod(ratio_ptr, NULL); break; case 'f': use_fallocate = true; break; case 'v': verbose = true; break; case '?': usage(); exit(1); } } /* Sanity test of parameters */ assert(threads); assert(fsize); assert(file_ratio[0]); assert(file_ratio[1]); pr_debug("Testing with (%d) threads\n", threads); pr_debug("size: %lu Bytes\n", fsize); _run_test(threads); return 0; }