FROMLIST: mm: introduce __page_add_new_anon_rmap()
When dealing with speculative page fault handler, we may race with VMA being split or merged. In this case the vma->vm_start and vm->vm_end fields may not match the address the page fault is occurring. This can only happens when the VMA is split but in that case, the anon_vma pointer of the new VMA will be the same as the original one, because in __split_vma the new->anon_vma is set to src->anon_vma when *new = *vma. So even if the VMA boundaries are not correct, the anon_vma pointer is still valid. If the VMA has been merged, then the VMA in which it has been merged must have the same anon_vma pointer otherwise the merge can't be done. So in all the case we know that the anon_vma is valid, since we have checked before starting the speculative page fault that the anon_vma pointer is valid for this VMA and since there is an anon_vma this means that at one time a page has been backed and that before the VMA is cleaned, the page table lock would have to be grab to clean the PTE, and the anon_vma field is checked once the PTE is locked. This patch introduce a new __page_add_new_anon_rmap() service which doesn't check for the VMA boundaries, and create a new inline one which do the check. When called from a page fault handler, if this is not a speculative one, there is a guarantee that vm_start and vm_end match the faulting address, so this check is useless. In the context of the speculative page fault handler, this check may be wrong but anon_vma is still valid as explained above. Change-Id: I72c47830181579f8c9618df879077d321653b5f1 Signed-off-by: Laurent Dufour <ldufour@linux.vnet.ibm.com> Link: https://lore.kernel.org/lkml/1523975611-15978-17-git-send-email-ldufour@linux.vnet.ibm.com/ Bug: 161210518 Signed-off-by: Vinayak Menon <vinmenon@codeaurora.org>
This commit is contained in:

committed by
Suren Baghdasaryan

