diff options
Diffstat (limited to 'mm/rmap.c')
| -rw-r--r-- | mm/rmap.c | 40 | 
1 files changed, 33 insertions, 7 deletions
diff --git a/mm/rmap.c b/mm/rmap.c index 0feeef860a8f..38a336e2eea1 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -250,7 +250,7 @@ static void anon_vma_unlink(struct anon_vma_chain *anon_vma_chain)  	list_del(&anon_vma_chain->same_anon_vma);  	/* We must garbage collect the anon_vma if it's empty */ -	empty = list_empty(&anon_vma->head) && !ksm_refcount(anon_vma); +	empty = list_empty(&anon_vma->head) && !anonvma_external_refcount(anon_vma);  	spin_unlock(&anon_vma->lock);  	if (empty) @@ -274,7 +274,7 @@ static void anon_vma_ctor(void *data)  	struct anon_vma *anon_vma = data;  	spin_lock_init(&anon_vma->lock); -	ksm_refcount_init(anon_vma); +	anonvma_external_refcount_init(anon_vma);  	INIT_LIST_HEAD(&anon_vma->head);  } @@ -1131,6 +1131,20 @@ static int try_to_unmap_cluster(unsigned long cursor, unsigned int *mapcount,  	return ret;  } +static bool is_vma_temporary_stack(struct vm_area_struct *vma) +{ +	int maybe_stack = vma->vm_flags & (VM_GROWSDOWN | VM_GROWSUP); + +	if (!maybe_stack) +		return false; + +	if ((vma->vm_flags & VM_STACK_INCOMPLETE_SETUP) == +						VM_STACK_INCOMPLETE_SETUP) +		return true; + +	return false; +} +  /**   * try_to_unmap_anon - unmap or unlock anonymous page using the object-based   * rmap method @@ -1159,7 +1173,21 @@ static int try_to_unmap_anon(struct page *page, enum ttu_flags flags)  	list_for_each_entry(avc, &anon_vma->head, same_anon_vma) {  		struct vm_area_struct *vma = avc->vma; -		unsigned long address = vma_address(page, vma); +		unsigned long address; + +		/* +		 * During exec, a temporary VMA is setup and later moved. +		 * The VMA is moved under the anon_vma lock but not the +		 * page tables leading to a race where migration cannot +		 * find the migration ptes. Rather than increasing the +		 * locking requirements of exec(), migration skips +		 * temporary VMAs until after exec() completes. +		 */ +		if (PAGE_MIGRATION && (flags & TTU_MIGRATION) && +				is_vma_temporary_stack(vma)) +			continue; + +		address = vma_address(page, vma);  		if (address == -EFAULT)  			continue;  		ret = try_to_unmap_one(page, vma, address, flags); @@ -1355,10 +1383,8 @@ static int rmap_walk_anon(struct page *page, int (*rmap_one)(struct page *,  	/*  	 * Note: remove_migration_ptes() cannot use page_lock_anon_vma()  	 * because that depends on page_mapped(); but not all its usages -	 * are holding mmap_sem, which also gave the necessary guarantee -	 * (that this anon_vma's slab has not already been destroyed). -	 * This needs to be reviewed later: avoiding page_lock_anon_vma() -	 * is risky, and currently limits the usefulness of rmap_walk(). +	 * are holding mmap_sem. Users without mmap_sem are required to +	 * take a reference count to prevent the anon_vma disappearing  	 */  	anon_vma = page_anon_vma(page);  	if (!anon_vma)  |