diff options
Diffstat (limited to 'mm/huge_memory.c')
| -rw-r--r-- | mm/huge_memory.c | 42 | 
1 files changed, 36 insertions, 6 deletions
diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 0e7740923fb..08a943b9cf9 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -787,6 +787,8 @@ static int do_huge_pmd_wp_page_fallback(struct mm_struct *mm,  	pmd_t _pmd;  	int ret = 0, i;  	struct page **pages; +	unsigned long mmun_start;	/* For mmu_notifiers */ +	unsigned long mmun_end;		/* For mmu_notifiers */  	pages = kmalloc(sizeof(struct page *) * HPAGE_PMD_NR,  			GFP_KERNEL); @@ -823,12 +825,16 @@ static int do_huge_pmd_wp_page_fallback(struct mm_struct *mm,  		cond_resched();  	} +	mmun_start = haddr; +	mmun_end   = haddr + HPAGE_PMD_SIZE; +	mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end); +  	spin_lock(&mm->page_table_lock);  	if (unlikely(!pmd_same(*pmd, orig_pmd)))  		goto out_free_pages;  	VM_BUG_ON(!PageHead(page)); -	pmdp_clear_flush_notify(vma, haddr, pmd); +	pmdp_clear_flush(vma, haddr, pmd);  	/* leave pmd empty until pte is filled */  	pgtable = pgtable_trans_huge_withdraw(mm); @@ -851,6 +857,8 @@ static int do_huge_pmd_wp_page_fallback(struct mm_struct *mm,  	page_remove_rmap(page);  	spin_unlock(&mm->page_table_lock); +	mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end); +  	ret |= VM_FAULT_WRITE;  	put_page(page); @@ -859,6 +867,7 @@ out:  out_free_pages:  	spin_unlock(&mm->page_table_lock); +	mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);  	mem_cgroup_uncharge_start();  	for (i = 0; i < HPAGE_PMD_NR; i++) {  		mem_cgroup_uncharge_page(pages[i]); @@ -875,6 +884,8 @@ int do_huge_pmd_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,  	int ret = 0;  	struct page *page, *new_page;  	unsigned long haddr; +	unsigned long mmun_start;	/* For mmu_notifiers */ +	unsigned long mmun_end;		/* For mmu_notifiers */  	VM_BUG_ON(!vma->anon_vma);  	spin_lock(&mm->page_table_lock); @@ -925,20 +936,24 @@ int do_huge_pmd_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,  	copy_user_huge_page(new_page, page, haddr, vma, HPAGE_PMD_NR);  	__SetPageUptodate(new_page); +	mmun_start = haddr; +	mmun_end   = haddr + HPAGE_PMD_SIZE; +	mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end); +  	spin_lock(&mm->page_table_lock);  	put_page(page);  	if (unlikely(!pmd_same(*pmd, orig_pmd))) {  		spin_unlock(&mm->page_table_lock);  		mem_cgroup_uncharge_page(new_page);  		put_page(new_page); -		goto out; +		goto out_mn;  	} else {  		pmd_t entry;  		VM_BUG_ON(!PageHead(page));  		entry = mk_pmd(new_page, vma->vm_page_prot);  		entry = maybe_pmd_mkwrite(pmd_mkdirty(entry), vma);  		entry = pmd_mkhuge(entry); -		pmdp_clear_flush_notify(vma, haddr, pmd); +		pmdp_clear_flush(vma, haddr, pmd);  		page_add_new_anon_rmap(new_page, vma, haddr);  		set_pmd_at(mm, haddr, pmd, entry);  		update_mmu_cache(vma, address, pmd); @@ -946,10 +961,14 @@ int do_huge_pmd_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,  		put_page(page);  		ret |= VM_FAULT_WRITE;  	} -out_unlock:  	spin_unlock(&mm->page_table_lock); +out_mn: +	mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);  out:  	return ret; +out_unlock: +	spin_unlock(&mm->page_table_lock); +	return ret;  }  struct page *follow_trans_huge_pmd(struct mm_struct *mm, @@ -1162,7 +1181,11 @@ static int __split_huge_page_splitting(struct page *page,  	struct mm_struct *mm = vma->vm_mm;  	pmd_t *pmd;  	int ret = 0; +	/* For mmu_notifiers */ +	const unsigned long mmun_start = address; +	const unsigned long mmun_end   = address + HPAGE_PMD_SIZE; +	mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end);  	spin_lock(&mm->page_table_lock);  	pmd = page_check_address_pmd(page, mm, address,  				     PAGE_CHECK_ADDRESS_PMD_NOTSPLITTING_FLAG); @@ -1174,10 +1197,11 @@ static int __split_huge_page_splitting(struct page *page,  		 * and it won't wait on the anon_vma->root->mutex to  		 * serialize against split_huge_page*.  		 */ -		pmdp_splitting_flush_notify(vma, address, pmd); +		pmdp_splitting_flush(vma, address, pmd);  		ret = 1;  	}  	spin_unlock(&mm->page_table_lock); +	mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);  	return ret;  } @@ -1898,6 +1922,8 @@ static void collapse_huge_page(struct mm_struct *mm,  	spinlock_t *ptl;  	int isolated;  	unsigned long hstart, hend; +	unsigned long mmun_start;	/* For mmu_notifiers */ +	unsigned long mmun_end;		/* For mmu_notifiers */  	VM_BUG_ON(address & ~HPAGE_PMD_MASK); @@ -1952,6 +1978,9 @@ static void collapse_huge_page(struct mm_struct *mm,  	pte = pte_offset_map(pmd, address);  	ptl = pte_lockptr(mm, pmd); +	mmun_start = address; +	mmun_end   = address + HPAGE_PMD_SIZE; +	mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end);  	spin_lock(&mm->page_table_lock); /* probably unnecessary */  	/*  	 * After this gup_fast can't run anymore. This also removes @@ -1959,8 +1988,9 @@ static void collapse_huge_page(struct mm_struct *mm,  	 * huge and small TLB entries for the same virtual address  	 * to avoid the risk of CPU bugs in that area.  	 */ -	_pmd = pmdp_clear_flush_notify(vma, address, pmd); +	_pmd = pmdp_clear_flush(vma, address, pmd);  	spin_unlock(&mm->page_table_lock); +	mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);  	spin_lock(ptl);  	isolated = __collapse_huge_page_isolate(vma, address, pte);  |