diff options
Diffstat (limited to 'mm/memory.c')
-rw-r--r-- | mm/memory.c | 41 |
1 files changed, 36 insertions, 5 deletions
diff --git a/mm/memory.c b/mm/memory.c index 6b2ab1051851..0e18b4d649ec 100644 --- a/mm/memory.c +++ b/mm/memory.c | |||
@@ -2623,7 +2623,7 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma, | |||
2623 | unsigned int flags, pte_t orig_pte) | 2623 | unsigned int flags, pte_t orig_pte) |
2624 | { | 2624 | { |
2625 | spinlock_t *ptl; | 2625 | spinlock_t *ptl; |
2626 | struct page *page; | 2626 | struct page *page, *swapcache = NULL; |
2627 | swp_entry_t entry; | 2627 | swp_entry_t entry; |
2628 | pte_t pte; | 2628 | pte_t pte; |
2629 | struct mem_cgroup *ptr = NULL; | 2629 | struct mem_cgroup *ptr = NULL; |
@@ -2679,10 +2679,25 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma, | |||
2679 | lock_page(page); | 2679 | lock_page(page); |
2680 | delayacct_clear_flag(DELAYACCT_PF_SWAPIN); | 2680 | delayacct_clear_flag(DELAYACCT_PF_SWAPIN); |
2681 | 2681 | ||
2682 | page = ksm_might_need_to_copy(page, vma, address); | 2682 | /* |
2683 | if (!page) { | 2683 | * Make sure try_to_free_swap or reuse_swap_page or swapoff did not |
2684 | ret = VM_FAULT_OOM; | 2684 | * release the swapcache from under us. The page pin, and pte_same |
2685 | goto out; | 2685 | * test below, are not enough to exclude that. Even if it is still |
2686 | * swapcache, we need to check that the page's swap has not changed. | ||
2687 | */ | ||
2688 | if (unlikely(!PageSwapCache(page) || page_private(page) != entry.val)) | ||
2689 | goto out_page; | ||
2690 | |||
2691 | if (ksm_might_need_to_copy(page, vma, address)) { | ||
2692 | swapcache = page; | ||
2693 | page = ksm_does_need_to_copy(page, vma, address); | ||
2694 | |||
2695 | if (unlikely(!page)) { | ||
2696 | ret = VM_FAULT_OOM; | ||
2697 | page = swapcache; | ||
2698 | swapcache = NULL; | ||
2699 | goto out_page; | ||
2700 | } | ||
2686 | } | 2701 | } |
2687 | 2702 | ||
2688 | if (mem_cgroup_try_charge_swapin(mm, page, GFP_KERNEL, &ptr)) { | 2703 | if (mem_cgroup_try_charge_swapin(mm, page, GFP_KERNEL, &ptr)) { |
@@ -2735,6 +2750,18 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma, | |||
2735 | if (vm_swap_full() || (vma->vm_flags & VM_LOCKED) || PageMlocked(page)) | 2750 | if (vm_swap_full() || (vma->vm_flags & VM_LOCKED) || PageMlocked(page)) |
2736 | try_to_free_swap(page); | 2751 | try_to_free_swap(page); |
2737 | unlock_page(page); | 2752 | unlock_page(page); |
2753 | if (swapcache) { | ||
2754 | /* | ||
2755 | * Hold the lock to avoid the swap entry to be reused | ||
2756 | * until we take the PT lock for the pte_same() check | ||
2757 | * (to avoid false positives from pte_same). For | ||
2758 | * further safety release the lock after the swap_free | ||
2759 | * so that the swap count won't change under a | ||
2760 | * parallel locked swapcache. | ||
2761 | */ | ||
2762 | unlock_page(swapcache); | ||
2763 | page_cache_release(swapcache); | ||
2764 | } | ||
2738 | 2765 | ||
2739 | if (flags & FAULT_FLAG_WRITE) { | 2766 | if (flags & FAULT_FLAG_WRITE) { |
2740 | ret |= do_wp_page(mm, vma, address, page_table, pmd, ptl, pte); | 2767 | ret |= do_wp_page(mm, vma, address, page_table, pmd, ptl, pte); |
@@ -2756,6 +2783,10 @@ out_page: | |||
2756 | unlock_page(page); | 2783 | unlock_page(page); |
2757 | out_release: | 2784 | out_release: |
2758 | page_cache_release(page); | 2785 | page_cache_release(page); |
2786 | if (swapcache) { | ||
2787 | unlock_page(swapcache); | ||
2788 | page_cache_release(swapcache); | ||
2789 | } | ||
2759 | return ret; | 2790 | return ret; |
2760 | } | 2791 | } |
2761 | 2792 | ||