aboutsummaryrefslogtreecommitdiffstats
path: root/mm/shmem.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/shmem.c')
-rw-r--r--mm/shmem.c37
1 files changed, 25 insertions, 12 deletions
diff --git a/mm/shmem.c b/mm/shmem.c
index 530c5033d028..ee9024483f60 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -833,6 +833,10 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, s
833 idx = 0; 833 idx = 0;
834 ptr = info->i_direct; 834 ptr = info->i_direct;
835 spin_lock(&info->lock); 835 spin_lock(&info->lock);
836 if (!info->swapped) {
837 list_del_init(&info->swaplist);
838 goto lost2;
839 }
836 limit = info->next_index; 840 limit = info->next_index;
837 size = limit; 841 size = limit;
838 if (size > SHMEM_NR_DIRECT) 842 if (size > SHMEM_NR_DIRECT)
@@ -894,8 +898,15 @@ found:
894 inode = igrab(&info->vfs_inode); 898 inode = igrab(&info->vfs_inode);
895 spin_unlock(&info->lock); 899 spin_unlock(&info->lock);
896 900
897 /* move head to start search for next from here */ 901 /*
898 list_move_tail(&shmem_swaplist, &info->swaplist); 902 * Move _head_ to start search for next from here.
903 * But be careful: shmem_delete_inode checks list_empty without taking
904 * mutex, and there's an instant in list_move_tail when info->swaplist
905 * would appear empty, if it were the only one on shmem_swaplist. We
906 * could avoid doing it if inode NULL; or use this minor optimization.
907 */
908 if (shmem_swaplist.next != &info->swaplist)
909 list_move_tail(&shmem_swaplist, &info->swaplist);
899 mutex_unlock(&shmem_swaplist_mutex); 910 mutex_unlock(&shmem_swaplist_mutex);
900 911
901 error = 1; 912 error = 1;
@@ -955,10 +966,7 @@ int shmem_unuse(swp_entry_t entry, struct page *page)
955 mutex_lock(&shmem_swaplist_mutex); 966 mutex_lock(&shmem_swaplist_mutex);
956 list_for_each_safe(p, next, &shmem_swaplist) { 967 list_for_each_safe(p, next, &shmem_swaplist) {
957 info = list_entry(p, struct shmem_inode_info, swaplist); 968 info = list_entry(p, struct shmem_inode_info, swaplist);
958 if (info->swapped) 969 found = shmem_unuse_inode(info, entry, page);
959 found = shmem_unuse_inode(info, entry, page);
960 else
961 list_del_init(&info->swaplist);
962 cond_resched(); 970 cond_resched();
963 if (found) 971 if (found)
964 goto out; 972 goto out;
@@ -1021,18 +1029,23 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc)
1021 remove_from_page_cache(page); 1029 remove_from_page_cache(page);
1022 shmem_swp_set(info, entry, swap.val); 1030 shmem_swp_set(info, entry, swap.val);
1023 shmem_swp_unmap(entry); 1031 shmem_swp_unmap(entry);
1032 if (list_empty(&info->swaplist))
1033 inode = igrab(inode);
1034 else
1035 inode = NULL;
1024 spin_unlock(&info->lock); 1036 spin_unlock(&info->lock);
1025 if (list_empty(&info->swaplist)) {
1026 mutex_lock(&shmem_swaplist_mutex);
1027 /* move instead of add in case we're racing */
1028 list_move_tail(&info->swaplist, &shmem_swaplist);
1029 mutex_unlock(&shmem_swaplist_mutex);
1030 }
1031 swap_duplicate(swap); 1037 swap_duplicate(swap);
1032 BUG_ON(page_mapped(page)); 1038 BUG_ON(page_mapped(page));
1033 page_cache_release(page); /* pagecache ref */ 1039 page_cache_release(page); /* pagecache ref */
1034 set_page_dirty(page); 1040 set_page_dirty(page);
1035 unlock_page(page); 1041 unlock_page(page);
1042 if (inode) {
1043 mutex_lock(&shmem_swaplist_mutex);
1044 /* move instead of add in case we're racing */
1045 list_move_tail(&info->swaplist, &shmem_swaplist);
1046 mutex_unlock(&shmem_swaplist_mutex);
1047 iput(inode);
1048 }
1036 return 0; 1049 return 0;
1037 } 1050 }
1038 1051