aboutsummaryrefslogtreecommitdiffstats
path: root/mm/shmem.c
diff options
context:
space:
mode:
authorJohannes Weiner <hannes@cmpxchg.org>2014-04-03 17:47:46 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-04-03 19:21:00 -0400
commit0cd6144aadd2afd19d1aca880153530c52957604 (patch)
tree529df1dc75d6a58eff057dde5feb07cecf6ba527 /mm/shmem.c
parente7b563bb2a6f4d974208da46200784b9c5b5a47e (diff)
mm + fs: prepare for non-page entries in page cache radix trees
shmem mappings already contain exceptional entries where swap slot information is remembered. To be able to store eviction information for regular page cache, prepare every site dealing with the radix trees directly to handle entries other than pages. The common lookup functions will filter out non-page entries and return NULL for page cache holes, just as before. But provide a raw version of the API which returns non-page entries as well, and switch shmem over to use it. Signed-off-by: Johannes Weiner <hannes@cmpxchg.org> Reviewed-by: Rik van Riel <riel@redhat.com> Reviewed-by: Minchan Kim <minchan@kernel.org> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: Bob Liu <bob.liu@oracle.com> Cc: Christoph Hellwig <hch@infradead.org> Cc: Dave Chinner <david@fromorbit.com> Cc: Greg Thelen <gthelen@google.com> Cc: Hugh Dickins <hughd@google.com> Cc: Jan Kara <jack@suse.cz> Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> Cc: Luigi Semenzato <semenzato@google.com> Cc: Mel Gorman <mgorman@suse.de> Cc: Metin Doslu <metin@citusdata.com> Cc: Michel Lespinasse <walken@google.com> Cc: Ozgun Erdogan <ozgun@citusdata.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Roman Gushchin <klamm@yandex-team.ru> Cc: Ryan Mallon <rmallon@gmail.com> Cc: Tejun Heo <tj@kernel.org> Cc: Vlastimil Babka <vbabka@suse.cz> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/shmem.c')
-rw-r--r--mm/shmem.c99
1 files changed, 20 insertions, 79 deletions
diff --git a/mm/shmem.c b/mm/shmem.c
index e470997010cd..a3ba988ec946 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -329,56 +329,6 @@ static void shmem_delete_from_page_cache(struct page *page, void *radswap)
329} 329}
330 330
331/* 331/*
332 * Like find_get_pages, but collecting swap entries as well as pages.
333 */
334static unsigned shmem_find_get_pages_and_swap(struct address_space *mapping,
335 pgoff_t start, unsigned int nr_pages,
336 struct page **pages, pgoff_t *indices)
337{
338 void **slot;
339 unsigned int ret = 0;
340 struct radix_tree_iter iter;
341
342 if (!nr_pages)
343 return 0;
344
345 rcu_read_lock();
346restart:
347 radix_tree_for_each_slot(slot, &mapping->page_tree, &iter, start) {
348 struct page *page;
349repeat:
350 page = radix_tree_deref_slot(slot);
351 if (unlikely(!page))
352 continue;
353 if (radix_tree_exception(page)) {
354 if (radix_tree_deref_retry(page))
355 goto restart;
356 /*
357 * Otherwise, we must be storing a swap entry
358 * here as an exceptional entry: so return it
359 * without attempting to raise page count.
360 */
361 goto export;
362 }
363 if (!page_cache_get_speculative(page))
364 goto repeat;
365
366 /* Has the page moved? */
367 if (unlikely(page != *slot)) {
368 page_cache_release(page);
369 goto repeat;
370 }
371export:
372 indices[ret] = iter.index;
373 pages[ret] = page;
374 if (++ret == nr_pages)
375 break;
376 }
377 rcu_read_unlock();
378 return ret;
379}
380
381/*
382 * Remove swap entry from radix tree, free the swap and its page cache. 332 * Remove swap entry from radix tree, free the swap and its page cache.
383 */ 333 */
384static int shmem_free_swap(struct address_space *mapping, 334static int shmem_free_swap(struct address_space *mapping,
@@ -396,21 +346,6 @@ static int shmem_free_swap(struct address_space *mapping,
396} 346}
397 347
398/* 348/*
399 * Pagevec may contain swap entries, so shuffle up pages before releasing.
400 */
401static void shmem_deswap_pagevec(struct pagevec *pvec)
402{
403 int i, j;
404
405 for (i = 0, j = 0; i < pagevec_count(pvec); i++) {
406 struct page *page = pvec->pages[i];
407 if (!radix_tree_exceptional_entry(page))
408 pvec->pages[j++] = page;
409 }
410 pvec->nr = j;
411}
412
413/*
414 * SysV IPC SHM_UNLOCK restore Unevictable pages to their evictable lists. 349 * SysV IPC SHM_UNLOCK restore Unevictable pages to their evictable lists.
415 */ 350 */
416void shmem_unlock_mapping(struct address_space *mapping) 351void shmem_unlock_mapping(struct address_space *mapping)
@@ -428,12 +363,12 @@ void shmem_unlock_mapping(struct address_space *mapping)
428 * Avoid pagevec_lookup(): find_get_pages() returns 0 as if it 363 * Avoid pagevec_lookup(): find_get_pages() returns 0 as if it
429 * has finished, if it hits a row of PAGEVEC_SIZE swap entries. 364 * has finished, if it hits a row of PAGEVEC_SIZE swap entries.
430 */ 365 */
431 pvec.nr = shmem_find_get_pages_and_swap(mapping, index, 366 pvec.nr = find_get_entries(mapping, index,
432 PAGEVEC_SIZE, pvec.pages, indices); 367 PAGEVEC_SIZE, pvec.pages, indices);
433 if (!pvec.nr) 368 if (!pvec.nr)
434 break; 369 break;
435 index = indices[pvec.nr - 1] + 1; 370 index = indices[pvec.nr - 1] + 1;
436 shmem_deswap_pagevec(&pvec); 371 pagevec_remove_exceptionals(&pvec);
437 check_move_unevictable_pages(pvec.pages, pvec.nr); 372 check_move_unevictable_pages(pvec.pages, pvec.nr);
438 pagevec_release(&pvec); 373 pagevec_release(&pvec);
439 cond_resched(); 374 cond_resched();
@@ -465,9 +400,9 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
465 pagevec_init(&pvec, 0); 400 pagevec_init(&pvec, 0);
466 index = start; 401 index = start;
467 while (index < end) { 402 while (index < end) {
468 pvec.nr = shmem_find_get_pages_and_swap(mapping, index, 403 pvec.nr = find_get_entries(mapping, index,
469 min(end - index, (pgoff_t)PAGEVEC_SIZE), 404 min(end - index, (pgoff_t)PAGEVEC_SIZE),
470 pvec.pages, indices); 405 pvec.pages, indices);
471 if (!pvec.nr) 406 if (!pvec.nr)
472 break; 407 break;
473 mem_cgroup_uncharge_start(); 408 mem_cgroup_uncharge_start();
@@ -496,7 +431,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
496 } 431 }
497 unlock_page(page); 432 unlock_page(page);
498 } 433 }
499 shmem_deswap_pagevec(&pvec); 434 pagevec_remove_exceptionals(&pvec);
500 pagevec_release(&pvec); 435 pagevec_release(&pvec);
501 mem_cgroup_uncharge_end(); 436 mem_cgroup_uncharge_end();
502 cond_resched(); 437 cond_resched();
@@ -534,9 +469,10 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
534 index = start; 469 index = start;
535 for ( ; ; ) { 470 for ( ; ; ) {
536 cond_resched(); 471 cond_resched();
537 pvec.nr = shmem_find_get_pages_and_swap(mapping, index, 472
473 pvec.nr = find_get_entries(mapping, index,
538 min(end - index, (pgoff_t)PAGEVEC_SIZE), 474 min(end - index, (pgoff_t)PAGEVEC_SIZE),
539 pvec.pages, indices); 475 pvec.pages, indices);
540 if (!pvec.nr) { 476 if (!pvec.nr) {
541 if (index == start || unfalloc) 477 if (index == start || unfalloc)
542 break; 478 break;
@@ -544,7 +480,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
544 continue; 480 continue;
545 } 481 }
546 if ((index == start || unfalloc) && indices[0] >= end) { 482 if ((index == start || unfalloc) && indices[0] >= end) {
547 shmem_deswap_pagevec(&pvec); 483 pagevec_remove_exceptionals(&pvec);
548 pagevec_release(&pvec); 484 pagevec_release(&pvec);
549 break; 485 break;
550 } 486 }
@@ -573,7 +509,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
573 } 509 }
574 unlock_page(page); 510 unlock_page(page);
575 } 511 }
576 shmem_deswap_pagevec(&pvec); 512 pagevec_remove_exceptionals(&pvec);
577 pagevec_release(&pvec); 513 pagevec_release(&pvec);
578 mem_cgroup_uncharge_end(); 514 mem_cgroup_uncharge_end();
579 index++; 515 index++;
@@ -1079,7 +1015,7 @@ static int shmem_getpage_gfp(struct inode *inode, pgoff_t index,
1079 return -EFBIG; 1015 return -EFBIG;
1080repeat: 1016repeat:
1081 swap.val = 0; 1017 swap.val = 0;
1082 page = find_lock_page(mapping, index); 1018 page = find_lock_entry(mapping, index);
1083 if (radix_tree_exceptional_entry(page)) { 1019 if (radix_tree_exceptional_entry(page)) {
1084 swap = radix_to_swp_entry(page); 1020 swap = radix_to_swp_entry(page);
1085 page = NULL; 1021 page = NULL;
@@ -1416,6 +1352,11 @@ static struct inode *shmem_get_inode(struct super_block *sb, const struct inode
1416 return inode; 1352 return inode;
1417} 1353}
1418 1354
1355bool shmem_mapping(struct address_space *mapping)
1356{
1357 return mapping->backing_dev_info == &shmem_backing_dev_info;
1358}
1359
1419#ifdef CONFIG_TMPFS 1360#ifdef CONFIG_TMPFS
1420static const struct inode_operations shmem_symlink_inode_operations; 1361static const struct inode_operations shmem_symlink_inode_operations;
1421static const struct inode_operations shmem_short_symlink_operations; 1362static const struct inode_operations shmem_short_symlink_operations;
@@ -1728,7 +1669,7 @@ static pgoff_t shmem_seek_hole_data(struct address_space *mapping,
1728 pagevec_init(&pvec, 0); 1669 pagevec_init(&pvec, 0);
1729 pvec.nr = 1; /* start small: we may be there already */ 1670 pvec.nr = 1; /* start small: we may be there already */
1730 while (!done) { 1671 while (!done) {
1731 pvec.nr = shmem_find_get_pages_and_swap(mapping, index, 1672 pvec.nr = find_get_entries(mapping, index,
1732 pvec.nr, pvec.pages, indices); 1673 pvec.nr, pvec.pages, indices);
1733 if (!pvec.nr) { 1674 if (!pvec.nr) {
1734 if (whence == SEEK_DATA) 1675 if (whence == SEEK_DATA)
@@ -1755,7 +1696,7 @@ static pgoff_t shmem_seek_hole_data(struct address_space *mapping,
1755 break; 1696 break;
1756 } 1697 }
1757 } 1698 }
1758 shmem_deswap_pagevec(&pvec); 1699 pagevec_remove_exceptionals(&pvec);
1759 pagevec_release(&pvec); 1700 pagevec_release(&pvec);
1760 pvec.nr = PAGEVEC_SIZE; 1701 pvec.nr = PAGEVEC_SIZE;
1761 cond_resched(); 1702 cond_resched();