diff options
author | Hugh Dickins <hughd@google.com> | 2011-08-03 19:21:25 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-08-03 20:25:24 -0400 |
commit | 6922c0c7abd387374255801f7739624867e8acad (patch) | |
tree | 851ec456dbca49ee78c302fd62839e9889a75290 /mm/shmem.c | |
parent | aa3b189551ad8e5cc1d9c663735c131650238278 (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.c | 88 |
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 | ||
222 | static 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 | |||
229 | static 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 | */ | ||
293 | static 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 | */ |
305 | static unsigned shmem_find_get_pages_and_swap(struct address_space *mapping, | 312 | static 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: | |||
694 | static int shmem_writepage(struct page *page, struct writeback_control *wbc) | 697 | static 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); |
773 | redirty: | 759 | redirty: |
774 | set_page_dirty(page); | 760 | set_page_dirty(page); |