Skip to content

Commit 6bdb913

Browse files
haggaietorvalds
authored andcommitted
mm: wrap calls to set_pte_at_notify with invalidate_range_start and invalidate_range_end
In order to allow sleeping during invalidate_page mmu notifier calls, we need to avoid calling when holding the PT lock. In addition to its direct calls, invalidate_page can also be called as a substitute for a change_pte call, in case the notifier client hasn't implemented change_pte. This patch drops the invalidate_page call from change_pte, and instead wraps all calls to change_pte with invalidate_range_start and invalidate_range_end calls. Note that change_pte still cannot sleep after this patch, and that clients implementing change_pte should not take action on it in case the number of outstanding invalidate_range_start calls is larger than one, otherwise they might miss a later invalidation. Signed-off-by: Haggai Eran <[email protected]> Cc: Andrea Arcangeli <[email protected]> Cc: Sagi Grimberg <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Xiao Guangrong <[email protected]> Cc: Or Gerlitz <[email protected]> Cc: Haggai Eran <[email protected]> Cc: Shachar Raindel <[email protected]> Cc: Liran Liss <[email protected]> Cc: Christoph Lameter <[email protected]> Cc: Avi Kivity <[email protected]> Cc: Hugh Dickins <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 2ec74c3 commit 6bdb913

File tree

4 files changed

+36
-14
lines changed

4 files changed

+36
-14
lines changed

kernel/events/uprobes.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,14 @@ static int __replace_page(struct vm_area_struct *vma, unsigned long addr,
141141
spinlock_t *ptl;
142142
pte_t *ptep;
143143
int err;
144+
/* For mmu_notifiers */
145+
const unsigned long mmun_start = addr;
146+
const unsigned long mmun_end = addr + PAGE_SIZE;
144147

145148
/* For try_to_free_swap() and munlock_vma_page() below */
146149
lock_page(page);
147150

151+
mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end);
148152
err = -EAGAIN;
149153
ptep = page_check_address(page, mm, addr, &ptl, 0);
150154
if (!ptep)
@@ -173,6 +177,7 @@ static int __replace_page(struct vm_area_struct *vma, unsigned long addr,
173177

174178
err = 0;
175179
unlock:
180+
mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
176181
unlock_page(page);
177182
return err;
178183
}

mm/ksm.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -709,15 +709,22 @@ static int write_protect_page(struct vm_area_struct *vma, struct page *page,
709709
spinlock_t *ptl;
710710
int swapped;
711711
int err = -EFAULT;
712+
unsigned long mmun_start; /* For mmu_notifiers */
713+
unsigned long mmun_end; /* For mmu_notifiers */
712714

713715
addr = page_address_in_vma(page, vma);
714716
if (addr == -EFAULT)
715717
goto out;
716718

717719
BUG_ON(PageTransCompound(page));
720+
721+
mmun_start = addr;
722+
mmun_end = addr + PAGE_SIZE;
723+
mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end);
724+
718725
ptep = page_check_address(page, mm, addr, &ptl, 0);
719726
if (!ptep)
720-
goto out;
727+
goto out_mn;
721728

722729
if (pte_write(*ptep) || pte_dirty(*ptep)) {
723730
pte_t entry;
@@ -752,6 +759,8 @@ static int write_protect_page(struct vm_area_struct *vma, struct page *page,
752759

753760
out_unlock:
754761
pte_unmap_unlock(ptep, ptl);
762+
out_mn:
763+
mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
755764
out:
756765
return err;
757766
}
@@ -776,6 +785,8 @@ static int replace_page(struct vm_area_struct *vma, struct page *page,
776785
spinlock_t *ptl;
777786
unsigned long addr;
778787
int err = -EFAULT;
788+
unsigned long mmun_start; /* For mmu_notifiers */
789+
unsigned long mmun_end; /* For mmu_notifiers */
779790

780791
addr = page_address_in_vma(page, vma);
781792
if (addr == -EFAULT)
@@ -794,10 +805,14 @@ static int replace_page(struct vm_area_struct *vma, struct page *page,
794805
if (!pmd_present(*pmd))
795806
goto out;
796807

808+
mmun_start = addr;
809+
mmun_end = addr + PAGE_SIZE;
810+
mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end);
811+
797812
ptep = pte_offset_map_lock(mm, pmd, addr, &ptl);
798813
if (!pte_same(*ptep, orig_pte)) {
799814
pte_unmap_unlock(ptep, ptl);
800-
goto out;
815+
goto out_mn;
801816
}
802817

803818
get_page(kpage);
@@ -814,6 +829,8 @@ static int replace_page(struct vm_area_struct *vma, struct page *page,
814829

815830
pte_unmap_unlock(ptep, ptl);
816831
err = 0;
832+
out_mn:
833+
mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
817834
out:
818835
return err;
819836
}

mm/memory.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2527,6 +2527,9 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
25272527
int ret = 0;
25282528
int page_mkwrite = 0;
25292529
struct page *dirty_page = NULL;
2530+
unsigned long mmun_start; /* For mmu_notifiers */
2531+
unsigned long mmun_end; /* For mmu_notifiers */
2532+
bool mmun_called = false; /* For mmu_notifiers */
25302533

25312534
old_page = vm_normal_page(vma, address, orig_pte);
25322535
if (!old_page) {
@@ -2704,6 +2707,11 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
27042707
if (mem_cgroup_newpage_charge(new_page, mm, GFP_KERNEL))
27052708
goto oom_free_new;
27062709

2710+
mmun_start = address & PAGE_MASK;
2711+
mmun_end = (address & PAGE_MASK) + PAGE_SIZE;
2712+
mmun_called = true;
2713+
mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end);
2714+
27072715
/*
27082716
* Re-check the pte - we dropped the lock
27092717
*/
@@ -2766,14 +2774,12 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
27662774
} else
27672775
mem_cgroup_uncharge_page(new_page);
27682776

2777+
if (new_page)
2778+
page_cache_release(new_page);
27692779
unlock:
27702780
pte_unmap_unlock(page_table, ptl);
2771-
if (new_page) {
2772-
if (new_page == old_page)
2773-
/* cow happened, notify before releasing old_page */
2774-
mmu_notifier_invalidate_page(mm, address);
2775-
page_cache_release(new_page);
2776-
}
2781+
if (mmun_called)
2782+
mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
27772783
if (old_page) {
27782784
/*
27792785
* Don't let another task, with possibly unlocked vma,

mm/mmu_notifier.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,6 @@ void __mmu_notifier_change_pte(struct mm_struct *mm, unsigned long address,
137137
hlist_for_each_entry_rcu(mn, n, &mm->mmu_notifier_mm->list, hlist) {
138138
if (mn->ops->change_pte)
139139
mn->ops->change_pte(mn, mm, address, pte);
140-
/*
141-
* Some drivers don't have change_pte,
142-
* so we must call invalidate_page in that case.
143-
*/
144-
else if (mn->ops->invalidate_page)
145-
mn->ops->invalidate_page(mn, mm, address);
146140
}
147141
srcu_read_unlock(&srcu, id);
148142
}

0 commit comments

Comments
 (0)