diff options
Diffstat (limited to 'mm/truncate.c')
-rw-r--r-- | mm/truncate.c | 117 |
1 files changed, 81 insertions, 36 deletions
diff --git a/mm/truncate.c b/mm/truncate.c index c75b736e54b7..e2e8a8a7eb9d 100644 --- a/mm/truncate.c +++ b/mm/truncate.c | |||
@@ -26,7 +26,8 @@ | |||
26 | /** | 26 | /** |
27 | * do_invalidatepage - invalidate part or all of a page | 27 | * do_invalidatepage - invalidate part or all of a page |
28 | * @page: the page which is affected | 28 | * @page: the page which is affected |
29 | * @offset: the index of the truncation point | 29 | * @offset: start of the range to invalidate |
30 | * @length: length of the range to invalidate | ||
30 | * | 31 | * |
31 | * do_invalidatepage() is called when all or part of the page has become | 32 | * do_invalidatepage() is called when all or part of the page has become |
32 | * invalidated by a truncate operation. | 33 | * invalidated by a truncate operation. |
@@ -37,24 +38,18 @@ | |||
37 | * point. Because the caller is about to free (and possibly reuse) those | 38 | * point. Because the caller is about to free (and possibly reuse) those |
38 | * blocks on-disk. | 39 | * blocks on-disk. |
39 | */ | 40 | */ |
40 | void do_invalidatepage(struct page *page, unsigned long offset) | 41 | void do_invalidatepage(struct page *page, unsigned int offset, |
42 | unsigned int length) | ||
41 | { | 43 | { |
42 | void (*invalidatepage)(struct page *, unsigned long); | 44 | void (*invalidatepage)(struct page *, unsigned int, unsigned int); |
45 | |||
43 | invalidatepage = page->mapping->a_ops->invalidatepage; | 46 | invalidatepage = page->mapping->a_ops->invalidatepage; |
44 | #ifdef CONFIG_BLOCK | 47 | #ifdef CONFIG_BLOCK |
45 | if (!invalidatepage) | 48 | if (!invalidatepage) |
46 | invalidatepage = block_invalidatepage; | 49 | invalidatepage = block_invalidatepage; |
47 | #endif | 50 | #endif |
48 | if (invalidatepage) | 51 | if (invalidatepage) |
49 | (*invalidatepage)(page, offset); | 52 | (*invalidatepage)(page, offset, length); |
50 | } | ||
51 | |||
52 | static inline void truncate_partial_page(struct page *page, unsigned partial) | ||
53 | { | ||
54 | zero_user_segment(page, partial, PAGE_CACHE_SIZE); | ||
55 | cleancache_invalidate_page(page->mapping, page); | ||
56 | if (page_has_private(page)) | ||
57 | do_invalidatepage(page, partial); | ||
58 | } | 53 | } |
59 | 54 | ||
60 | /* | 55 | /* |
@@ -103,7 +98,7 @@ truncate_complete_page(struct address_space *mapping, struct page *page) | |||
103 | return -EIO; | 98 | return -EIO; |
104 | 99 | ||
105 | if (page_has_private(page)) | 100 | if (page_has_private(page)) |
106 | do_invalidatepage(page, 0); | 101 | do_invalidatepage(page, 0, PAGE_CACHE_SIZE); |
107 | 102 | ||
108 | cancel_dirty_page(page, PAGE_CACHE_SIZE); | 103 | cancel_dirty_page(page, PAGE_CACHE_SIZE); |
109 | 104 | ||
@@ -185,11 +180,11 @@ int invalidate_inode_page(struct page *page) | |||
185 | * truncate_inode_pages_range - truncate range of pages specified by start & end byte offsets | 180 | * truncate_inode_pages_range - truncate range of pages specified by start & end byte offsets |
186 | * @mapping: mapping to truncate | 181 | * @mapping: mapping to truncate |
187 | * @lstart: offset from which to truncate | 182 | * @lstart: offset from which to truncate |
188 | * @lend: offset to which to truncate | 183 | * @lend: offset to which to truncate (inclusive) |
189 | * | 184 | * |
190 | * Truncate the page cache, removing the pages that are between | 185 | * Truncate the page cache, removing the pages that are between |
191 | * specified offsets (and zeroing out partial page | 186 | * specified offsets (and zeroing out partial pages |
192 | * (if lstart is not page aligned)). | 187 | * if lstart or lend + 1 is not page aligned). |
193 | * | 188 | * |
194 | * Truncate takes two passes - the first pass is nonblocking. It will not | 189 | * Truncate takes two passes - the first pass is nonblocking. It will not |
195 | * block on page locks and it will not block on writeback. The second pass | 190 | * block on page locks and it will not block on writeback. The second pass |
@@ -200,35 +195,58 @@ int invalidate_inode_page(struct page *page) | |||
200 | * We pass down the cache-hot hint to the page freeing code. Even if the | 195 | * We pass down the cache-hot hint to the page freeing code. Even if the |
201 | * mapping is large, it is probably the case that the final pages are the most | 196 | * mapping is large, it is probably the case that the final pages are the most |
202 | * recently touched, and freeing happens in ascending file offset order. | 197 | * recently touched, and freeing happens in ascending file offset order. |
198 | * | ||
199 | * Note that since ->invalidatepage() accepts range to invalidate | ||
200 | * truncate_inode_pages_range is able to handle cases where lend + 1 is not | ||
201 | * page aligned properly. | ||
203 | */ | 202 | */ |
204 | void truncate_inode_pages_range(struct address_space *mapping, | 203 | void truncate_inode_pages_range(struct address_space *mapping, |
205 | loff_t lstart, loff_t lend) | 204 | loff_t lstart, loff_t lend) |
206 | { | 205 | { |
207 | const pgoff_t start = (lstart + PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT; | 206 | pgoff_t start; /* inclusive */ |
208 | const unsigned partial = lstart & (PAGE_CACHE_SIZE - 1); | 207 | pgoff_t end; /* exclusive */ |
209 | struct pagevec pvec; | 208 | unsigned int partial_start; /* inclusive */ |
210 | pgoff_t index; | 209 | unsigned int partial_end; /* exclusive */ |
211 | pgoff_t end; | 210 | struct pagevec pvec; |
212 | int i; | 211 | pgoff_t index; |
212 | int i; | ||
213 | 213 | ||
214 | cleancache_invalidate_inode(mapping); | 214 | cleancache_invalidate_inode(mapping); |
215 | if (mapping->nrpages == 0) | 215 | if (mapping->nrpages == 0) |
216 | return; | 216 | return; |
217 | 217 | ||
218 | BUG_ON((lend & (PAGE_CACHE_SIZE - 1)) != (PAGE_CACHE_SIZE - 1)); | 218 | /* Offsets within partial pages */ |
219 | end = (lend >> PAGE_CACHE_SHIFT); | 219 | partial_start = lstart & (PAGE_CACHE_SIZE - 1); |
220 | partial_end = (lend + 1) & (PAGE_CACHE_SIZE - 1); | ||
221 | |||
222 | /* | ||
223 | * 'start' and 'end' always covers the range of pages to be fully | ||
224 | * truncated. Partial pages are covered with 'partial_start' at the | ||
225 | * start of the range and 'partial_end' at the end of the range. | ||
226 | * Note that 'end' is exclusive while 'lend' is inclusive. | ||
227 | */ | ||
228 | start = (lstart + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | ||
229 | if (lend == -1) | ||
230 | /* | ||
231 | * lend == -1 indicates end-of-file so we have to set 'end' | ||
232 | * to the highest possible pgoff_t and since the type is | ||
233 | * unsigned we're using -1. | ||
234 | */ | ||
235 | end = -1; | ||
236 | else | ||
237 | end = (lend + 1) >> PAGE_CACHE_SHIFT; | ||
220 | 238 | ||
221 | pagevec_init(&pvec, 0); | 239 | pagevec_init(&pvec, 0); |
222 | index = start; | 240 | index = start; |
223 | while (index <= end && pagevec_lookup(&pvec, mapping, index, | 241 | while (index < end && pagevec_lookup(&pvec, mapping, index, |
224 | min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) { | 242 | min(end - index, (pgoff_t)PAGEVEC_SIZE))) { |
225 | mem_cgroup_uncharge_start(); | 243 | mem_cgroup_uncharge_start(); |
226 | for (i = 0; i < pagevec_count(&pvec); i++) { | 244 | for (i = 0; i < pagevec_count(&pvec); i++) { |
227 | struct page *page = pvec.pages[i]; | 245 | struct page *page = pvec.pages[i]; |
228 | 246 | ||
229 | /* We rely upon deletion not changing page->index */ | 247 | /* We rely upon deletion not changing page->index */ |
230 | index = page->index; | 248 | index = page->index; |
231 | if (index > end) | 249 | if (index >= end) |
232 | break; | 250 | break; |
233 | 251 | ||
234 | if (!trylock_page(page)) | 252 | if (!trylock_page(page)) |
@@ -247,27 +265,56 @@ void truncate_inode_pages_range(struct address_space *mapping, | |||
247 | index++; | 265 | index++; |
248 | } | 266 | } |
249 | 267 | ||
250 | if (partial) { | 268 | if (partial_start) { |
251 | struct page *page = find_lock_page(mapping, start - 1); | 269 | struct page *page = find_lock_page(mapping, start - 1); |
252 | if (page) { | 270 | if (page) { |
271 | unsigned int top = PAGE_CACHE_SIZE; | ||
272 | if (start > end) { | ||
273 | /* Truncation within a single page */ | ||
274 | top = partial_end; | ||
275 | partial_end = 0; | ||
276 | } | ||
253 | wait_on_page_writeback(page); | 277 | wait_on_page_writeback(page); |
254 | truncate_partial_page(page, partial); | 278 | zero_user_segment(page, partial_start, top); |
279 | cleancache_invalidate_page(mapping, page); | ||
280 | if (page_has_private(page)) | ||
281 | do_invalidatepage(page, partial_start, | ||
282 | top - partial_start); | ||
255 | unlock_page(page); | 283 | unlock_page(page); |
256 | page_cache_release(page); | 284 | page_cache_release(page); |
257 | } | 285 | } |
258 | } | 286 | } |
287 | if (partial_end) { | ||
288 | struct page *page = find_lock_page(mapping, end); | ||
289 | if (page) { | ||
290 | wait_on_page_writeback(page); | ||
291 | zero_user_segment(page, 0, partial_end); | ||
292 | cleancache_invalidate_page(mapping, page); | ||
293 | if (page_has_private(page)) | ||
294 | do_invalidatepage(page, 0, | ||
295 | partial_end); | ||
296 | unlock_page(page); | ||
297 | page_cache_release(page); | ||
298 | } | ||
299 | } | ||
300 | /* | ||
301 | * If the truncation happened within a single page no pages | ||
302 | * will be released, just zeroed, so we can bail out now. | ||
303 | */ | ||
304 | if (start >= end) | ||
305 | return; | ||
259 | 306 | ||
260 | index = start; | 307 | index = start; |
261 | for ( ; ; ) { | 308 | for ( ; ; ) { |
262 | cond_resched(); | 309 | cond_resched(); |
263 | if (!pagevec_lookup(&pvec, mapping, index, | 310 | if (!pagevec_lookup(&pvec, mapping, index, |
264 | min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) { | 311 | min(end - index, (pgoff_t)PAGEVEC_SIZE))) { |
265 | if (index == start) | 312 | if (index == start) |
266 | break; | 313 | break; |
267 | index = start; | 314 | index = start; |
268 | continue; | 315 | continue; |
269 | } | 316 | } |
270 | if (index == start && pvec.pages[0]->index > end) { | 317 | if (index == start && pvec.pages[0]->index >= end) { |
271 | pagevec_release(&pvec); | 318 | pagevec_release(&pvec); |
272 | break; | 319 | break; |
273 | } | 320 | } |
@@ -277,7 +324,7 @@ void truncate_inode_pages_range(struct address_space *mapping, | |||
277 | 324 | ||
278 | /* We rely upon deletion not changing page->index */ | 325 | /* We rely upon deletion not changing page->index */ |
279 | index = page->index; | 326 | index = page->index; |
280 | if (index > end) | 327 | if (index >= end) |
281 | break; | 328 | break; |
282 | 329 | ||
283 | lock_page(page); | 330 | lock_page(page); |
@@ -598,10 +645,8 @@ void truncate_pagecache_range(struct inode *inode, loff_t lstart, loff_t lend) | |||
598 | * This rounding is currently just for example: unmap_mapping_range | 645 | * This rounding is currently just for example: unmap_mapping_range |
599 | * expands its hole outwards, whereas we want it to contract the hole | 646 | * expands its hole outwards, whereas we want it to contract the hole |
600 | * inwards. However, existing callers of truncate_pagecache_range are | 647 | * inwards. However, existing callers of truncate_pagecache_range are |
601 | * doing their own page rounding first; and truncate_inode_pages_range | 648 | * doing their own page rounding first. Note that unmap_mapping_range |
602 | * currently BUGs if lend is not pagealigned-1 (it handles partial | 649 | * allows holelen 0 for all, and we allow lend -1 for end of file. |
603 | * page at start of hole, but not partial page at end of hole). Note | ||
604 | * unmap_mapping_range allows holelen 0 for all, and we allow lend -1. | ||
605 | */ | 650 | */ |
606 | 651 | ||
607 | /* | 652 | /* |