generic: ENOSPC regression test in a multi-threaded scenario
[xfstests-dev.git] / src / t_enospc.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2020 IBM.
4  * All Rights Reserved.
5  *
6  * simple mmap write multithreaded test for testing enospc
7  */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <stdbool.h>
11 #include <unistd.h>
12 #include <fcntl.h>
13 #include <string.h>
14 #include <assert.h>
15 #include <sys/mman.h>
16 #include <pthread.h>
17 #include <signal.h>
18
19 #define pr_debug(fmt, ...) do { \
20     if (verbose) { \
21         printf (fmt, ##__VA_ARGS__); \
22     } \
23 } while (0)
24
25 struct thread_s {
26         int id;
27 };
28
29 static float file_ratio[2] = {0.5, 0.5};
30 static unsigned long fsize = 0;
31 static char *base_dir = ".";
32 static char *ratio = "1";
33
34 static bool use_fallocate = false;
35 static bool verbose = false;
36
37 pthread_barrier_t bar;
38
39 void handle_sigbus(int sig)
40 {
41         pr_debug("Enospc test failed with SIGBUS\n");
42         exit(7);
43 }
44
45 void enospc_test(int id)
46 {
47         int fd;
48         char fpath[255] = {0};
49         char *addr;
50         unsigned long size = 0;
51
52         /*
53          * Comma separated values against -r option indicates that the file
54          * should be divided into two small files.
55          * The file_ratio specifies the proportion in which the file sizes must
56          * be divided into.
57          *
58          * Half of the files will be divided into size of the first ratio and the
59          * rest of the following ratio
60          */
61
62         if (id % 2 == 0)
63                 size = fsize * file_ratio[0];
64         else
65                 size = fsize * file_ratio[1];
66
67         pthread_barrier_wait(&bar);
68
69         sprintf(fpath, "%s/mmap-file-%d", base_dir, id);
70         pr_debug("Test write phase starting file %s fsize %lu, id %d\n", fpath, size, id);
71
72         signal(SIGBUS, handle_sigbus);
73
74         fd = open(fpath, O_RDWR | O_CREAT, 0644);
75         if (fd < 0) {
76                 pr_debug("Open failed\n");
77                 exit(1);
78         }
79
80         if (use_fallocate)
81                 assert(fallocate(fd, 0, 0, size) == 0);
82         else
83                 assert(ftruncate(fd, size) == 0);
84
85         addr = mmap(NULL, size, PROT_WRITE, MAP_SHARED, fd, 0);
86         assert(addr != MAP_FAILED);
87
88         for (int i = 0; i < size; i++) {
89                 addr[i] = 0xAB;
90         }
91
92         assert(munmap(addr, size) != -1);
93         close(fd);
94         pr_debug("Test write phase completed...file %s, fsize %lu, id %d\n", fpath, size, id);
95 }
96
97 void *spawn_test_thread(void *arg)
98 {
99         struct thread_s *thread_info = (struct thread_s *)arg;
100
101         enospc_test(thread_info->id);
102         return NULL;
103 }
104
105 void _run_test(int threads)
106 {
107         pthread_t tid[threads];
108
109         pthread_barrier_init(&bar, NULL, threads+1);
110         for (int i = 0; i < threads; i++) {
111                 struct thread_s *thread_info = (struct thread_s *) malloc(sizeof(struct thread_s));
112                 thread_info->id = i;
113                 assert(pthread_create(&tid[i], NULL, spawn_test_thread, thread_info) == 0);
114         }
115
116         pthread_barrier_wait(&bar);
117
118         for (int i = 0; i <threads; i++) {
119                 assert(pthread_join(tid[i], NULL) == 0);
120         }
121
122         pthread_barrier_destroy(&bar);
123         return;
124 }
125
126 static void usage(void)
127 {
128         printf("\nUsage: t_enospc [options]\n\n"
129                " -t                    thread count\n"
130                " -s size               file size\n"
131                " -p path               set base path\n"
132                " -f fallocate          use fallocate instead of ftruncate\n"
133                " -v verbose            print debug information\n"
134                " -r ratio              ratio of file sizes, ',' separated values (def: 0.5,0.5)\n");
135 }
136
137 int main(int argc, char *argv[])
138 {
139         int opt;
140         int threads = 0;
141         char *ratio_ptr;
142
143         while ((opt = getopt(argc, argv, ":r:p:t:s:vf")) != -1) {
144                 switch(opt) {
145                 case 't':
146                         threads = atoi(optarg);
147                         pr_debug("Testing with (%d) threads\n", threads);
148                         break;
149                 case 's':
150                         fsize = atoi(optarg);
151                         pr_debug("size: %lu Bytes\n", fsize);
152                         break;
153                 case 'p':
154                         base_dir = optarg;
155                         break;
156                 case 'r':
157                         ratio = optarg;
158                         ratio_ptr = strtok(ratio, ",");
159                         if (ratio_ptr[0] == '1') {
160                                 file_ratio[0] = 1;
161                                 file_ratio[1] = 1;
162                                 break;
163                         }
164                         if (ratio_ptr != NULL)
165                                 file_ratio[0] = strtod(ratio_ptr, NULL);
166                         ratio_ptr = strtok(NULL, ",");
167                         if (ratio_ptr != NULL)
168                                 file_ratio[1] = strtod(ratio_ptr, NULL);
169                         break;
170                 case 'f':
171                         use_fallocate = true;
172                         break;
173                 case 'v':
174                         verbose = true;
175                         break;
176                 case '?':
177                         usage();
178                         exit(1);
179                 }
180         }
181
182         /* Sanity test of parameters */
183         assert(threads);
184         assert(fsize);
185         assert(file_ratio[0]);
186         assert(file_ratio[1]);
187
188         pr_debug("Testing with (%d) threads\n", threads);
189         pr_debug("size: %lu Bytes\n", fsize);
190
191         _run_test(threads);
192         return 0;
193 }