Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 283017
b: refs/heads/master
c: 948f017
h: refs/heads/master
i:
  283015: e36f069
v: v3
  • Loading branch information
Andrea Arcangeli authored and Linus Torvalds committed Jan 11, 2012
1 parent d61fd3a commit 162c1f7
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 4 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: df0a6daa01fa3856c08f4274d4f21a8092caa480
refs/heads/master: 948f017b093a9baac23855fcd920d3a970b71bb6
1 change: 1 addition & 0 deletions trunk/include/linux/rmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ void anon_vma_init(void); /* create anon_vma_cachep */
int anon_vma_prepare(struct vm_area_struct *);
void unlink_anon_vmas(struct vm_area_struct *);
int anon_vma_clone(struct vm_area_struct *, struct vm_area_struct *);
void anon_vma_moveto_tail(struct vm_area_struct *);
int anon_vma_fork(struct vm_area_struct *, struct vm_area_struct *);
void __anon_vma_link(struct vm_area_struct *);

Expand Down
24 changes: 21 additions & 3 deletions trunk/mm/mmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -2322,13 +2322,16 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
struct vm_area_struct *new_vma, *prev;
struct rb_node **rb_link, *rb_parent;
struct mempolicy *pol;
bool faulted_in_anon_vma = true;

/*
* If anonymous vma has not yet been faulted, update new pgoff
* to match new location, to increase its chance of merging.
*/
if (!vma->vm_file && !vma->anon_vma)
if (unlikely(!vma->vm_file && !vma->anon_vma)) {
pgoff = addr >> PAGE_SHIFT;
faulted_in_anon_vma = false;
}

find_vma_prepare(mm, addr, &prev, &rb_link, &rb_parent);
new_vma = vma_merge(mm, prev, addr, addr + len, vma->vm_flags,
Expand All @@ -2337,9 +2340,24 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
/*
* Source vma may have been merged into new_vma
*/
if (vma_start >= new_vma->vm_start &&
vma_start < new_vma->vm_end)
if (unlikely(vma_start >= new_vma->vm_start &&
vma_start < new_vma->vm_end)) {
/*
* The only way we can get a vma_merge with
* self during an mremap is if the vma hasn't
* been faulted in yet and we were allowed to
* reset the dst vma->vm_pgoff to the
* destination address of the mremap to allow
* the merge to happen. mremap must change the
* vm_pgoff linearity between src and dst vmas
* (in turn preventing a vma_merge) to be
* safe. It is only safe to keep the vm_pgoff
* linear if there are no pages mapped yet.
*/
VM_BUG_ON(faulted_in_anon_vma);
*vmap = new_vma;
} else
anon_vma_moveto_tail(new_vma);
} else {
new_vma = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
if (new_vma) {
Expand Down
9 changes: 9 additions & 0 deletions trunk/mm/mremap.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,15 @@ static unsigned long move_vma(struct vm_area_struct *vma,

moved_len = move_page_tables(vma, old_addr, new_vma, new_addr, old_len);
if (moved_len < old_len) {
/*
* Before moving the page tables from the new vma to
* the old vma, we need to be sure the old vma is
* queued after new vma in the same_anon_vma list to
* prevent SMP races with rmap_walk (that could lead
* rmap_walk to miss some page table).
*/
anon_vma_moveto_tail(vma);

/*
* On error, move entries back from new area to old,
* which will succeed since page tables still there,
Expand Down
45 changes: 45 additions & 0 deletions trunk/mm/rmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,51 @@ int anon_vma_clone(struct vm_area_struct *dst, struct vm_area_struct *src)
return -ENOMEM;
}

/*
* Some rmap walk that needs to find all ptes/hugepmds without false
* negatives (like migrate and split_huge_page) running concurrent
* with operations that copy or move pagetables (like mremap() and
* fork()) to be safe. They depend on the anon_vma "same_anon_vma"
* list to be in a certain order: the dst_vma must be placed after the
* src_vma in the list. This is always guaranteed by fork() but
* mremap() needs to call this function to enforce it in case the
* dst_vma isn't newly allocated and chained with the anon_vma_clone()
* function but just an extension of a pre-existing vma through
* vma_merge.
*
* NOTE: the same_anon_vma list can still be changed by other
* processes while mremap runs because mremap doesn't hold the
* anon_vma mutex to prevent modifications to the list while it
* runs. All we need to enforce is that the relative order of this
* process vmas isn't changing (we don't care about other vmas
* order). Each vma corresponds to an anon_vma_chain structure so
* there's no risk that other processes calling anon_vma_moveto_tail()
* and changing the same_anon_vma list under mremap() will screw with
* the relative order of this process vmas in the list, because we
* they can't alter the order of any vma that belongs to this
* process. And there can't be another anon_vma_moveto_tail() running
* concurrently with mremap() coming from this process because we hold
* the mmap_sem for the whole mremap(). fork() ordering dependency
* also shouldn't be affected because fork() only cares that the
* parent vmas are placed in the list before the child vmas and
* anon_vma_moveto_tail() won't reorder vmas from either the fork()
* parent or child.
*/
void anon_vma_moveto_tail(struct vm_area_struct *dst)
{
struct anon_vma_chain *pavc;
struct anon_vma *root = NULL;

list_for_each_entry_reverse(pavc, &dst->anon_vma_chain, same_vma) {
struct anon_vma *anon_vma = pavc->anon_vma;
VM_BUG_ON(pavc->vma != dst);
root = lock_anon_vma_root(root, anon_vma);
list_del(&pavc->same_anon_vma);
list_add_tail(&pavc->same_anon_vma, &anon_vma->head);
}
unlock_anon_vma_root(root);
}

/*
* Attach vma to its own anon_vma, as well as to the anon_vmas that
* the corresponding VMA in the parent process is attached to.
Expand Down

0 comments on commit 162c1f7

Please sign in to comment.