blob: 4070ea51666ff15dc03dd3e2fef5feca820e03bb [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020 IBM.
* All Rights Reserved.
*
* simple mmap write multithreaded test for testing enospc
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>
#include <sys/mman.h>
#include <pthread.h>
#include <signal.h>
#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;
}