aboutsummaryrefslogtreecommitdiffstats
path: root/mm/shmem.c
diff options
context:
space:
mode:
authorHugh Dickins <hughd@google.com>2011-08-03 19:21:25 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-08-03 20:25:24 -0400
commit6922c0c7abd387374255801f7739624867e8acad (patch)
tree851ec456dbca49ee78c302fd62839e9889a75290 /mm/shmem.c
parentaa3b189551ad8e5cc1d9c663735c131650238278 (diff)
tmpfs: convert shmem_writepage and enable swap
Convert shmem_writepage() to use shmem_delete_from_page_cache() to use shmem_radix_tree_replace() to substitute swap entry for page pointer atomically in the radix tree. As with shmem_add_to_page_cache(), it's not entirely satisfactory to be copying such code from delete_from_swap_cache, but again judged easier to sell than making its other callers go through the extras. Remove the toy implementation's shmem_put_swap() and shmem_get_swap(), now unreferenced, and the hack to disable swap: it's now good to go. The way things have worked out, info->lock no longer helps to guard the shmem_swaplist: we increment swapped under shmem_swaplist_mutex only. That global mutex exclusion between shmem_writepage() and shmem_unuse() is not pretty, and we ought to find another way; but it's been forced on us by recent race discoveries, not a consequence of this patchset. And what has become of the WARN_ON_ONCE(1) free_swap_and_cache() if a swap entry was found already present? That's no longer possible, the (unknown) one inserting this page into filecache would hit the swap entry occupying that slot. Signed-off-by: Hugh Dickins <hughd@google.com> Acked-by: Rik van Riel <riel@redhat.com> 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.c88
1 files changed, 37 insertions, 51 deletions
diff --git a/mm/shmem.c b/mm/shmem.c
index 13ef2d7e912d..0f094a258526 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -6,7 +6,8 @@
6 * 2000-2001 Christoph Rohland 6 * 2000-2001 Christoph Rohland
7 * 2000-2001 SAP AG 7 * 2000-2001 SAP AG
8 * 2002 Red Hat Inc. 8 * 2002 Red Hat Inc.
9 * Copyright (C) 2002-2005 Hugh Dickins. 9 * Copyright (C) 2002-2011 Hugh Dickins.
10 * Copyright (C) 2011 Google Inc.
10 * Copyright (C) 2002-2005 VERITAS Software Corporation. 11 * Copyright (C) 2002-2005 VERITAS Software Corporation.
11 * Copyright (C) 2004 Andi Kleen, SuSE Labs 12 * Copyright (C) 2004 Andi Kleen, SuSE Labs
12 * 13 *
@@ -219,19 +220,6 @@ static void shmem_recalc_inode(struct inode *inode)
219 } 220 }
220} 221}
221 222
222static void shmem_put_swap(struct shmem_inode_info *info, pgoff_t index,
223 swp_entry_t swap)
224{
225 if (index < SHMEM_NR_DIRECT)
226 info->i_direct[index] = swap;
227}
228
229static swp_entry_t shmem_get_swap(struct shmem_inode_info *info, pgoff_t index)
230{
231 return (index < SHMEM_NR_DIRECT) ?
232 info->i_direct[index] : (swp_entry_t){0};
233}
234
235/* 223/*
236 * Replace item expected in radix tree by a new item, while holding tree lock. 224 * Replace item expected in radix tree by a new item, while holding tree lock.
237 */ 225 */
@@ -300,6 +288,25 @@ static int shmem_add_to_page_cache(struct page *page,
300} 288}
301 289
302/* 290/*
291 * Like delete_from_page_cache, but substitutes swap for page.
292 */
293static void shmem_delete_from_page_cache(struct page *page, void *radswap)
294{
295 struct address_space *mapping = page->mapping;
296 int error;
297
298 spin_lock_irq(&mapping->tree_lock);
299 error = shmem_radix_tree_replace(mapping, page->index, page, radswap);
300 page->mapping = NULL;
301 mapping->nrpages--;
302 __dec_zone_page_state(page, NR_FILE_PAGES);
303 __dec_zone_page_state(page, NR_SHMEM);
304 spin_unlock_irq(&mapping->tree_lock);
305 page_cache_release(page);
306 BUG_ON(error);
307}
308
309/*
303 * Like find_get_pages, but collecting swap entries as well as pages. 310 * Like find_get_pages, but collecting swap entries as well as pages.
304 */ 311 */
305static unsigned shmem_find_get_pages_and_swap(struct address_space *mapping, 312static unsigned shmem_find_get_pages_and_swap(struct address_space *mapping,
@@ -664,14 +671,10 @@ int shmem_unuse(swp_entry_t swap, struct page *page)
664 mutex_lock(&shmem_swaplist_mutex); 671 mutex_lock(&shmem_swaplist_mutex);
665 list_for_each_safe(this, next, &shmem_swaplist) { 672 list_for_each_safe(this, next, &shmem_swaplist) {
666 info = list_entry(this, struct shmem_inode_info, swaplist); 673 info = list_entry(this, struct shmem_inode_info, swaplist);
667 if (!info->swapped) {
668 spin_lock(&info->lock);
669 if (!info->swapped)
670 list_del_init(&info->swaplist);
671 spin_unlock(&info->lock);
672 }
673 if (info->swapped) 674 if (info->swapped)
674 found = shmem_unuse_inode(info, swap, page); 675 found = shmem_unuse_inode(info, swap, page);
676 else
677 list_del_init(&info->swaplist);
675 cond_resched(); 678 cond_resched();
676 if (found) 679 if (found)
677 break; 680 break;
@@ -694,10 +697,10 @@ out:
694static int shmem_writepage(struct page *page, struct writeback_control *wbc) 697static int shmem_writepage(struct page *page, struct writeback_control *wbc)
695{ 698{
696 struct shmem_inode_info *info; 699 struct shmem_inode_info *info;
697 swp_entry_t swap, oswap;
698 struct address_space *mapping; 700 struct address_space *mapping;
699 pgoff_t index;
700 struct inode *inode; 701 struct inode *inode;
702 swp_entry_t swap;
703 pgoff_t index;
701 704
702 BUG_ON(!PageLocked(page)); 705 BUG_ON(!PageLocked(page));
703 mapping = page->mapping; 706 mapping = page->mapping;
@@ -720,55 +723,38 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc)
720 WARN_ON_ONCE(1); /* Still happens? Tell us about it! */ 723 WARN_ON_ONCE(1); /* Still happens? Tell us about it! */
721 goto redirty; 724 goto redirty;
722 } 725 }
723
724 /*
725 * Disable even the toy swapping implementation, while we convert
726 * functions one by one to having swap entries in the radix tree.
727 */
728 if (index < ULONG_MAX)
729 goto redirty;
730
731 swap = get_swap_page(); 726 swap = get_swap_page();
732 if (!swap.val) 727 if (!swap.val)
733 goto redirty; 728 goto redirty;
734 729
735 /* 730 /*
736 * Add inode to shmem_unuse()'s list of swapped-out inodes, 731 * Add inode to shmem_unuse()'s list of swapped-out inodes,
737 * if it's not already there. Do it now because we cannot take 732 * if it's not already there. Do it now before the page is
738 * mutex while holding spinlock, and must do so before the page 733 * moved to swap cache, when its pagelock no longer protects
739 * is moved to swap cache, when its pagelock no longer protects
740 * the inode from eviction. But don't unlock the mutex until 734 * the inode from eviction. But don't unlock the mutex until
741 * we've taken the spinlock, because shmem_unuse_inode() will 735 * we've incremented swapped, because shmem_unuse_inode() will
742 * prune a !swapped inode from the swaplist under both locks. 736 * prune a !swapped inode from the swaplist under this mutex.
743 */ 737 */
744 mutex_lock(&shmem_swaplist_mutex); 738 mutex_lock(&shmem_swaplist_mutex);
745 if (list_empty(&info->swaplist)) 739 if (list_empty(&info->swaplist))
746 list_add_tail(&info->swaplist, &shmem_swaplist); 740 list_add_tail(&info->swaplist, &shmem_swaplist);
747 741
748 spin_lock(&info->lock);
749 mutex_unlock(&shmem_swaplist_mutex);
750
751 oswap = shmem_get_swap(info, index);
752 if (oswap.val) {
753 WARN_ON_ONCE(1); /* Still happens? Tell us about it! */
754 free_swap_and_cache(oswap);
755 shmem_put_swap(info, index, (swp_entry_t){0});
756 info->swapped--;
757 }
758 shmem_recalc_inode(inode);
759
760 if (add_to_swap_cache(page, swap, GFP_ATOMIC) == 0) { 742 if (add_to_swap_cache(page, swap, GFP_ATOMIC) == 0) {
761 delete_from_page_cache(page);
762 shmem_put_swap(info, index, swap);
763 info->swapped++;
764 swap_shmem_alloc(swap); 743 swap_shmem_alloc(swap);
744 shmem_delete_from_page_cache(page, swp_to_radix_entry(swap));
745
746 spin_lock(&info->lock);
747 info->swapped++;
748 shmem_recalc_inode(inode);
765 spin_unlock(&info->lock); 749 spin_unlock(&info->lock);
750
751 mutex_unlock(&shmem_swaplist_mutex);
766 BUG_ON(page_mapped(page)); 752 BUG_ON(page_mapped(page));
767 swap_writepage(page, wbc); 753 swap_writepage(page, wbc);
768 return 0; 754 return 0;
769 } 755 }
770 756
771 spin_unlock(&info->lock); 757 mutex_unlock(&shmem_swaplist_mutex);
772 swapcache_free(swap, NULL); 758 swapcache_free(swap, NULL);
773redirty: 759redirty:
774 set_page_dirty(page); 760 set_page_dirty(page);