]>
Commit | Line | Data |
---|---|---|
294e30fe JB |
1 | /* |
2 | * Copyright (C) 2013 Fusion IO. All rights reserved. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public | |
6 | * License v2 as published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
11 | * General Public License for more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public | |
14 | * License along with this program; if not, write to the | |
15 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
16 | * Boston, MA 021110-1307, USA. | |
17 | */ | |
18 | ||
19 | #include <linux/pagemap.h> | |
20 | #include <linux/sched.h> | |
0f331229 | 21 | #include <linux/slab.h> |
ee22184b | 22 | #include <linux/sizes.h> |
294e30fe JB |
23 | #include "btrfs-tests.h" |
24 | #include "../extent_io.h" | |
25 | ||
26 | #define PROCESS_UNLOCK (1 << 0) | |
27 | #define PROCESS_RELEASE (1 << 1) | |
28 | #define PROCESS_TEST_LOCKED (1 << 2) | |
29 | ||
30 | static noinline int process_page_range(struct inode *inode, u64 start, u64 end, | |
31 | unsigned long flags) | |
32 | { | |
33 | int ret; | |
34 | struct page *pages[16]; | |
09cbfeaf KS |
35 | unsigned long index = start >> PAGE_SHIFT; |
36 | unsigned long end_index = end >> PAGE_SHIFT; | |
294e30fe JB |
37 | unsigned long nr_pages = end_index - index + 1; |
38 | int i; | |
39 | int count = 0; | |
40 | int loops = 0; | |
41 | ||
42 | while (nr_pages > 0) { | |
43 | ret = find_get_pages_contig(inode->i_mapping, index, | |
44 | min_t(unsigned long, nr_pages, | |
45 | ARRAY_SIZE(pages)), pages); | |
46 | for (i = 0; i < ret; i++) { | |
47 | if (flags & PROCESS_TEST_LOCKED && | |
48 | !PageLocked(pages[i])) | |
49 | count++; | |
50 | if (flags & PROCESS_UNLOCK && PageLocked(pages[i])) | |
51 | unlock_page(pages[i]); | |
09cbfeaf | 52 | put_page(pages[i]); |
294e30fe | 53 | if (flags & PROCESS_RELEASE) |
09cbfeaf | 54 | put_page(pages[i]); |
294e30fe JB |
55 | } |
56 | nr_pages -= ret; | |
57 | index += ret; | |
58 | cond_resched(); | |
59 | loops++; | |
60 | if (loops > 100000) { | |
61 | printk(KERN_ERR "stuck in a loop, start %Lu, end %Lu, nr_pages %lu, ret %d\n", start, end, nr_pages, ret); | |
62 | break; | |
63 | } | |
64 | } | |
65 | return count; | |
66 | } | |
67 | ||
68 | static int test_find_delalloc(void) | |
69 | { | |
70 | struct inode *inode; | |
71 | struct extent_io_tree tmp; | |
72 | struct page *page; | |
73 | struct page *locked_page = NULL; | |
74 | unsigned long index = 0; | |
ee22184b BL |
75 | u64 total_dirty = SZ_256M; |
76 | u64 max_bytes = SZ_128M; | |
294e30fe JB |
77 | u64 start, end, test_start; |
78 | u64 found; | |
79 | int ret = -EINVAL; | |
80 | ||
0f331229 OS |
81 | test_msg("Running find delalloc tests\n"); |
82 | ||
294e30fe JB |
83 | inode = btrfs_new_test_inode(); |
84 | if (!inode) { | |
85 | test_msg("Failed to allocate test inode\n"); | |
86 | return -ENOMEM; | |
87 | } | |
88 | ||
89 | extent_io_tree_init(&tmp, &inode->i_data); | |
90 | ||
91 | /* | |
92 | * First go through and create and mark all of our pages dirty, we pin | |
93 | * everything to make sure our pages don't get evicted and screw up our | |
94 | * test. | |
95 | */ | |
09cbfeaf | 96 | for (index = 0; index < (total_dirty >> PAGE_SHIFT); index++) { |
8cce83ba | 97 | page = find_or_create_page(inode->i_mapping, index, GFP_KERNEL); |
294e30fe JB |
98 | if (!page) { |
99 | test_msg("Failed to allocate test page\n"); | |
100 | ret = -ENOMEM; | |
101 | goto out; | |
102 | } | |
103 | SetPageDirty(page); | |
104 | if (index) { | |
105 | unlock_page(page); | |
106 | } else { | |
09cbfeaf | 107 | get_page(page); |
294e30fe JB |
108 | locked_page = page; |
109 | } | |
110 | } | |
111 | ||
112 | /* Test this scenario | |
113 | * |--- delalloc ---| | |
114 | * |--- search ---| | |
115 | */ | |
7cd8c752 | 116 | set_extent_delalloc(&tmp, 0, 4095, NULL); |
294e30fe JB |
117 | start = 0; |
118 | end = 0; | |
119 | found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, | |
120 | &end, max_bytes); | |
121 | if (!found) { | |
122 | test_msg("Should have found at least one delalloc\n"); | |
123 | goto out_bits; | |
124 | } | |
125 | if (start != 0 || end != 4095) { | |
126 | test_msg("Expected start 0 end 4095, got start %Lu end %Lu\n", | |
127 | start, end); | |
128 | goto out_bits; | |
129 | } | |
130 | unlock_extent(&tmp, start, end); | |
131 | unlock_page(locked_page); | |
09cbfeaf | 132 | put_page(locked_page); |
294e30fe JB |
133 | |
134 | /* | |
135 | * Test this scenario | |
136 | * | |
137 | * |--- delalloc ---| | |
138 | * |--- search ---| | |
139 | */ | |
ee22184b | 140 | test_start = SZ_64M; |
294e30fe | 141 | locked_page = find_lock_page(inode->i_mapping, |
09cbfeaf | 142 | test_start >> PAGE_SHIFT); |
294e30fe JB |
143 | if (!locked_page) { |
144 | test_msg("Couldn't find the locked page\n"); | |
145 | goto out_bits; | |
146 | } | |
7cd8c752 | 147 | set_extent_delalloc(&tmp, 4096, max_bytes - 1, NULL); |
294e30fe JB |
148 | start = test_start; |
149 | end = 0; | |
150 | found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, | |
151 | &end, max_bytes); | |
152 | if (!found) { | |
153 | test_msg("Couldn't find delalloc in our range\n"); | |
154 | goto out_bits; | |
155 | } | |
156 | if (start != test_start || end != max_bytes - 1) { | |
157 | test_msg("Expected start %Lu end %Lu, got start %Lu, end " | |
158 | "%Lu\n", test_start, max_bytes - 1, start, end); | |
159 | goto out_bits; | |
160 | } | |
161 | if (process_page_range(inode, start, end, | |
162 | PROCESS_TEST_LOCKED | PROCESS_UNLOCK)) { | |
163 | test_msg("There were unlocked pages in the range\n"); | |
164 | goto out_bits; | |
165 | } | |
166 | unlock_extent(&tmp, start, end); | |
167 | /* locked_page was unlocked above */ | |
09cbfeaf | 168 | put_page(locked_page); |
294e30fe JB |
169 | |
170 | /* | |
171 | * Test this scenario | |
172 | * |--- delalloc ---| | |
173 | * |--- search ---| | |
174 | */ | |
175 | test_start = max_bytes + 4096; | |
176 | locked_page = find_lock_page(inode->i_mapping, test_start >> | |
09cbfeaf | 177 | PAGE_SHIFT); |
294e30fe | 178 | if (!locked_page) { |
01327610 | 179 | test_msg("Couldn't find the locked page\n"); |
294e30fe JB |
180 | goto out_bits; |
181 | } | |
182 | start = test_start; | |
183 | end = 0; | |
184 | found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, | |
185 | &end, max_bytes); | |
186 | if (found) { | |
187 | test_msg("Found range when we shouldn't have\n"); | |
188 | goto out_bits; | |
189 | } | |
190 | if (end != (u64)-1) { | |
191 | test_msg("Did not return the proper end offset\n"); | |
192 | goto out_bits; | |
193 | } | |
194 | ||
195 | /* | |
196 | * Test this scenario | |
197 | * [------- delalloc -------| | |
198 | * [max_bytes]|-- search--| | |
199 | * | |
200 | * We are re-using our test_start from above since it works out well. | |
201 | */ | |
7cd8c752 | 202 | set_extent_delalloc(&tmp, max_bytes, total_dirty - 1, NULL); |
294e30fe JB |
203 | start = test_start; |
204 | end = 0; | |
205 | found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, | |
206 | &end, max_bytes); | |
207 | if (!found) { | |
208 | test_msg("Didn't find our range\n"); | |
209 | goto out_bits; | |
210 | } | |
211 | if (start != test_start || end != total_dirty - 1) { | |
212 | test_msg("Expected start %Lu end %Lu, got start %Lu end %Lu\n", | |
213 | test_start, total_dirty - 1, start, end); | |
214 | goto out_bits; | |
215 | } | |
216 | if (process_page_range(inode, start, end, | |
217 | PROCESS_TEST_LOCKED | PROCESS_UNLOCK)) { | |
218 | test_msg("Pages in range were not all locked\n"); | |
219 | goto out_bits; | |
220 | } | |
221 | unlock_extent(&tmp, start, end); | |
222 | ||
223 | /* | |
224 | * Now to test where we run into a page that is no longer dirty in the | |
225 | * range we want to find. | |
226 | */ | |
ee22184b | 227 | page = find_get_page(inode->i_mapping, |
09cbfeaf | 228 | (max_bytes + SZ_1M) >> PAGE_SHIFT); |
294e30fe JB |
229 | if (!page) { |
230 | test_msg("Couldn't find our page\n"); | |
231 | goto out_bits; | |
232 | } | |
233 | ClearPageDirty(page); | |
09cbfeaf | 234 | put_page(page); |
294e30fe JB |
235 | |
236 | /* We unlocked it in the previous test */ | |
237 | lock_page(locked_page); | |
238 | start = test_start; | |
239 | end = 0; | |
240 | /* | |
241 | * Currently if we fail to find dirty pages in the delalloc range we | |
ea1754a0 | 242 | * will adjust max_bytes down to PAGE_SIZE and then re-search. If |
294e30fe JB |
243 | * this changes at any point in the future we will need to fix this |
244 | * tests expected behavior. | |
245 | */ | |
246 | found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, | |
247 | &end, max_bytes); | |
248 | if (!found) { | |
249 | test_msg("Didn't find our range\n"); | |
250 | goto out_bits; | |
251 | } | |
09cbfeaf | 252 | if (start != test_start && end != test_start + PAGE_SIZE - 1) { |
294e30fe | 253 | test_msg("Expected start %Lu end %Lu, got start %Lu end %Lu\n", |
09cbfeaf | 254 | test_start, test_start + PAGE_SIZE - 1, start, |
294e30fe JB |
255 | end); |
256 | goto out_bits; | |
257 | } | |
258 | if (process_page_range(inode, start, end, PROCESS_TEST_LOCKED | | |
259 | PROCESS_UNLOCK)) { | |
260 | test_msg("Pages in range were not all locked\n"); | |
261 | goto out_bits; | |
262 | } | |
263 | ret = 0; | |
264 | out_bits: | |
91166212 | 265 | clear_extent_bits(&tmp, 0, total_dirty - 1, (unsigned)-1); |
294e30fe JB |
266 | out: |
267 | if (locked_page) | |
09cbfeaf | 268 | put_page(locked_page); |
294e30fe JB |
269 | process_page_range(inode, 0, total_dirty - 1, |
270 | PROCESS_UNLOCK | PROCESS_RELEASE); | |
271 | iput(inode); | |
272 | return ret; | |
273 | } | |
274 | ||
0f331229 OS |
275 | static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb, |
276 | unsigned long len) | |
277 | { | |
278 | unsigned long i, x; | |
279 | ||
280 | memset(bitmap, 0, len); | |
281 | memset_extent_buffer(eb, 0, 0, len); | |
282 | if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) { | |
283 | test_msg("Bitmap was not zeroed\n"); | |
284 | return -EINVAL; | |
285 | } | |
286 | ||
287 | bitmap_set(bitmap, 0, len * BITS_PER_BYTE); | |
288 | extent_buffer_bitmap_set(eb, 0, 0, len * BITS_PER_BYTE); | |
289 | if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) { | |
290 | test_msg("Setting all bits failed\n"); | |
291 | return -EINVAL; | |
292 | } | |
293 | ||
294 | bitmap_clear(bitmap, 0, len * BITS_PER_BYTE); | |
295 | extent_buffer_bitmap_clear(eb, 0, 0, len * BITS_PER_BYTE); | |
296 | if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) { | |
297 | test_msg("Clearing all bits failed\n"); | |
298 | return -EINVAL; | |
299 | } | |
300 | ||
09cbfeaf | 301 | bitmap_set(bitmap, (PAGE_SIZE - sizeof(long) / 2) * BITS_PER_BYTE, |
0f331229 | 302 | sizeof(long) * BITS_PER_BYTE); |
09cbfeaf | 303 | extent_buffer_bitmap_set(eb, PAGE_SIZE - sizeof(long) / 2, 0, |
0f331229 OS |
304 | sizeof(long) * BITS_PER_BYTE); |
305 | if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) { | |
306 | test_msg("Setting straddling pages failed\n"); | |
307 | return -EINVAL; | |
308 | } | |
309 | ||
310 | bitmap_set(bitmap, 0, len * BITS_PER_BYTE); | |
311 | bitmap_clear(bitmap, | |
09cbfeaf | 312 | (PAGE_SIZE - sizeof(long) / 2) * BITS_PER_BYTE, |
0f331229 OS |
313 | sizeof(long) * BITS_PER_BYTE); |
314 | extent_buffer_bitmap_set(eb, 0, 0, len * BITS_PER_BYTE); | |
09cbfeaf | 315 | extent_buffer_bitmap_clear(eb, PAGE_SIZE - sizeof(long) / 2, 0, |
0f331229 OS |
316 | sizeof(long) * BITS_PER_BYTE); |
317 | if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) { | |
318 | test_msg("Clearing straddling pages failed\n"); | |
319 | return -EINVAL; | |
320 | } | |
321 | ||
322 | /* | |
323 | * Generate a wonky pseudo-random bit pattern for the sake of not using | |
324 | * something repetitive that could miss some hypothetical off-by-n bug. | |
325 | */ | |
326 | x = 0; | |
327 | for (i = 0; i < len / sizeof(long); i++) { | |
328 | x = (0x19660dULL * (u64)x + 0x3c6ef35fULL) & 0xffffffffUL; | |
329 | bitmap[i] = x; | |
330 | } | |
331 | write_extent_buffer(eb, bitmap, 0, len); | |
332 | ||
333 | for (i = 0; i < len * BITS_PER_BYTE; i++) { | |
334 | int bit, bit1; | |
335 | ||
336 | bit = !!test_bit(i, bitmap); | |
337 | bit1 = !!extent_buffer_test_bit(eb, 0, i); | |
338 | if (bit1 != bit) { | |
339 | test_msg("Testing bit pattern failed\n"); | |
340 | return -EINVAL; | |
341 | } | |
342 | ||
343 | bit1 = !!extent_buffer_test_bit(eb, i / BITS_PER_BYTE, | |
344 | i % BITS_PER_BYTE); | |
345 | if (bit1 != bit) { | |
346 | test_msg("Testing bit pattern with offset failed\n"); | |
347 | return -EINVAL; | |
348 | } | |
349 | } | |
350 | ||
351 | return 0; | |
352 | } | |
353 | ||
354 | static int test_eb_bitmaps(void) | |
355 | { | |
09cbfeaf | 356 | unsigned long len = PAGE_SIZE * 4; |
0f331229 OS |
357 | unsigned long *bitmap; |
358 | struct extent_buffer *eb; | |
359 | int ret; | |
360 | ||
361 | test_msg("Running extent buffer bitmap tests\n"); | |
362 | ||
8cce83ba | 363 | bitmap = kmalloc(len, GFP_KERNEL); |
0f331229 OS |
364 | if (!bitmap) { |
365 | test_msg("Couldn't allocate test bitmap\n"); | |
366 | return -ENOMEM; | |
367 | } | |
368 | ||
369 | eb = __alloc_dummy_extent_buffer(NULL, 0, len); | |
370 | if (!eb) { | |
371 | test_msg("Couldn't allocate test extent buffer\n"); | |
372 | kfree(bitmap); | |
373 | return -ENOMEM; | |
374 | } | |
375 | ||
376 | ret = __test_eb_bitmaps(bitmap, eb, len); | |
377 | if (ret) | |
378 | goto out; | |
379 | ||
380 | /* Do it over again with an extent buffer which isn't page-aligned. */ | |
381 | free_extent_buffer(eb); | |
09cbfeaf | 382 | eb = __alloc_dummy_extent_buffer(NULL, PAGE_SIZE / 2, len); |
0f331229 OS |
383 | if (!eb) { |
384 | test_msg("Couldn't allocate test extent buffer\n"); | |
385 | kfree(bitmap); | |
386 | return -ENOMEM; | |
387 | } | |
388 | ||
389 | ret = __test_eb_bitmaps(bitmap, eb, len); | |
390 | out: | |
391 | free_extent_buffer(eb); | |
392 | kfree(bitmap); | |
393 | return ret; | |
394 | } | |
395 | ||
294e30fe JB |
396 | int btrfs_test_extent_io(void) |
397 | { | |
0f331229 OS |
398 | int ret; |
399 | ||
400 | test_msg("Running extent I/O tests\n"); | |
401 | ||
402 | ret = test_find_delalloc(); | |
403 | if (ret) | |
404 | goto out; | |
405 | ||
406 | ret = test_eb_bitmaps(); | |
407 | out: | |
408 | test_msg("Extent I/O tests finished\n"); | |
409 | return ret; | |
294e30fe | 410 | } |