Subject: mm: Ensure vma->vm_file stability for speculative faults From: Peter Zijlstra Date: Wed Jan 06 19:35:54 CET 2010 Ensure ->fault() calls have stable access to vma->vm_file by synchronizing the removal against threads faulting on that vma. Its a bit sad we need this much effort for what is basically an ill-formed program, but such is life ;-) TODO: - cure that O(nr_threads) loop Signed-off-by: Peter Zijlstra LKML-Reference: --- include/linux/sched.h | 1 + mm/memory.c | 2 ++ mm/mmap.c | 22 ++++++++++++++++++++++ mm/page-writeback.c | 3 ++- 4 files changed, 27 insertions(+), 1 deletion(-) Index: linux-2.6/include/linux/sched.h =================================================================== --- linux-2.6.orig/include/linux/sched.h +++ linux-2.6/include/linux/sched.h @@ -1277,6 +1277,7 @@ struct task_struct { struct plist_node pushable_tasks; struct mm_struct *mm, *active_mm; + struct vm_area_struct *fault_vma; /* task state */ int exit_state; Index: linux-2.6/mm/memory.c =================================================================== --- linux-2.6.orig/mm/memory.c +++ linux-2.6/mm/memory.c @@ -3157,6 +3157,7 @@ int handle_speculative_fault(struct mm_s goto out_unmap; entry = *pte; + rcu_assign_pointer(current->fault_vma, vma); if (read_seqcount_retry(&vma->vm_sequence, seq)) goto out_unmap; @@ -3167,6 +3168,7 @@ int handle_speculative_fault(struct mm_s ret = handle_pte_fault(mm, vma, address, entry, pmd, flags, seq); out_unlock: + rcu_assign_pointer(current->fault_vma, NULL); rcu_read_unlock(); return ret; Index: linux-2.6/mm/mmap.c =================================================================== --- linux-2.6.orig/mm/mmap.c +++ linux-2.6/mm/mmap.c @@ -235,6 +235,27 @@ static void free_vma(struct vm_area_stru call_rcu(&vma->vm_rcu_head, free_vma_rcu); } +static void sync_vma(struct vm_area_struct *vma) +{ + struct task_struct *t; + int block = 0; + + if (!vma->vm_file) + return; + + rcu_read_lock(); + for (t = current; (t = next_thread(t)) != current; ) { + if (rcu_dereference(t->fault_vma) == vma) { + block = 1; + break; + } + } + rcu_read_unlock(); + + if (block) + synchronize_rcu(); +} + /* * Close a vm structure and free it, returning the next. */ @@ -243,6 +264,7 @@ static struct vm_area_struct *remove_vma struct vm_area_struct *next = vma->vm_next; might_sleep(); + sync_vma(vma); if (vma->vm_ops && vma->vm_ops->close) vma->vm_ops->close(vma); if (vma->vm_file) {