xref: /openbmc/linux/fs/btrfs/tests/extent-io-tests.c (revision 0ee4c2ac)
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 
28 static 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 
66 static 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;
260 out_bits:
261 	clear_extent_bits(&tmp, 0, total_dirty - 1,
262 			  (unsigned long)-1, GFP_NOFS);
263 out:
264 	if (locked_page)
265 		page_cache_release(locked_page);
266 	process_page_range(inode, 0, total_dirty - 1,
267 			   PROCESS_UNLOCK | PROCESS_RELEASE);
268 	iput(inode);
269 	return ret;
270 }
271 
272 int btrfs_test_extent_io(void)
273 {
274 	test_msg("Running find delalloc tests\n");
275 	return test_find_delalloc();
276 }
277