Commit | Line | Data |
---|---|---|
c1d7c514 | 1 | // SPDX-License-Identifier: GPL-2.0 |
294e30fe JB |
2 | /* |
3 | * Copyright (C) 2013 Fusion IO. All rights reserved. | |
294e30fe JB |
4 | */ |
5 | ||
6 | #include <linux/pagemap.h> | |
7 | #include <linux/sched.h> | |
0f331229 | 8 | #include <linux/slab.h> |
ee22184b | 9 | #include <linux/sizes.h> |
294e30fe | 10 | #include "btrfs-tests.h" |
ed9e4afd | 11 | #include "../ctree.h" |
294e30fe JB |
12 | #include "../extent_io.h" |
13 | ||
14 | #define PROCESS_UNLOCK (1 << 0) | |
15 | #define PROCESS_RELEASE (1 << 1) | |
16 | #define PROCESS_TEST_LOCKED (1 << 2) | |
17 | ||
18 | static noinline int process_page_range(struct inode *inode, u64 start, u64 end, | |
19 | unsigned long flags) | |
20 | { | |
21 | int ret; | |
22 | struct page *pages[16]; | |
09cbfeaf KS |
23 | unsigned long index = start >> PAGE_SHIFT; |
24 | unsigned long end_index = end >> PAGE_SHIFT; | |
294e30fe JB |
25 | unsigned long nr_pages = end_index - index + 1; |
26 | int i; | |
27 | int count = 0; | |
28 | int loops = 0; | |
29 | ||
30 | while (nr_pages > 0) { | |
31 | ret = find_get_pages_contig(inode->i_mapping, index, | |
32 | min_t(unsigned long, nr_pages, | |
33 | ARRAY_SIZE(pages)), pages); | |
34 | for (i = 0; i < ret; i++) { | |
35 | if (flags & PROCESS_TEST_LOCKED && | |
36 | !PageLocked(pages[i])) | |
37 | count++; | |
38 | if (flags & PROCESS_UNLOCK && PageLocked(pages[i])) | |
39 | unlock_page(pages[i]); | |
09cbfeaf | 40 | put_page(pages[i]); |
294e30fe | 41 | if (flags & PROCESS_RELEASE) |
09cbfeaf | 42 | put_page(pages[i]); |
294e30fe JB |
43 | } |
44 | nr_pages -= ret; | |
45 | index += ret; | |
46 | cond_resched(); | |
47 | loops++; | |
48 | if (loops > 100000) { | |
3c7251f2 DS |
49 | printk(KERN_ERR |
50 | "stuck in a loop, start %llu, end %llu, nr_pages %lu, ret %d\n", | |
51 | start, end, nr_pages, ret); | |
294e30fe JB |
52 | break; |
53 | } | |
54 | } | |
55 | return count; | |
56 | } | |
57 | ||
b9ef22de | 58 | static int test_find_delalloc(u32 sectorsize) |
294e30fe JB |
59 | { |
60 | struct inode *inode; | |
61 | struct extent_io_tree tmp; | |
62 | struct page *page; | |
63 | struct page *locked_page = NULL; | |
64 | unsigned long index = 0; | |
d9cb2459 QW |
65 | /* In this test we need at least 2 file extents at its maximum size */ |
66 | u64 max_bytes = BTRFS_MAX_EXTENT_SIZE; | |
67 | u64 total_dirty = 2 * max_bytes; | |
294e30fe | 68 | u64 start, end, test_start; |
3522e903 | 69 | bool found; |
294e30fe JB |
70 | int ret = -EINVAL; |
71 | ||
315b76b4 | 72 | test_msg("running find delalloc tests"); |
0f331229 | 73 | |
294e30fe JB |
74 | inode = btrfs_new_test_inode(); |
75 | if (!inode) { | |
3c7251f2 | 76 | test_err("failed to allocate test inode"); |
294e30fe JB |
77 | return -ENOMEM; |
78 | } | |
79 | ||
c258d6e3 | 80 | extent_io_tree_init(NULL, &tmp, NULL); |
294e30fe JB |
81 | |
82 | /* | |
83 | * First go through and create and mark all of our pages dirty, we pin | |
84 | * everything to make sure our pages don't get evicted and screw up our | |
85 | * test. | |
86 | */ | |
09cbfeaf | 87 | for (index = 0; index < (total_dirty >> PAGE_SHIFT); index++) { |
8cce83ba | 88 | page = find_or_create_page(inode->i_mapping, index, GFP_KERNEL); |
294e30fe | 89 | if (!page) { |
3c7251f2 | 90 | test_err("failed to allocate test page"); |
294e30fe JB |
91 | ret = -ENOMEM; |
92 | goto out; | |
93 | } | |
94 | SetPageDirty(page); | |
95 | if (index) { | |
96 | unlock_page(page); | |
97 | } else { | |
09cbfeaf | 98 | get_page(page); |
294e30fe JB |
99 | locked_page = page; |
100 | } | |
101 | } | |
102 | ||
103 | /* Test this scenario | |
104 | * |--- delalloc ---| | |
105 | * |--- search ---| | |
106 | */ | |
e3b8a485 | 107 | set_extent_delalloc(&tmp, 0, sectorsize - 1, 0, NULL); |
294e30fe JB |
108 | start = 0; |
109 | end = 0; | |
ce9f967f | 110 | found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, |
917aacec | 111 | &end); |
294e30fe | 112 | if (!found) { |
3c7251f2 | 113 | test_err("should have found at least one delalloc"); |
294e30fe JB |
114 | goto out_bits; |
115 | } | |
b9ef22de | 116 | if (start != 0 || end != (sectorsize - 1)) { |
3c7251f2 | 117 | test_err("expected start 0 end %u, got start %llu end %llu", |
b9ef22de | 118 | sectorsize - 1, start, end); |
294e30fe JB |
119 | goto out_bits; |
120 | } | |
121 | unlock_extent(&tmp, start, end); | |
122 | unlock_page(locked_page); | |
09cbfeaf | 123 | put_page(locked_page); |
294e30fe JB |
124 | |
125 | /* | |
126 | * Test this scenario | |
127 | * | |
128 | * |--- delalloc ---| | |
129 | * |--- search ---| | |
130 | */ | |
ee22184b | 131 | test_start = SZ_64M; |
294e30fe | 132 | locked_page = find_lock_page(inode->i_mapping, |
09cbfeaf | 133 | test_start >> PAGE_SHIFT); |
294e30fe | 134 | if (!locked_page) { |
3c7251f2 | 135 | test_err("couldn't find the locked page"); |
294e30fe JB |
136 | goto out_bits; |
137 | } | |
e3b8a485 | 138 | set_extent_delalloc(&tmp, sectorsize, max_bytes - 1, 0, NULL); |
294e30fe JB |
139 | start = test_start; |
140 | end = 0; | |
ce9f967f | 141 | found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, |
917aacec | 142 | &end); |
294e30fe | 143 | if (!found) { |
3c7251f2 | 144 | test_err("couldn't find delalloc in our range"); |
294e30fe JB |
145 | goto out_bits; |
146 | } | |
147 | if (start != test_start || end != max_bytes - 1) { | |
3c7251f2 DS |
148 | test_err("expected start %llu end %llu, got start %llu, end %llu", |
149 | test_start, max_bytes - 1, start, end); | |
294e30fe JB |
150 | goto out_bits; |
151 | } | |
152 | if (process_page_range(inode, start, end, | |
153 | PROCESS_TEST_LOCKED | PROCESS_UNLOCK)) { | |
3c7251f2 | 154 | test_err("there were unlocked pages in the range"); |
294e30fe JB |
155 | goto out_bits; |
156 | } | |
157 | unlock_extent(&tmp, start, end); | |
158 | /* locked_page was unlocked above */ | |
09cbfeaf | 159 | put_page(locked_page); |
294e30fe JB |
160 | |
161 | /* | |
162 | * Test this scenario | |
163 | * |--- delalloc ---| | |
164 | * |--- search ---| | |
165 | */ | |
b9ef22de | 166 | test_start = max_bytes + sectorsize; |
294e30fe | 167 | locked_page = find_lock_page(inode->i_mapping, test_start >> |
09cbfeaf | 168 | PAGE_SHIFT); |
294e30fe | 169 | if (!locked_page) { |
3c7251f2 | 170 | test_err("couldn't find the locked page"); |
294e30fe JB |
171 | goto out_bits; |
172 | } | |
173 | start = test_start; | |
174 | end = 0; | |
ce9f967f | 175 | found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, |
917aacec | 176 | &end); |
294e30fe | 177 | if (found) { |
3c7251f2 | 178 | test_err("found range when we shouldn't have"); |
294e30fe JB |
179 | goto out_bits; |
180 | } | |
181 | if (end != (u64)-1) { | |
3c7251f2 | 182 | test_err("did not return the proper end offset"); |
294e30fe JB |
183 | goto out_bits; |
184 | } | |
185 | ||
186 | /* | |
187 | * Test this scenario | |
188 | * [------- delalloc -------| | |
189 | * [max_bytes]|-- search--| | |
190 | * | |
191 | * We are re-using our test_start from above since it works out well. | |
192 | */ | |
e3b8a485 | 193 | set_extent_delalloc(&tmp, max_bytes, total_dirty - 1, 0, NULL); |
294e30fe JB |
194 | start = test_start; |
195 | end = 0; | |
ce9f967f | 196 | found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, |
917aacec | 197 | &end); |
294e30fe | 198 | if (!found) { |
3c7251f2 | 199 | test_err("didn't find our range"); |
294e30fe JB |
200 | goto out_bits; |
201 | } | |
202 | if (start != test_start || end != total_dirty - 1) { | |
3c7251f2 | 203 | test_err("expected start %llu end %llu, got start %llu end %llu", |
294e30fe JB |
204 | test_start, total_dirty - 1, start, end); |
205 | goto out_bits; | |
206 | } | |
207 | if (process_page_range(inode, start, end, | |
208 | PROCESS_TEST_LOCKED | PROCESS_UNLOCK)) { | |
3c7251f2 | 209 | test_err("pages in range were not all locked"); |
294e30fe JB |
210 | goto out_bits; |
211 | } | |
212 | unlock_extent(&tmp, start, end); | |
213 | ||
214 | /* | |
215 | * Now to test where we run into a page that is no longer dirty in the | |
216 | * range we want to find. | |
217 | */ | |
ee22184b | 218 | page = find_get_page(inode->i_mapping, |
09cbfeaf | 219 | (max_bytes + SZ_1M) >> PAGE_SHIFT); |
294e30fe | 220 | if (!page) { |
3c7251f2 | 221 | test_err("couldn't find our page"); |
294e30fe JB |
222 | goto out_bits; |
223 | } | |
224 | ClearPageDirty(page); | |
09cbfeaf | 225 | put_page(page); |
294e30fe JB |
226 | |
227 | /* We unlocked it in the previous test */ | |
228 | lock_page(locked_page); | |
229 | start = test_start; | |
230 | end = 0; | |
231 | /* | |
232 | * Currently if we fail to find dirty pages in the delalloc range we | |
ea1754a0 | 233 | * will adjust max_bytes down to PAGE_SIZE and then re-search. If |
294e30fe JB |
234 | * this changes at any point in the future we will need to fix this |
235 | * tests expected behavior. | |
236 | */ | |
ce9f967f | 237 | found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, |
917aacec | 238 | &end); |
294e30fe | 239 | if (!found) { |
3c7251f2 | 240 | test_err("didn't find our range"); |
294e30fe JB |
241 | goto out_bits; |
242 | } | |
09cbfeaf | 243 | if (start != test_start && end != test_start + PAGE_SIZE - 1) { |
3c7251f2 DS |
244 | test_err("expected start %llu end %llu, got start %llu end %llu", |
245 | test_start, test_start + PAGE_SIZE - 1, start, end); | |
294e30fe JB |
246 | goto out_bits; |
247 | } | |
248 | if (process_page_range(inode, start, end, PROCESS_TEST_LOCKED | | |
249 | PROCESS_UNLOCK)) { | |
3c7251f2 | 250 | test_err("pages in range were not all locked"); |
294e30fe JB |
251 | goto out_bits; |
252 | } | |
253 | ret = 0; | |
254 | out_bits: | |
91166212 | 255 | clear_extent_bits(&tmp, 0, total_dirty - 1, (unsigned)-1); |
294e30fe JB |
256 | out: |
257 | if (locked_page) | |
09cbfeaf | 258 | put_page(locked_page); |
294e30fe JB |
259 | process_page_range(inode, 0, total_dirty - 1, |
260 | PROCESS_UNLOCK | PROCESS_RELEASE); | |
261 | iput(inode); | |
262 | return ret; | |
263 | } | |
264 | ||
9426ce75 OS |
265 | static int check_eb_bitmap(unsigned long *bitmap, struct extent_buffer *eb, |
266 | unsigned long len) | |
34b3e6c9 | 267 | { |
9426ce75 OS |
268 | unsigned long i; |
269 | ||
270 | for (i = 0; i < len * BITS_PER_BYTE; i++) { | |
271 | int bit, bit1; | |
272 | ||
273 | bit = !!test_bit(i, bitmap); | |
274 | bit1 = !!extent_buffer_test_bit(eb, 0, i); | |
275 | if (bit1 != bit) { | |
3c7251f2 | 276 | test_err("bits do not match"); |
9426ce75 OS |
277 | return -EINVAL; |
278 | } | |
279 | ||
280 | bit1 = !!extent_buffer_test_bit(eb, i / BITS_PER_BYTE, | |
281 | i % BITS_PER_BYTE); | |
282 | if (bit1 != bit) { | |
3c7251f2 | 283 | test_err("offset bits do not match"); |
9426ce75 OS |
284 | return -EINVAL; |
285 | } | |
286 | } | |
287 | return 0; | |
34b3e6c9 FX |
288 | } |
289 | ||
0f331229 OS |
290 | static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb, |
291 | unsigned long len) | |
292 | { | |
9426ce75 OS |
293 | unsigned long i, j; |
294 | u32 x; | |
295 | int ret; | |
0f331229 OS |
296 | |
297 | memset(bitmap, 0, len); | |
b159fa28 | 298 | memzero_extent_buffer(eb, 0, len); |
0f331229 | 299 | if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) { |
3c7251f2 | 300 | test_err("bitmap was not zeroed"); |
0f331229 OS |
301 | return -EINVAL; |
302 | } | |
303 | ||
304 | bitmap_set(bitmap, 0, len * BITS_PER_BYTE); | |
305 | extent_buffer_bitmap_set(eb, 0, 0, len * BITS_PER_BYTE); | |
9426ce75 OS |
306 | ret = check_eb_bitmap(bitmap, eb, len); |
307 | if (ret) { | |
3c7251f2 | 308 | test_err("setting all bits failed"); |
9426ce75 | 309 | return ret; |
0f331229 OS |
310 | } |
311 | ||
312 | bitmap_clear(bitmap, 0, len * BITS_PER_BYTE); | |
313 | extent_buffer_bitmap_clear(eb, 0, 0, len * BITS_PER_BYTE); | |
9426ce75 OS |
314 | ret = check_eb_bitmap(bitmap, eb, len); |
315 | if (ret) { | |
3c7251f2 | 316 | test_err("clearing all bits failed"); |
9426ce75 | 317 | return ret; |
0f331229 OS |
318 | } |
319 | ||
ed9e4afd FX |
320 | /* Straddling pages test */ |
321 | if (len > PAGE_SIZE) { | |
322 | bitmap_set(bitmap, | |
323 | (PAGE_SIZE - sizeof(long) / 2) * BITS_PER_BYTE, | |
324 | sizeof(long) * BITS_PER_BYTE); | |
325 | extent_buffer_bitmap_set(eb, PAGE_SIZE - sizeof(long) / 2, 0, | |
326 | sizeof(long) * BITS_PER_BYTE); | |
9426ce75 OS |
327 | ret = check_eb_bitmap(bitmap, eb, len); |
328 | if (ret) { | |
3c7251f2 | 329 | test_err("setting straddling pages failed"); |
9426ce75 | 330 | return ret; |
ed9e4afd | 331 | } |
0f331229 | 332 | |
ed9e4afd FX |
333 | bitmap_set(bitmap, 0, len * BITS_PER_BYTE); |
334 | bitmap_clear(bitmap, | |
335 | (PAGE_SIZE - sizeof(long) / 2) * BITS_PER_BYTE, | |
336 | sizeof(long) * BITS_PER_BYTE); | |
337 | extent_buffer_bitmap_set(eb, 0, 0, len * BITS_PER_BYTE); | |
338 | extent_buffer_bitmap_clear(eb, PAGE_SIZE - sizeof(long) / 2, 0, | |
339 | sizeof(long) * BITS_PER_BYTE); | |
9426ce75 OS |
340 | ret = check_eb_bitmap(bitmap, eb, len); |
341 | if (ret) { | |
3c7251f2 | 342 | test_err("clearing straddling pages failed"); |
9426ce75 | 343 | return ret; |
ed9e4afd | 344 | } |
0f331229 OS |
345 | } |
346 | ||
347 | /* | |
348 | * Generate a wonky pseudo-random bit pattern for the sake of not using | |
349 | * something repetitive that could miss some hypothetical off-by-n bug. | |
350 | */ | |
351 | x = 0; | |
9426ce75 OS |
352 | bitmap_clear(bitmap, 0, len * BITS_PER_BYTE); |
353 | extent_buffer_bitmap_clear(eb, 0, 0, len * BITS_PER_BYTE); | |
354 | for (i = 0; i < len * BITS_PER_BYTE / 32; i++) { | |
355 | x = (0x19660dULL * (u64)x + 0x3c6ef35fULL) & 0xffffffffU; | |
356 | for (j = 0; j < 32; j++) { | |
357 | if (x & (1U << j)) { | |
358 | bitmap_set(bitmap, i * 32 + j, 1); | |
359 | extent_buffer_bitmap_set(eb, 0, i * 32 + j, 1); | |
360 | } | |
0f331229 | 361 | } |
9426ce75 | 362 | } |
0f331229 | 363 | |
9426ce75 OS |
364 | ret = check_eb_bitmap(bitmap, eb, len); |
365 | if (ret) { | |
3c7251f2 | 366 | test_err("random bit pattern failed"); |
9426ce75 | 367 | return ret; |
0f331229 OS |
368 | } |
369 | ||
370 | return 0; | |
371 | } | |
372 | ||
b9ef22de | 373 | static int test_eb_bitmaps(u32 sectorsize, u32 nodesize) |
0f331229 | 374 | { |
da17066c | 375 | struct btrfs_fs_info *fs_info; |
b9ef22de | 376 | unsigned long len; |
0f331229 OS |
377 | unsigned long *bitmap; |
378 | struct extent_buffer *eb; | |
379 | int ret; | |
380 | ||
315b76b4 | 381 | test_msg("running extent buffer bitmap tests"); |
ed9e4afd FX |
382 | |
383 | /* | |
384 | * In ppc64, sectorsize can be 64K, thus 4 * 64K will be larger than | |
385 | * BTRFS_MAX_METADATA_BLOCKSIZE. | |
386 | */ | |
387 | len = (sectorsize < BTRFS_MAX_METADATA_BLOCKSIZE) | |
388 | ? sectorsize * 4 : sectorsize; | |
0f331229 | 389 | |
da17066c JM |
390 | fs_info = btrfs_alloc_dummy_fs_info(len, len); |
391 | ||
8cce83ba | 392 | bitmap = kmalloc(len, GFP_KERNEL); |
0f331229 | 393 | if (!bitmap) { |
3c7251f2 | 394 | test_err("couldn't allocate test bitmap"); |
0f331229 OS |
395 | return -ENOMEM; |
396 | } | |
397 | ||
da17066c | 398 | eb = __alloc_dummy_extent_buffer(fs_info, 0, len); |
0f331229 | 399 | if (!eb) { |
3c7251f2 | 400 | test_err("couldn't allocate test extent buffer"); |
0f331229 OS |
401 | kfree(bitmap); |
402 | return -ENOMEM; | |
403 | } | |
404 | ||
405 | ret = __test_eb_bitmaps(bitmap, eb, len); | |
406 | if (ret) | |
407 | goto out; | |
408 | ||
409 | /* Do it over again with an extent buffer which isn't page-aligned. */ | |
410 | free_extent_buffer(eb); | |
b9ef22de | 411 | eb = __alloc_dummy_extent_buffer(NULL, nodesize / 2, len); |
0f331229 | 412 | if (!eb) { |
3c7251f2 | 413 | test_err("couldn't allocate test extent buffer"); |
0f331229 OS |
414 | kfree(bitmap); |
415 | return -ENOMEM; | |
416 | } | |
417 | ||
418 | ret = __test_eb_bitmaps(bitmap, eb, len); | |
419 | out: | |
420 | free_extent_buffer(eb); | |
421 | kfree(bitmap); | |
422 | return ret; | |
423 | } | |
424 | ||
b9ef22de | 425 | int btrfs_test_extent_io(u32 sectorsize, u32 nodesize) |
294e30fe | 426 | { |
0f331229 OS |
427 | int ret; |
428 | ||
315b76b4 | 429 | test_msg("running extent I/O tests"); |
0f331229 | 430 | |
b9ef22de | 431 | ret = test_find_delalloc(sectorsize); |
0f331229 OS |
432 | if (ret) |
433 | goto out; | |
434 | ||
b9ef22de | 435 | ret = test_eb_bitmaps(sectorsize, nodesize); |
0f331229 | 436 | out: |
315b76b4 | 437 | test_msg("extent I/O tests finished"); |
0f331229 | 438 | return ret; |
294e30fe | 439 | } |