diff options
| author | Thomas Gleixner <tglx@linutronix.de> | 2010-10-12 11:27:20 -0400 |
|---|---|---|
| committer | Thomas Gleixner <tglx@linutronix.de> | 2010-10-12 11:27:28 -0400 |
| commit | 37eca0d64a2dbeece25969ec0698e1ff72bdcf39 (patch) | |
| tree | 54ba70f0428f586f4fe28d8b429b9f9e0799a17a /mm/memory.c | |
| parent | 277b199800ac90811ac86d215063df1984f51619 (diff) | |
| parent | 3c06806e690885ce978ef180c8f8b6f8c17fb4b4 (diff) | |
Merge branch 'linus' into core/locking
Reason: Pull in the semaphore related changes
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'mm/memory.c')
| -rw-r--r-- | mm/memory.c | 71 |
1 files changed, 58 insertions, 13 deletions
diff --git a/mm/memory.c b/mm/memory.c index b6e5fd23cc5a..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,25 +2783,43 @@ 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 | ||
| 2762 | /* | 2793 | /* |
| 2763 | * This is like a special single-page "expand_downwards()", | 2794 | * This is like a special single-page "expand_{down|up}wards()", |
| 2764 | * except we must first make sure that 'address-PAGE_SIZE' | 2795 | * except we must first make sure that 'address{-|+}PAGE_SIZE' |
| 2765 | * doesn't hit another vma. | 2796 | * doesn't hit another vma. |
| 2766 | * | ||
| 2767 | * The "find_vma()" will do the right thing even if we wrap | ||
| 2768 | */ | 2797 | */ |
| 2769 | static inline int check_stack_guard_page(struct vm_area_struct *vma, unsigned long address) | 2798 | static inline int check_stack_guard_page(struct vm_area_struct *vma, unsigned long address) |
| 2770 | { | 2799 | { |
| 2771 | address &= PAGE_MASK; | 2800 | address &= PAGE_MASK; |
| 2772 | if ((vma->vm_flags & VM_GROWSDOWN) && address == vma->vm_start) { | 2801 | if ((vma->vm_flags & VM_GROWSDOWN) && address == vma->vm_start) { |
| 2773 | address -= PAGE_SIZE; | 2802 | struct vm_area_struct *prev = vma->vm_prev; |
| 2774 | if (find_vma(vma->vm_mm, address) != vma) | 2803 | |
| 2775 | return -ENOMEM; | 2804 | /* |
| 2805 | * Is there a mapping abutting this one below? | ||
| 2806 | * | ||
| 2807 | * That's only ok if it's the same stack mapping | ||
| 2808 | * that has gotten split.. | ||
| 2809 | */ | ||
| 2810 | if (prev && prev->vm_end == address) | ||
| 2811 | return prev->vm_flags & VM_GROWSDOWN ? 0 : -ENOMEM; | ||
| 2812 | |||
| 2813 | expand_stack(vma, address - PAGE_SIZE); | ||
| 2814 | } | ||
| 2815 | if ((vma->vm_flags & VM_GROWSUP) && address + PAGE_SIZE == vma->vm_end) { | ||
| 2816 | struct vm_area_struct *next = vma->vm_next; | ||
| 2817 | |||
| 2818 | /* As VM_GROWSDOWN but s/below/above/ */ | ||
| 2819 | if (next && next->vm_start == address + PAGE_SIZE) | ||
| 2820 | return next->vm_flags & VM_GROWSUP ? 0 : -ENOMEM; | ||
| 2776 | 2821 | ||
| 2777 | expand_stack(vma, address); | 2822 | expand_upwards(vma, address + PAGE_SIZE); |
| 2778 | } | 2823 | } |
| 2779 | return 0; | 2824 | return 0; |
| 2780 | } | 2825 | } |
