diff options
Diffstat (limited to 'mm')
-rw-r--r-- | mm/shmem.c | 69 |
1 files changed, 44 insertions, 25 deletions
diff --git a/mm/shmem.c b/mm/shmem.c index e577adf4ae85..4ae47f54c822 100644 --- a/mm/shmem.c +++ b/mm/shmem.c | |||
@@ -827,6 +827,7 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, s | |||
827 | struct page *subdir; | 827 | struct page *subdir; |
828 | swp_entry_t *ptr; | 828 | swp_entry_t *ptr; |
829 | int offset; | 829 | int offset; |
830 | int error; | ||
830 | 831 | ||
831 | idx = 0; | 832 | idx = 0; |
832 | ptr = info->i_direct; | 833 | ptr = info->i_direct; |
@@ -884,7 +885,20 @@ lost2: | |||
884 | found: | 885 | found: |
885 | idx += offset; | 886 | idx += offset; |
886 | inode = &info->vfs_inode; | 887 | inode = &info->vfs_inode; |
887 | if (add_to_page_cache(page, inode->i_mapping, idx, GFP_ATOMIC) == 0) { | 888 | error = add_to_page_cache(page, inode->i_mapping, idx, GFP_ATOMIC); |
889 | if (error == -EEXIST) { | ||
890 | struct page *filepage = find_get_page(inode->i_mapping, idx); | ||
891 | if (filepage) { | ||
892 | /* | ||
893 | * There might be a more uptodate page coming down | ||
894 | * from a stacked writepage: forget our swappage if so. | ||
895 | */ | ||
896 | if (PageUptodate(filepage)) | ||
897 | error = 0; | ||
898 | page_cache_release(filepage); | ||
899 | } | ||
900 | } | ||
901 | if (!error) { | ||
888 | delete_from_swap_cache(page); | 902 | delete_from_swap_cache(page); |
889 | set_page_dirty(page); | 903 | set_page_dirty(page); |
890 | info->flags |= SHMEM_PAGEIN; | 904 | info->flags |= SHMEM_PAGEIN; |
@@ -937,44 +951,45 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc) | |||
937 | struct inode *inode; | 951 | struct inode *inode; |
938 | 952 | ||
939 | BUG_ON(!PageLocked(page)); | 953 | BUG_ON(!PageLocked(page)); |
940 | /* | ||
941 | * shmem_backing_dev_info's capabilities prevent regular writeback or | ||
942 | * sync from ever calling shmem_writepage; but a stacking filesystem | ||
943 | * may use the ->writepage of its underlying filesystem, in which case | ||
944 | * we want to do nothing when that underlying filesystem is tmpfs | ||
945 | * (writing out to swap is useful as a response to memory pressure, but | ||
946 | * of no use to stabilize the data) - just redirty the page, unlock it | ||
947 | * and claim success in this case. AOP_WRITEPAGE_ACTIVATE, and the | ||
948 | * page_mapped check below, must be avoided unless we're in reclaim. | ||
949 | */ | ||
950 | if (!wbc->for_reclaim) { | ||
951 | set_page_dirty(page); | ||
952 | unlock_page(page); | ||
953 | return 0; | ||
954 | } | ||
955 | BUG_ON(page_mapped(page)); | ||
956 | |||
957 | mapping = page->mapping; | 954 | mapping = page->mapping; |
958 | index = page->index; | 955 | index = page->index; |
959 | inode = mapping->host; | 956 | inode = mapping->host; |
960 | info = SHMEM_I(inode); | 957 | info = SHMEM_I(inode); |
961 | if (info->flags & VM_LOCKED) | 958 | if (info->flags & VM_LOCKED) |
962 | goto redirty; | 959 | goto redirty; |
963 | swap = get_swap_page(); | 960 | if (!total_swap_pages) |
964 | if (!swap.val) | ||
965 | goto redirty; | 961 | goto redirty; |
966 | 962 | ||
963 | /* | ||
964 | * shmem_backing_dev_info's capabilities prevent regular writeback or | ||
965 | * sync from ever calling shmem_writepage; but a stacking filesystem | ||
966 | * may use the ->writepage of its underlying filesystem, in which case | ||
967 | * tmpfs should write out to swap only in response to memory pressure, | ||
968 | * and not for pdflush or sync. However, in those cases, we do still | ||
969 | * want to check if there's a redundant swappage to be discarded. | ||
970 | */ | ||
971 | if (wbc->for_reclaim) | ||
972 | swap = get_swap_page(); | ||
973 | else | ||
974 | swap.val = 0; | ||
975 | |||
967 | spin_lock(&info->lock); | 976 | spin_lock(&info->lock); |
968 | shmem_recalc_inode(inode); | ||
969 | if (index >= info->next_index) { | 977 | if (index >= info->next_index) { |
970 | BUG_ON(!(info->flags & SHMEM_TRUNCATE)); | 978 | BUG_ON(!(info->flags & SHMEM_TRUNCATE)); |
971 | goto unlock; | 979 | goto unlock; |
972 | } | 980 | } |
973 | entry = shmem_swp_entry(info, index, NULL); | 981 | entry = shmem_swp_entry(info, index, NULL); |
974 | BUG_ON(!entry); | 982 | if (entry->val) { |
975 | BUG_ON(entry->val); | 983 | /* |
984 | * The more uptodate page coming down from a stacked | ||
985 | * writepage should replace our old swappage. | ||
986 | */ | ||
987 | free_swap_and_cache(*entry); | ||
988 | shmem_swp_set(info, entry, 0); | ||
989 | } | ||
990 | shmem_recalc_inode(inode); | ||
976 | 991 | ||
977 | if (add_to_swap_cache(page, swap, GFP_ATOMIC) == 0) { | 992 | if (swap.val && add_to_swap_cache(page, swap, GFP_ATOMIC) == 0) { |
978 | remove_from_page_cache(page); | 993 | remove_from_page_cache(page); |
979 | shmem_swp_set(info, entry, swap.val); | 994 | shmem_swp_set(info, entry, swap.val); |
980 | shmem_swp_unmap(entry); | 995 | shmem_swp_unmap(entry); |
@@ -986,6 +1001,7 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc) | |||
986 | spin_unlock(&shmem_swaplist_lock); | 1001 | spin_unlock(&shmem_swaplist_lock); |
987 | } | 1002 | } |
988 | swap_duplicate(swap); | 1003 | swap_duplicate(swap); |
1004 | BUG_ON(page_mapped(page)); | ||
989 | page_cache_release(page); /* pagecache ref */ | 1005 | page_cache_release(page); /* pagecache ref */ |
990 | set_page_dirty(page); | 1006 | set_page_dirty(page); |
991 | unlock_page(page); | 1007 | unlock_page(page); |
@@ -998,7 +1014,10 @@ unlock: | |||
998 | swap_free(swap); | 1014 | swap_free(swap); |
999 | redirty: | 1015 | redirty: |
1000 | set_page_dirty(page); | 1016 | set_page_dirty(page); |
1001 | return AOP_WRITEPAGE_ACTIVATE; /* Return with the page locked */ | 1017 | if (wbc->for_reclaim) |
1018 | return AOP_WRITEPAGE_ACTIVATE; /* Return with page locked */ | ||
1019 | unlock_page(page); | ||
1020 | return 0; | ||
1002 | } | 1021 | } |
1003 | 1022 | ||
1004 | #ifdef CONFIG_NUMA | 1023 | #ifdef CONFIG_NUMA |