src/locktest: Change command macro names
[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 #define PAGE(a) ((a)*0x1000)
29 #define FILE_SIZE PAGE(4)
30
31 void *dax_data;
32 int nodax_fd;
33 int dax_fd;
34 bool done;
35
36 #define err_exit(op)                                                          \
37 {                                                                             \
38         fprintf(stderr, "%s %s: %s\n", __func__, op, strerror(errno));        \
39         exit(1);                                                              \
40 }
41
42 #if defined(FALLOC_FL_PUNCH_HOLE) && defined(FALLOC_FL_KEEP_SIZE)
43 void punch_hole_fn(void *ptr)
44 {
45         ssize_t read;
46         int rc;
47
48         while (!done) {
49                 read = 0;
50
51                 do {
52                         rc = pread(nodax_fd, dax_data + read, FILE_SIZE - read,
53                                         read);
54                         if (rc > 0)
55                                 read += rc;
56                 } while (rc > 0);
57
58                 if (read != FILE_SIZE || rc != 0)
59                         err_exit("pread");
60
61                 rc = fallocate(dax_fd,
62                                 FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
63                                 0, FILE_SIZE);
64                 if (rc < 0)
65                         err_exit("fallocate");
66
67                 usleep(rand() % 1000);
68         }
69 }
70 #else
71 void punch_hole_fn(void *ptr) { }
72 #endif
73
74 #if defined(FALLOC_FL_ZERO_RANGE) && defined(FALLOC_FL_KEEP_SIZE)
75 void zero_range_fn(void *ptr)
76 {
77         ssize_t read;
78         int rc;
79
80         while (!done) {
81                 read = 0;
82
83                 do {
84                         rc = pread(nodax_fd, dax_data + read, FILE_SIZE - read,
85                                         read);
86                         if (rc > 0)
87                                 read += rc;
88                 } while (rc > 0);
89
90                 if (read != FILE_SIZE || rc != 0)
91                         err_exit("pread");
92
93                 rc = fallocate(dax_fd,
94                                 FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE,
95                                 0, FILE_SIZE);
96                 if (rc < 0)
97                         err_exit("fallocate");
98
99                 usleep(rand() % 1000);
100         }
101 }
102 #else
103 void zero_range_fn(void *ptr) { }
104 #endif
105
106 void truncate_down_fn(void *ptr)
107 {
108         ssize_t read;
109         int rc;
110
111         while (!done) {
112                 read = 0;
113
114                 if (ftruncate(dax_fd, 0) < 0)
115                         err_exit("ftruncate");
116                 if (fallocate(dax_fd, 0, 0, FILE_SIZE) < 0)
117                         err_exit("fallocate");
118
119                 do {
120                         rc = pread(nodax_fd, dax_data + read, FILE_SIZE - read,
121                                         read);
122                         if (rc > 0)
123                                 read += rc;
124                 } while (rc > 0);
125
126                 /*
127                  * For this test we ignore errors from pread().  These errors
128                  * can happen if we try and read while the other thread has
129                  * made the file size 0.
130                  */
131
132                 usleep(rand() % 1000);
133         }
134 }
135
136 #ifdef FALLOC_FL_COLLAPSE_RANGE
137 void collapse_range_fn(void *ptr)
138 {
139         ssize_t read;
140         int rc;
141
142         while (!done) {
143                 read = 0;
144
145                 if (fallocate(dax_fd, 0, 0, FILE_SIZE) < 0)
146                         err_exit("fallocate 1");
147                 if (fallocate(dax_fd, FALLOC_FL_COLLAPSE_RANGE, 0, PAGE(1)) < 0)
148                         err_exit("fallocate 2");
149                 if (fallocate(dax_fd, 0, 0, FILE_SIZE) < 0)
150                         err_exit("fallocate 3");
151
152                 do {
153                         rc = pread(nodax_fd, dax_data + read, FILE_SIZE - read,
154                                         read);
155                         if (rc > 0)
156                                 read += rc;
157                 } while (rc > 0);
158
159                 /* For this test we ignore errors from pread. */
160
161                 usleep(rand() % 1000);
162         }
163 }
164 #else
165 void collapse_range_fn(void *ptr) { }
166 #endif
167
168 void run_test(void (*test_fn)(void *))
169 {
170         const int NUM_THREADS = 2;
171         pthread_t worker_thread[NUM_THREADS];
172         int i;
173
174         done = 0;
175         for (i = 0; i < NUM_THREADS; i++)
176                 pthread_create(&worker_thread[i], NULL, (void*)test_fn, NULL);
177
178         sleep(1);
179         done = 1;
180
181         for (i = 0; i < NUM_THREADS; i++)
182                 pthread_join(worker_thread[i], NULL);
183 }
184
185 int main(int argc, char *argv[])
186 {
187         int err;
188
189         if (argc != 3) {
190                 printf("Usage: %s <dax file> <non-dax file>\n",
191                                 basename(argv[0]));
192                 exit(0);
193         }
194
195         dax_fd = open(argv[1], O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
196         if (dax_fd < 0)
197                 err_exit("dax_fd open");
198
199         nodax_fd = open(argv[2], O_RDWR|O_CREAT|O_DIRECT, S_IRUSR|S_IWUSR);
200         if (nodax_fd < 0)
201                 err_exit("nodax_fd open");
202
203         if (ftruncate(dax_fd, 0) < 0)
204                 err_exit("dax_fd ftruncate");
205         if (fallocate(dax_fd, 0, 0, FILE_SIZE) < 0)
206                 err_exit("dax_fd fallocate");
207
208         if (ftruncate(nodax_fd, 0) < 0)
209                 err_exit("nodax_fd ftruncate");
210         if (fallocate(nodax_fd, 0, 0, FILE_SIZE) < 0)
211                 err_exit("nodax_fd fallocate");
212
213         dax_data = mmap(NULL, FILE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED,
214                         dax_fd, 0);
215         if (dax_data == MAP_FAILED)
216                 err_exit("mmap");
217
218         run_test(&punch_hole_fn);
219         run_test(&zero_range_fn);
220         run_test(&truncate_down_fn);
221         run_test(&collapse_range_fn);
222
223         if (munmap(dax_data, FILE_SIZE) != 0)
224                 err_exit("munmap");
225
226         err = close(dax_fd);
227         if (err < 0)
228                 err_exit("dax_fd close");
229
230         err = close(nodax_fd);
231         if (err < 0)
232                 err_exit("nodax_fd close");
233
234         return 0;
235 }