diff options
-rw-r--r-- | include/linux/mm.h | 2 | ||||
-rw-r--r-- | mm/truncate.c | 44 |
2 files changed, 39 insertions, 7 deletions
diff --git a/include/linux/mm.h b/include/linux/mm.h index a06a84d347fb..92acae9f1f4c 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h | |||
@@ -896,6 +896,8 @@ extern unsigned long do_brk(unsigned long, unsigned long); | |||
896 | /* filemap.c */ | 896 | /* filemap.c */ |
897 | extern unsigned long page_unuse(struct page *); | 897 | extern unsigned long page_unuse(struct page *); |
898 | extern void truncate_inode_pages(struct address_space *, loff_t); | 898 | extern void truncate_inode_pages(struct address_space *, loff_t); |
899 | extern void truncate_inode_pages_range(struct address_space *, | ||
900 | loff_t lstart, loff_t lend); | ||
899 | 901 | ||
900 | /* generic vm_area_ops exported for stackable file systems */ | 902 | /* generic vm_area_ops exported for stackable file systems */ |
901 | extern struct page *filemap_nopage(struct vm_area_struct *, unsigned long, int *); | 903 | extern struct page *filemap_nopage(struct vm_area_struct *, unsigned long, int *); |
diff --git a/mm/truncate.c b/mm/truncate.c index 9173ab500604..7dee32745901 100644 --- a/mm/truncate.c +++ b/mm/truncate.c | |||
@@ -82,12 +82,15 @@ invalidate_complete_page(struct address_space *mapping, struct page *page) | |||
82 | } | 82 | } |
83 | 83 | ||
84 | /** | 84 | /** |
85 | * truncate_inode_pages - truncate *all* the pages from an offset | 85 | * truncate_inode_pages - truncate range of pages specified by start and |
86 | * end byte offsets | ||
86 | * @mapping: mapping to truncate | 87 | * @mapping: mapping to truncate |
87 | * @lstart: offset from which to truncate | 88 | * @lstart: offset from which to truncate |
89 | * @lend: offset to which to truncate | ||
88 | * | 90 | * |
89 | * Truncate the page cache at a set offset, removing the pages that are beyond | 91 | * Truncate the page cache, removing the pages that are between |
90 | * that offset (and zeroing out partial pages). | 92 | * specified offsets (and zeroing out partial page |
93 | * (if lstart is not page aligned)). | ||
91 | * | 94 | * |
92 | * Truncate takes two passes - the first pass is nonblocking. It will not | 95 | * Truncate takes two passes - the first pass is nonblocking. It will not |
93 | * block on page locks and it will not block on writeback. The second pass | 96 | * block on page locks and it will not block on writeback. The second pass |
@@ -101,12 +104,12 @@ invalidate_complete_page(struct address_space *mapping, struct page *page) | |||
101 | * We pass down the cache-hot hint to the page freeing code. Even if the | 104 | * We pass down the cache-hot hint to the page freeing code. Even if the |
102 | * mapping is large, it is probably the case that the final pages are the most | 105 | * mapping is large, it is probably the case that the final pages are the most |
103 | * recently touched, and freeing happens in ascending file offset order. | 106 | * recently touched, and freeing happens in ascending file offset order. |
104 | * | ||
105 | * Called under (and serialised by) inode->i_sem. | ||
106 | */ | 107 | */ |
107 | void truncate_inode_pages(struct address_space *mapping, loff_t lstart) | 108 | void truncate_inode_pages_range(struct address_space *mapping, |
109 | loff_t lstart, loff_t lend) | ||
108 | { | 110 | { |
109 | const pgoff_t start = (lstart + PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT; | 111 | const pgoff_t start = (lstart + PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT; |
112 | pgoff_t end; | ||
110 | const unsigned partial = lstart & (PAGE_CACHE_SIZE - 1); | 113 | const unsigned partial = lstart & (PAGE_CACHE_SIZE - 1); |
111 | struct pagevec pvec; | 114 | struct pagevec pvec; |
112 | pgoff_t next; | 115 | pgoff_t next; |
@@ -115,13 +118,22 @@ void truncate_inode_pages(struct address_space *mapping, loff_t lstart) | |||
115 | if (mapping->nrpages == 0) | 118 | if (mapping->nrpages == 0) |
116 | return; | 119 | return; |
117 | 120 | ||
121 | BUG_ON((lend & (PAGE_CACHE_SIZE - 1)) != (PAGE_CACHE_SIZE - 1)); | ||
122 | end = (lend >> PAGE_CACHE_SHIFT); | ||
123 | |||
118 | pagevec_init(&pvec, 0); | 124 | pagevec_init(&pvec, 0); |
119 | next = start; | 125 | next = start; |
120 | while (pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) { | 126 | while (next <= end && |
127 | pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) { | ||
121 | for (i = 0; i < pagevec_count(&pvec); i++) { | 128 | for (i = 0; i < pagevec_count(&pvec); i++) { |
122 | struct page *page = pvec.pages[i]; | 129 | struct page *page = pvec.pages[i]; |
123 | pgoff_t page_index = page->index; | 130 | pgoff_t page_index = page->index; |
124 | 131 | ||
132 | if (page_index > end) { | ||
133 | next = page_index; | ||
134 | break; | ||
135 | } | ||
136 | |||
125 | if (page_index > next) | 137 | if (page_index > next) |
126 | next = page_index; | 138 | next = page_index; |
127 | next++; | 139 | next++; |
@@ -157,9 +169,15 @@ void truncate_inode_pages(struct address_space *mapping, loff_t lstart) | |||
157 | next = start; | 169 | next = start; |
158 | continue; | 170 | continue; |
159 | } | 171 | } |
172 | if (pvec.pages[0]->index > end) { | ||
173 | pagevec_release(&pvec); | ||
174 | break; | ||
175 | } | ||
160 | for (i = 0; i < pagevec_count(&pvec); i++) { | 176 | for (i = 0; i < pagevec_count(&pvec); i++) { |
161 | struct page *page = pvec.pages[i]; | 177 | struct page *page = pvec.pages[i]; |
162 | 178 | ||
179 | if (page->index > end) | ||
180 | break; | ||
163 | lock_page(page); | 181 | lock_page(page); |
164 | wait_on_page_writeback(page); | 182 | wait_on_page_writeback(page); |
165 | if (page->index > next) | 183 | if (page->index > next) |
@@ -171,7 +189,19 @@ void truncate_inode_pages(struct address_space *mapping, loff_t lstart) | |||
171 | pagevec_release(&pvec); | 189 | pagevec_release(&pvec); |
172 | } | 190 | } |
173 | } | 191 | } |
192 | EXPORT_SYMBOL(truncate_inode_pages_range); | ||
174 | 193 | ||
194 | /** | ||
195 | * truncate_inode_pages - truncate *all* the pages from an offset | ||
196 | * @mapping: mapping to truncate | ||
197 | * @lstart: offset from which to truncate | ||
198 | * | ||
199 | * Called under (and serialised by) inode->i_sem. | ||
200 | */ | ||
201 | void truncate_inode_pages(struct address_space *mapping, loff_t lstart) | ||
202 | { | ||
203 | truncate_inode_pages_range(mapping, lstart, (loff_t)-1); | ||
204 | } | ||
175 | EXPORT_SYMBOL(truncate_inode_pages); | 205 | EXPORT_SYMBOL(truncate_inode_pages); |
176 | 206 | ||
177 | /** | 207 | /** |