FROMLIST: mm: make pte_unmap_same compatible with SPF

pte_unmap_same() is making the assumption that the page table are still
around because the mmap_sem is held.
This is no more the case when running a speculative page fault and
additional check must be made to ensure that the final page table are still
there.

This is now done by calling pte_spinlock() to check for the VMA's
consistency while locking for the page tables.

This is requiring passing a vm_fault structure to pte_unmap_same() which is
containing all the needed parameters.

As pte_spinlock() may fail in the case of a speculative page fault, if the
VMA has been touched in our back, pte_unmap_same() should now return 3
cases :
	1. pte are the same (0)
	2. pte are different (VM_FAULT_PTNOTSAME)
	3. a VMA's changes has been detected (VM_FAULT_RETRY)

The case 2 is handled by the introduction of a new VM_FAULT flag named
VM_FAULT_PTNOTSAME which is then trapped in cow_user_page().
If VM_FAULT_RETRY is returned, it is passed up to the callers to retry the
page fault while holding the mmap_sem.

Change-Id: Iaccfa0d877334f4343f8b0ec3400af5070ff5864
Acked-by: David Rientjes <rientjes@google.com>
Signed-off-by: Laurent Dufour <ldufour@linux.vnet.ibm.com>
Link: https://lore.kernel.org/lkml/1523975611-15978-7-git-send-email-ldufour@linux.vnet.ibm.com/
Bug: 161210518
Signed-off-by: Vinayak Menon <vinmenon@codeaurora.org>
Signed-off-by: Charan Teja Reddy <charante@codeaurora.org>
This commit is contained in:
Laurent Dufour
2018-04-17 16:33:12 +02:00
committed by Suren Baghdasaryan
parent b23ffc113b
commit 5835d87162
2 changed files with 30 additions and 12 deletions

View File

@@ -735,6 +735,7 @@ enum vm_fault_reason {
VM_FAULT_FALLBACK = (__force vm_fault_t)0x000800,
VM_FAULT_DONE_COW = (__force vm_fault_t)0x001000,
VM_FAULT_NEEDDSYNC = (__force vm_fault_t)0x002000,
VM_FAULT_PTNOTSAME = (__force vm_fault_t)0x004000,
VM_FAULT_HINDEX_MASK = (__force vm_fault_t)0x0f0000,
};

View File

@@ -2595,21 +2595,29 @@ EXPORT_SYMBOL_GPL(apply_to_existing_page_range);
* parts, do_swap_page must check under lock before unmapping the pte and
* proceeding (but do_wp_page is only called after already making such a check;
* and do_anonymous_page can safely check later on).
*
* pte_unmap_same() returns:
* 0 if the PTE are the same
* VM_FAULT_PTNOTSAME if the PTE are different
* VM_FAULT_RETRY if the VMA has changed in our back during
* a speculative page fault handling.
*/
static inline int pte_unmap_same(struct mm_struct *mm, pmd_t *pmd,
pte_t *page_table, pte_t orig_pte)
static inline int pte_unmap_same(struct vm_fault *vmf)
{
int same = 1;
#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPTION)
int ret = 0;
#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)
if (sizeof(pte_t) > sizeof(unsigned long)) {
spinlock_t *ptl = pte_lockptr(mm, pmd);
spin_lock(ptl);
same = pte_same(*page_table, orig_pte);
spin_unlock(ptl);
if (pte_spinlock(vmf)) {
if (!pte_same(*vmf->pte, vmf->orig_pte))
ret = VM_FAULT_PTNOTSAME;
spin_unlock(vmf->ptl);
} else
ret = VM_FAULT_RETRY;
}
#endif
pte_unmap(page_table);
return same;
pte_unmap(vmf->pte);
return ret;
}
static inline bool cow_user_page(struct page *dst, struct page *src,
@@ -3289,11 +3297,20 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
pte_t pte;
int locked;
int exclusive = 0;
vm_fault_t ret = 0;
vm_fault_t ret;
void *shadow = NULL;
if (!pte_unmap_same(vma->vm_mm, vmf->pmd, vmf->pte, vmf->orig_pte))
ret = pte_unmap_same(vmf);
if (ret) {
/*
* If pte != orig_pte, this means another thread did the
* swap operation in our back.
* So nothing else to do.
*/
if (ret == VM_FAULT_PTNOTSAME)
ret = 0;
goto out;
}
entry = pte_to_swp_entry(vmf->orig_pte);
if (unlikely(non_swap_entry(entry))) {