summaryrefslogtreecommitdiffstats
path: root/mm/filemap.c
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2017-11-15 20:37:33 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2017-11-15 21:21:06 -0500
commitaa65c29ce1b6e1990cd2c7d8004bbea7ff3aff38 (patch)
treef3a3fe2b5a6430f04e721c4c35f9209fa6482e32 /mm/filemap.c
parent5ecc4d852c03b82646bf563460091b95f6a8c7c0 (diff)
mm: batch radix tree operations when truncating pages
Currently we remove pages from the radix tree one by one. To speed up page cache truncation, lock several pages at once and free them in one go. This allows us to batch radix tree operations in a more efficient way and also save round-trips on mapping->tree_lock. As a result we gain about 20% speed improvement in page cache truncation. Data from a simple benchmark timing 10000 truncates of 1024 pages (on ext4 on ramdisk but the filesystem is barely visible in the profiles). The range shows 1% and 95% percentiles of the measured times: 4.14-rc2 4.14-rc2 + batched truncation 248-256 209-219 249-258 209-217 248-255 211-239 248-255 209-217 247-256 210-218 [jack@suse.cz: convert delete_from_page_cache_batch() to pagevec] Link: http://lkml.kernel.org/r/20171018111648.13714-1-jack@suse.cz [akpm@linux-foundation.org: move struct pagevec forward declaration to top-of-file] Link: http://lkml.kernel.org/r/20171010151937.26984-8-jack@suse.cz Signed-off-by: Jan Kara <jack@suse.cz> Acked-by: Mel Gorman <mgorman@suse.de> Reviewed-by: Andi Kleen <ak@linux.intel.com> Cc: Dave Chinner <david@fromorbit.com> Cc: Dave Hansen <dave.hansen@intel.com> Cc: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/filemap.c')
-rw-r--r--mm/filemap.c83
1 files changed, 83 insertions, 0 deletions
diff --git a/mm/filemap.c b/mm/filemap.c
index a11b42189436..a470dd8cd05b 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -304,6 +304,89 @@ void delete_from_page_cache(struct page *page)
304} 304}
305EXPORT_SYMBOL(delete_from_page_cache); 305EXPORT_SYMBOL(delete_from_page_cache);
306 306
307/*
308 * page_cache_tree_delete_batch - delete several pages from page cache
309 * @mapping: the mapping to which pages belong
310 * @pvec: pagevec with pages to delete
311 *
312 * The function walks over mapping->page_tree and removes pages passed in @pvec
313 * from the radix tree. The function expects @pvec to be sorted by page index.
314 * It tolerates holes in @pvec (radix tree entries at those indices are not
315 * modified). The function expects only THP head pages to be present in the
316 * @pvec and takes care to delete all corresponding tail pages from the radix
317 * tree as well.
318 *
319 * The function expects mapping->tree_lock to be held.
320 */
321static void
322page_cache_tree_delete_batch(struct address_space *mapping,
323 struct pagevec *pvec)
324{
325 struct radix_tree_iter iter;
326 void **slot;
327 int total_pages = 0;
328 int i = 0, tail_pages = 0;
329 struct page *page;
330 pgoff_t start;
331
332 start = pvec->pages[0]->index;
333 radix_tree_for_each_slot(slot, &mapping->page_tree, &iter, start) {
334 if (i >= pagevec_count(pvec) && !tail_pages)
335 break;
336 page = radix_tree_deref_slot_protected(slot,
337 &mapping->tree_lock);
338 if (radix_tree_exceptional_entry(page))
339 continue;
340 if (!tail_pages) {
341 /*
342 * Some page got inserted in our range? Skip it. We
343 * have our pages locked so they are protected from
344 * being removed.
345 */
346 if (page != pvec->pages[i])
347 continue;
348 WARN_ON_ONCE(!PageLocked(page));
349 if (PageTransHuge(page) && !PageHuge(page))
350 tail_pages = HPAGE_PMD_NR - 1;
351 page->mapping = NULL;
352 /*
353 * Leave page->index set: truncation lookup relies
354 * upon it
355 */
356 i++;
357 } else {
358 tail_pages--;
359 }
360 radix_tree_clear_tags(&mapping->page_tree, iter.node, slot);
361 __radix_tree_replace(&mapping->page_tree, iter.node, slot, NULL,
362 workingset_update_node, mapping);
363 total_pages++;
364 }
365 mapping->nrpages -= total_pages;
366}
367
368void delete_from_page_cache_batch(struct address_space *mapping,
369 struct pagevec *pvec)
370{
371 int i;
372 unsigned long flags;
373
374 if (!pagevec_count(pvec))
375 return;
376
377 spin_lock_irqsave(&mapping->tree_lock, flags);
378 for (i = 0; i < pagevec_count(pvec); i++) {
379 trace_mm_filemap_delete_from_page_cache(pvec->pages[i]);
380
381 unaccount_page_cache_page(mapping, pvec->pages[i]);
382 }
383 page_cache_tree_delete_batch(mapping, pvec);
384 spin_unlock_irqrestore(&mapping->tree_lock, flags);
385
386 for (i = 0; i < pagevec_count(pvec); i++)
387 page_cache_free_page(mapping, pvec->pages[i]);
388}
389
307int filemap_check_errors(struct address_space *mapping) 390int filemap_check_errors(struct address_space *mapping)
308{ 391{
309 int ret = 0; 392 int ret = 0;