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> 21#include "btrfs-tests.h" 22#include "../extent_io.h" 23 24#define PROCESS_UNLOCK (1 << 0) 25#define PROCESS_RELEASE (1 << 1) 26#define PROCESS_TEST_LOCKED (1 << 2) 27 28static noinline int process_page_range(struct inode *inode, u64 start, u64 end, 29 unsigned long flags) 30{ 31 int ret; 32 struct page *pages[16]; 33 unsigned long index = start >> PAGE_CACHE_SHIFT; 34 unsigned long end_index = end >> PAGE_CACHE_SHIFT; 35 unsigned long nr_pages = end_index - index + 1; 36 int i; 37 int count = 0; 38 int loops = 0; 39 40 while (nr_pages > 0) { 41 ret = find_get_pages_contig(inode->i_mapping, index, 42 min_t(unsigned long, nr_pages, 43 ARRAY_SIZE(pages)), pages); 44 for (i = 0; i < ret; i++) { 45 if (flags & PROCESS_TEST_LOCKED && 46 !PageLocked(pages[i])) 47 count++; 48 if (flags & PROCESS_UNLOCK && PageLocked(pages[i])) 49 unlock_page(pages[i]); 50 page_cache_release(pages[i]); 51 if (flags & PROCESS_RELEASE) 52 page_cache_release(pages[i]); 53 } 54 nr_pages -= ret; 55 index += ret; 56 cond_resched(); 57 loops++; 58 if (loops > 100000) { 59 printk(KERN_ERR "stuck in a loop, start %Lu, end %Lu, nr_pages %lu, ret %d\n", start, end, nr_pages, ret); 60 break; 61 } 62 } 63 return count; 64} 65 66static int test_find_delalloc(void) 67{ 68 struct inode *inode; 69 struct extent_io_tree tmp; 70 struct page *page; 71 struct page *locked_page = NULL; 72 unsigned long index = 0; 73 u64 total_dirty = 256 * 1024 * 1024; 74 u64 max_bytes = 128 * 1024 * 1024; 75 u64 start, end, test_start; 76 u64 found; 77 int ret = -EINVAL; 78 79 inode = btrfs_new_test_inode(); 80 if (!inode) { 81 test_msg("Failed to allocate test inode\n"); 82 return -ENOMEM; 83 } 84 85 extent_io_tree_init(&tmp, &inode->i_data); 86 87 /* 88 * First go through and create and mark all of our pages dirty, we pin 89 * everything to make sure our pages don't get evicted and screw up our 90 * test. 91 */ 92 for (index = 0; index < (total_dirty >> PAGE_CACHE_SHIFT); index++) { 93 page = find_or_create_page(inode->i_mapping, index, GFP_NOFS); 94 if (!page) { 95 test_msg("Failed to allocate test page\n"); 96 ret = -ENOMEM; 97 goto out; 98 } 99 SetPageDirty(page); 100 if (index) { 101 unlock_page(page); 102 } else { 103 page_cache_get(page); 104 locked_page = page; 105 } 106 } 107 108 /* Test this scenario 109 * |--- delalloc ---| 110 * |--- search ---| 111 */ 112 set_extent_delalloc(&tmp, 0, 4095, NULL, GFP_NOFS); 113 start = 0; 114 end = 0; 115 found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, 116 &end, max_bytes); 117 if (!found) { 118 test_msg("Should have found at least one delalloc\n"); 119 goto out_bits; 120 } 121 if (start != 0 || end != 4095) { 122 test_msg("Expected start 0 end 4095, got start %Lu end %Lu\n", 123 start, end); 124 goto out_bits; 125 } 126 unlock_extent(&tmp, start, end); 127 unlock_page(locked_page); 128 page_cache_release(locked_page); 129 130 /* 131 * Test this scenario 132 * 133 * |--- delalloc ---| 134 * |--- search ---| 135 */ 136 test_start = 64 * 1024 * 1024; 137 locked_page = find_lock_page(inode->i_mapping, 138 test_start >> PAGE_CACHE_SHIFT); 139 if (!locked_page) { 140 test_msg("Couldn't find the locked page\n"); 141 goto out_bits; 142 } 143 set_extent_delalloc(&tmp, 4096, max_bytes - 1, NULL, GFP_NOFS); 144 start = test_start; 145 end = 0; 146 found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, 147 &end, max_bytes); 148 if (!found) { 149 test_msg("Couldn't find delalloc in our range\n"); 150 goto out_bits; 151 } 152 if (start != test_start || end != max_bytes - 1) { 153 test_msg("Expected start %Lu end %Lu, got start %Lu, end " 154 "%Lu\n", test_start, max_bytes - 1, start, end); 155 goto out_bits; 156 } 157 if (process_page_range(inode, start, end, 158 PROCESS_TEST_LOCKED | PROCESS_UNLOCK)) { 159 test_msg("There were unlocked pages in the range\n"); 160 goto out_bits; 161 } 162 unlock_extent(&tmp, start, end); 163 /* locked_page was unlocked above */ 164 page_cache_release(locked_page); 165 166 /* 167 * Test this scenario 168 * |--- delalloc ---| 169 * |--- search ---| 170 */ 171 test_start = max_bytes + 4096; 172 locked_page = find_lock_page(inode->i_mapping, test_start >> 173 PAGE_CACHE_SHIFT); 174 if (!locked_page) { 175 test_msg("Could'nt find the locked page\n"); 176 goto out_bits; 177 } 178 start = test_start; 179 end = 0; 180 found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, 181 &end, max_bytes); 182 if (found) { 183 test_msg("Found range when we shouldn't have\n"); 184 goto out_bits; 185 } 186 if (end != (u64)-1) { 187 test_msg("Did not return the proper end offset\n"); 188 goto out_bits; 189 } 190 191 /* 192 * Test this scenario 193 * [------- delalloc -------| 194 * [max_bytes]|-- search--| 195 * 196 * We are re-using our test_start from above since it works out well. 197 */ 198 set_extent_delalloc(&tmp, max_bytes, total_dirty - 1, NULL, GFP_NOFS); 199 start = test_start; 200 end = 0; 201 found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, 202 &end, max_bytes); 203 if (!found) { 204 test_msg("Didn't find our range\n"); 205 goto out_bits; 206 } 207 if (start != test_start || end != total_dirty - 1) { 208 test_msg("Expected start %Lu end %Lu, got start %Lu end %Lu\n", 209 test_start, total_dirty - 1, start, end); 210 goto out_bits; 211 } 212 if (process_page_range(inode, start, end, 213 PROCESS_TEST_LOCKED | PROCESS_UNLOCK)) { 214 test_msg("Pages in range were not all locked\n"); 215 goto out_bits; 216 } 217 unlock_extent(&tmp, start, end); 218 219 /* 220 * Now to test where we run into a page that is no longer dirty in the 221 * range we want to find. 222 */ 223 page = find_get_page(inode->i_mapping, (max_bytes + (1 * 1024 * 1024)) 224 >> PAGE_CACHE_SHIFT); 225 if (!page) { 226 test_msg("Couldn't find our page\n"); 227 goto out_bits; 228 } 229 ClearPageDirty(page); 230 page_cache_release(page); 231 232 /* We unlocked it in the previous test */ 233 lock_page(locked_page); 234 start = test_start; 235 end = 0; 236 /* 237 * Currently if we fail to find dirty pages in the delalloc range we 238 * will adjust max_bytes down to PAGE_CACHE_SIZE and then re-search. If 239 * this changes at any point in the future we will need to fix this 240 * tests expected behavior. 241 */ 242 found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, 243 &end, max_bytes); 244 if (!found) { 245 test_msg("Didn't find our range\n"); 246 goto out_bits; 247 } 248 if (start != test_start && end != test_start + PAGE_CACHE_SIZE - 1) { 249 test_msg("Expected start %Lu end %Lu, got start %Lu end %Lu\n", 250 test_start, test_start + PAGE_CACHE_SIZE - 1, start, 251 end); 252 goto out_bits; 253 } 254 if (process_page_range(inode, start, end, PROCESS_TEST_LOCKED | 255 PROCESS_UNLOCK)) { 256 test_msg("Pages in range were not all locked\n"); 257 goto out_bits; 258 } 259 ret = 0; 260out_bits: 261 clear_extent_bits(&tmp, 0, total_dirty - 1, (unsigned)-1, GFP_NOFS); 262out: 263 if (locked_page) 264 page_cache_release(locked_page); 265 process_page_range(inode, 0, total_dirty - 1, 266 PROCESS_UNLOCK | PROCESS_RELEASE); 267 iput(inode); 268 return ret; 269} 270 271int btrfs_test_extent_io(void) 272{ 273 test_msg("Running find delalloc tests\n"); 274 return test_find_delalloc(); 275} 276