diff options
Diffstat (limited to 'mm/memory.c')
| -rw-r--r-- | mm/memory.c | 74 | 
1 files changed, 53 insertions, 21 deletions
diff --git a/mm/memory.c b/mm/memory.c index 388dcf9aa283..9cb27470fee9 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -61,6 +61,7 @@  #include <linux/string.h>  #include <linux/dma-debug.h>  #include <linux/debugfs.h> +#include <linux/userfaultfd_k.h>  #include <asm/io.h>  #include <asm/pgalloc.h> @@ -180,22 +181,22 @@ static void check_sync_rss_stat(struct task_struct *task)  #ifdef HAVE_GENERIC_MMU_GATHER -static int tlb_next_batch(struct mmu_gather *tlb) +static bool tlb_next_batch(struct mmu_gather *tlb)  {  	struct mmu_gather_batch *batch;  	batch = tlb->active;  	if (batch->next) {  		tlb->active = batch->next; -		return 1; +		return true;  	}  	if (tlb->batch_count == MAX_GATHER_BATCH_COUNT) -		return 0; +		return false;  	batch = (void *)__get_free_pages(GFP_NOWAIT | __GFP_NOWARN, 0);  	if (!batch) -		return 0; +		return false;  	tlb->batch_count++;  	batch->next = NULL; @@ -205,7 +206,7 @@ static int tlb_next_batch(struct mmu_gather *tlb)  	tlb->active->next = batch;  	tlb->active = batch; -	return 1; +	return true;  }  /* tlb_gather_mmu @@ -2425,8 +2426,6 @@ void unmap_mapping_range(struct address_space *mapping,  	if (details.last_index < details.first_index)  		details.last_index = ULONG_MAX; - -	/* DAX uses i_mmap_lock to serialise file truncate vs page fault */  	i_mmap_lock_write(mapping);  	if (unlikely(!RB_EMPTY_ROOT(&mapping->i_mmap)))  		unmap_mapping_range_tree(&mapping->i_mmap, &details); @@ -2685,6 +2684,12 @@ static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma,  		page_table = pte_offset_map_lock(mm, pmd, address, &ptl);  		if (!pte_none(*page_table))  			goto unlock; +		/* Deliver the page fault to userland, check inside PT lock */ +		if (userfaultfd_missing(vma)) { +			pte_unmap_unlock(page_table, ptl); +			return handle_userfault(vma, address, flags, +						VM_UFFD_MISSING); +		}  		goto setpte;  	} @@ -2713,6 +2718,15 @@ static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma,  	if (!pte_none(*page_table))  		goto release; +	/* Deliver the page fault to userland, check inside PT lock */ +	if (userfaultfd_missing(vma)) { +		pte_unmap_unlock(page_table, ptl); +		mem_cgroup_cancel_charge(page, memcg); +		page_cache_release(page); +		return handle_userfault(vma, address, flags, +					VM_UFFD_MISSING); +	} +  	inc_mm_counter_fast(mm, MM_ANONPAGES);  	page_add_new_anon_rmap(page, vma, address);  	mem_cgroup_commit_charge(page, memcg, false); @@ -2999,9 +3013,9 @@ static int do_cow_fault(struct mm_struct *mm, struct vm_area_struct *vma,  		} else {  			/*  			 * The fault handler has no page to lock, so it holds -			 * i_mmap_lock for read to protect against truncate. +			 * i_mmap_lock for write to protect against truncate.  			 */ -			i_mmap_unlock_read(vma->vm_file->f_mapping); +			i_mmap_unlock_write(vma->vm_file->f_mapping);  		}  		goto uncharge_out;  	} @@ -3015,9 +3029,9 @@ static int do_cow_fault(struct mm_struct *mm, struct vm_area_struct *vma,  	} else {  		/*  		 * The fault handler has no page to lock, so it holds -		 * i_mmap_lock for read to protect against truncate. +		 * i_mmap_lock for write to protect against truncate.  		 */ -		i_mmap_unlock_read(vma->vm_file->f_mapping); +		i_mmap_unlock_write(vma->vm_file->f_mapping);  	}  	return ret;  uncharge_out: @@ -3216,6 +3230,27 @@ out:  	return 0;  } +static int create_huge_pmd(struct mm_struct *mm, struct vm_area_struct *vma, +			unsigned long address, pmd_t *pmd, unsigned int flags) +{ +	if (vma_is_anonymous(vma)) +		return do_huge_pmd_anonymous_page(mm, vma, address, pmd, flags); +	if (vma->vm_ops->pmd_fault) +		return vma->vm_ops->pmd_fault(vma, address, pmd, flags); +	return VM_FAULT_FALLBACK; +} + +static int wp_huge_pmd(struct mm_struct *mm, struct vm_area_struct *vma, +			unsigned long address, pmd_t *pmd, pmd_t orig_pmd, +			unsigned int flags) +{ +	if (vma_is_anonymous(vma)) +		return do_huge_pmd_wp_page(mm, vma, address, pmd, orig_pmd); +	if (vma->vm_ops->pmd_fault) +		return vma->vm_ops->pmd_fault(vma, address, pmd, flags); +	return VM_FAULT_FALLBACK; +} +  /*   * These routines also need to handle stuff like marking pages dirty   * and/or accessed for architectures that don't do it in hardware (most @@ -3251,12 +3286,12 @@ static int handle_pte_fault(struct mm_struct *mm,  	barrier();  	if (!pte_present(entry)) {  		if (pte_none(entry)) { -			if (vma->vm_ops) +			if (vma_is_anonymous(vma)) +				return do_anonymous_page(mm, vma, address, +							 pte, pmd, flags); +			else  				return do_fault(mm, vma, address, pte, pmd,  						flags, entry); - -			return do_anonymous_page(mm, vma, address, pte, pmd, -					flags);  		}  		return do_swap_page(mm, vma, address,  					pte, pmd, flags, entry); @@ -3318,10 +3353,7 @@ static int __handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,  	if (!pmd)  		return VM_FAULT_OOM;  	if (pmd_none(*pmd) && transparent_hugepage_enabled(vma)) { -		int ret = VM_FAULT_FALLBACK; -		if (!vma->vm_ops) -			ret = do_huge_pmd_anonymous_page(mm, vma, address, -					pmd, flags); +		int ret = create_huge_pmd(mm, vma, address, pmd, flags);  		if (!(ret & VM_FAULT_FALLBACK))  			return ret;  	} else { @@ -3345,8 +3377,8 @@ static int __handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,  							     orig_pmd, pmd);  			if (dirty && !pmd_write(orig_pmd)) { -				ret = do_huge_pmd_wp_page(mm, vma, address, pmd, -							  orig_pmd); +				ret = wp_huge_pmd(mm, vma, address, pmd, +							orig_pmd, flags);  				if (!(ret & VM_FAULT_FALLBACK))  					return ret;  			} else {  |