parent
10a5eb6be8
commit
a1dbf20e8e
@@ -173,8 +173,16 @@ void page_add_anon_rmap(struct page *, struct vm_area_struct *,
|
|||||||
unsigned long, bool);
|
unsigned long, bool);
|
||||||
void do_page_add_anon_rmap(struct page *, struct vm_area_struct *,
|
void do_page_add_anon_rmap(struct page *, struct vm_area_struct *,
|
||||||
unsigned long, int);
|
unsigned long, int);
|
||||||
void page_add_new_anon_rmap(struct page *, struct vm_area_struct *,
|
void __page_add_new_anon_rmap(struct page *page, struct vm_area_struct *vma,
|
||||||
unsigned long, bool);
|
unsigned long address, bool compound);
|
||||||
|
static inline void page_add_new_anon_rmap(struct page *page,
|
||||||
|
struct vm_area_struct *vma,
|
||||||
|
unsigned long address, bool compound)
|
||||||
|
{
|
||||||
|
VM_BUG_ON_VMA(address < vma->vm_start || address >= vma->vm_end, vma);
|
||||||
|
__page_add_new_anon_rmap(page, vma, address, compound);
|
||||||
|
}
|
||||||
|
|
||||||
void page_add_file_rmap(struct page *, bool);
|
void page_add_file_rmap(struct page *, bool);
|
||||||
void page_remove_rmap(struct page *, bool);
|
void page_remove_rmap(struct page *, bool);
|
||||||
|
|
||||||
|
@@ -2951,7 +2951,7 @@ static vm_fault_t wp_page_copy(struct vm_fault *vmf)
|
|||||||
* thread doing COW.
|
* thread doing COW.
|
||||||
*/
|
*/
|
||||||
ptep_clear_flush_notify(vma, vmf->address, vmf->pte);
|
ptep_clear_flush_notify(vma, vmf->address, vmf->pte);
|
||||||
page_add_new_anon_rmap(new_page, vma, vmf->address, false);
|
__page_add_new_anon_rmap(new_page, vma, vmf->address, false);
|
||||||
__lru_cache_add_inactive_or_unevictable(new_page, vmf->vma_flags);
|
__lru_cache_add_inactive_or_unevictable(new_page, vmf->vma_flags);
|
||||||
/*
|
/*
|
||||||
* We call the notify macro here because, when using secondary
|
* We call the notify macro here because, when using secondary
|
||||||
@@ -3494,7 +3494,7 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
|
|||||||
|
|
||||||
/* ksm created a completely new copy */
|
/* ksm created a completely new copy */
|
||||||
if (unlikely(page != swapcache && swapcache)) {
|
if (unlikely(page != swapcache && swapcache)) {
|
||||||
page_add_new_anon_rmap(page, vma, vmf->address, false);
|
__page_add_new_anon_rmap(page, vma, vmf->address, false);
|
||||||
__lru_cache_add_inactive_or_unevictable(page, vmf->vma_flags);
|
__lru_cache_add_inactive_or_unevictable(page, vmf->vma_flags);
|
||||||
} else {
|
} else {
|
||||||
do_page_add_anon_rmap(page, vma, vmf->address, exclusive);
|
do_page_add_anon_rmap(page, vma, vmf->address, exclusive);
|
||||||
@@ -3644,7 +3644,7 @@ static vm_fault_t do_anonymous_page(struct vm_fault *vmf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
inc_mm_counter_fast(vma->vm_mm, MM_ANONPAGES);
|
inc_mm_counter_fast(vma->vm_mm, MM_ANONPAGES);
|
||||||
page_add_new_anon_rmap(page, vma, vmf->address, false);
|
__page_add_new_anon_rmap(page, vma, vmf->address, false);
|
||||||
__lru_cache_add_inactive_or_unevictable(page, vmf->vma_flags);
|
__lru_cache_add_inactive_or_unevictable(page, vmf->vma_flags);
|
||||||
setpte:
|
setpte:
|
||||||
set_pte_at(vma->vm_mm, vmf->address, vmf->pte, entry);
|
set_pte_at(vma->vm_mm, vmf->address, vmf->pte, entry);
|
||||||
@@ -3906,7 +3906,7 @@ vm_fault_t alloc_set_pte(struct vm_fault *vmf, struct page *page)
|
|||||||
/* copy-on-write page */
|
/* copy-on-write page */
|
||||||
if (write && !(vmf->vma_flags & VM_SHARED)) {
|
if (write && !(vmf->vma_flags & VM_SHARED)) {
|
||||||
inc_mm_counter_fast(vma->vm_mm, MM_ANONPAGES);
|
inc_mm_counter_fast(vma->vm_mm, MM_ANONPAGES);
|
||||||
page_add_new_anon_rmap(page, vma, vmf->address, false);
|
__page_add_new_anon_rmap(page, vma, vmf->address, false);
|
||||||
__lru_cache_add_inactive_or_unevictable(page, vmf->vma_flags);
|
__lru_cache_add_inactive_or_unevictable(page, vmf->vma_flags);
|
||||||
} else {
|
} else {
|
||||||
inc_mm_counter_fast(vma->vm_mm, mm_counter_file(page));
|
inc_mm_counter_fast(vma->vm_mm, mm_counter_file(page));
|
||||||
|
@@ -1156,7 +1156,7 @@ void do_page_add_anon_rmap(struct page *page,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* page_add_new_anon_rmap - add pte mapping to a new anonymous page
|
* __page_add_new_anon_rmap - add pte mapping to a new anonymous page
|
||||||
* @page: the page to add the mapping to
|
* @page: the page to add the mapping to
|
||||||
* @vma: the vm area in which the mapping is added
|
* @vma: the vm area in which the mapping is added
|
||||||
* @address: the user virtual address mapped
|
* @address: the user virtual address mapped
|
||||||
@@ -1166,12 +1166,11 @@ void do_page_add_anon_rmap(struct page *page,
|
|||||||
* This means the inc-and-test can be bypassed.
|
* This means the inc-and-test can be bypassed.
|
||||||
* Page does not have to be locked.
|
* Page does not have to be locked.
|
||||||
*/
|
*/
|
||||||
void page_add_new_anon_rmap(struct page *page,
|
void __page_add_new_anon_rmap(struct page *page,
|
||||||
struct vm_area_struct *vma, unsigned long address, bool compound)
|
struct vm_area_struct *vma, unsigned long address, bool compound)
|
||||||
{
|
{
|
||||||
int nr = compound ? thp_nr_pages(page) : 1;
|
int nr = compound ? thp_nr_pages(page) : 1;
|
||||||
|
|
||||||
VM_BUG_ON_VMA(address < vma->vm_start || address >= vma->vm_end, vma);
|
|
||||||
__SetPageSwapBacked(page);
|
__SetPageSwapBacked(page);
|
||||||
if (compound) {
|
if (compound) {
|
||||||
VM_BUG_ON_PAGE(!PageTransHuge(page), page);
|
VM_BUG_ON_PAGE(!PageTransHuge(page), page);
|
||||||
|
Reference in New Issue
Block a user