overlay: Test invalidate of readdir cache
[xfstests-dev.git] / src / t_mmap_collision.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2018 Intel Corporation.
4  *
5  * As of kernel version 4.18-rc6 Linux has an issue with ext4+DAX where DMA
6  * and direct I/O operations aren't synchronized with respect to operations
7  * which can change the block mappings of an inode.  This means that we can
8  * schedule an I/O for an inode and have the block mapping for that inode
9  * change before the I/O is actually complete.  So, blocks which were once
10  * allocated to a given inode and then freed could still have I/O operations
11  * happening to them.  If these blocks have also been reallocated to a
12  * different inode, this interaction can lead to data corruption.
13  *
14  * This test exercises four of the paths in ext4 which hit this issue.
15  */
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <pthread.h>
19 #include <stdbool.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/mman.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27
28 void *dax_data;
29 int nodax_fd;
30 int dax_fd;
31 bool done;
32 static int pagesize;
33 static int file_size;
34
35 #define err_exit(op)                                                          \
36 {                                                                             \
37         fprintf(stderr, "%s %s: %s\n", __func__, op, strerror(errno));        \
38         exit(1);                                                              \
39 }
40
41 #if defined(FALLOC_FL_PUNCH_HOLE) && defined(FALLOC_FL_KEEP_SIZE)
42 void punch_hole_fn(void *ptr)
43 {
44         ssize_t read;
45         int rc;
46
47         while (!done) {
48                 read = 0;
49
50                 do {
51                         rc = pread(nodax_fd, dax_data + read, file_size - read,
52                                         read);
53                         if (rc > 0)
54                                 read += rc;
55                 } while (rc > 0);
56
57                 if (read != file_size || rc != 0)
58                         err_exit("pread");
59
60                 rc = fallocate(dax_fd,
61                                 FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
62                                 0, file_size);
63                 if (rc < 0)
64                         err_exit("fallocate");
65
66                 usleep(rand() % 1000);
67         }
68 }
69 #else
70 void punch_hole_fn(void *ptr) { }
71 #endif
72
73 #if defined(FALLOC_FL_ZERO_RANGE) && defined(FALLOC_FL_KEEP_SIZE)
74 void zero_range_fn(void *ptr)
75 {
76         ssize_t read;
77         int rc;
78
79         while (!done) {
80                 read = 0;
81
82                 do {
83                         rc = pread(nodax_fd, dax_data + read, file_size - read,
84                                         read);
85                         if (rc > 0)
86                                 read += rc;
87                 } while (rc > 0);
88
89                 if (read != file_size || rc != 0)
90                         err_exit("pread");
91
92                 rc = fallocate(dax_fd,
93                                 FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE,
94                                 0, file_size);
95                 if (rc < 0)
96                         err_exit("fallocate");
97
98                 usleep(rand() % 1000);
99         }
100 }
101 #else
102 void zero_range_fn(void *ptr) { }
103 #endif
104
105 void truncate_down_fn(void *ptr)
106 {
107         ssize_t read;
108         int rc;
109
110         while (!done) {
111                 read = 0;
112
113                 if (ftruncate(dax_fd, 0) < 0)
114                         err_exit("ftruncate");
115                 if (fallocate(dax_fd, 0, 0, file_size) < 0)
116                         err_exit("fallocate");
117
118                 do {
119                         rc = pread(nodax_fd, dax_data + read, file_size - read,
120                                         read);
121                         if (rc > 0)
122                                 read += rc;
123                 } while (rc > 0);
124
125                 /*
126                  * For this test we ignore errors from pread().  These errors
127                  * can happen if we try and read while the other thread has
128                  * made the file size 0.
129                  */
130
131                 usleep(rand() % 1000);
132         }
133 }
134
135 #ifdef FALLOC_FL_COLLAPSE_RANGE
136 void collapse_range_fn(void *ptr)
137 {
138         ssize_t read;
139         int rc;
140
141         while (!done) {
142                 read = 0;
143
144                 if (fallocate(dax_fd, 0, 0, file_size) < 0)
145                         err_exit("fallocate 1");
146                 if (fallocate(dax_fd, FALLOC_FL_COLLAPSE_RANGE, 0, pagesize) < 0)
147                         err_exit("fallocate 2");
148                 if (fallocate(dax_fd, 0, 0, file_size) < 0)
149                         err_exit("fallocate 3");
150
151                 do {
152                         rc = pread(nodax_fd, dax_data + read, file_size - read,
153                                         read);
154                         if (rc > 0)
155                                 read += rc;
156                 } while (rc > 0);
157
158                 /* For this test we ignore errors from pread. */
159
160                 usleep(rand() % 1000);
161         }
162 }
163 #else
164 void collapse_range_fn(void *ptr) { }
165 #endif
166
167 void run_test(void (*test_fn)(void *))
168 {
169         const int NUM_THREADS = 2;
170         pthread_t worker_thread[NUM_THREADS];
171         int i;
172
173         done = 0;
174         for (i = 0; i < NUM_THREADS; i++)
175                 pthread_create(&worker_thread[i], NULL, (void*)test_fn, NULL);
176
177         sleep(1);
178         done = 1;
179
180         for (i = 0; i < NUM_THREADS; i++)
181                 pthread_join(worker_thread[i], NULL);
182 }
183
184 int main(int argc, char *argv[])
185 {
186         int err;
187
188         if (argc != 3) {
189                 printf("Usage: %s <dax file> <non-dax file>\n",
190                                 basename(argv[0]));
191                 exit(0);
192         }
193
194         pagesize = getpagesize();
195         file_size = 4 * pagesize;
196
197         dax_fd = open(argv[1], O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
198         if (dax_fd < 0)
199                 err_exit("dax_fd open");
200
201         nodax_fd = open(argv[2], O_RDWR|O_CREAT|O_DIRECT, S_IRUSR|S_IWUSR);
202         if (nodax_fd < 0)
203                 err_exit("nodax_fd open");
204
205         if (ftruncate(dax_fd, 0) < 0)
206                 err_exit("dax_fd ftruncate");
207         if (fallocate(dax_fd, 0, 0, file_size) < 0)
208                 err_exit("dax_fd fallocate");
209
210         if (ftruncate(nodax_fd, 0) < 0)
211                 err_exit("nodax_fd ftruncate");
212         if (fallocate(nodax_fd, 0, 0, file_size) < 0)
213                 err_exit("nodax_fd fallocate");
214
215         dax_data = mmap(NULL, file_size, PROT_READ|PROT_WRITE, MAP_SHARED,
216                         dax_fd, 0);
217         if (dax_data == MAP_FAILED)
218                 err_exit("mmap");
219
220         run_test(&punch_hole_fn);
221         run_test(&zero_range_fn);
222         run_test(&truncate_down_fn);
223         run_test(&collapse_range_fn);
224
225         if (munmap(dax_data, file_size) != 0)
226                 err_exit("munmap");
227
228         err = close(dax_fd);
229         if (err < 0)
230                 err_exit("dax_fd close");
231
232         err = close(nodax_fd);
233         if (err < 0)
234                 err_exit("nodax_fd close");
235
236         return 0;
237 }