diff options
Diffstat (limited to 'mm/hugetlb.c')
| -rw-r--r-- | mm/hugetlb.c | 32 | 
1 files changed, 30 insertions, 2 deletions
| diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 424b0ef08a60..2d2ff5e8bf2b 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -3984,6 +3984,9 @@ int hugetlb_mcopy_atomic_pte(struct mm_struct *dst_mm,  			    unsigned long src_addr,  			    struct page **pagep)  { +	struct address_space *mapping; +	pgoff_t idx; +	unsigned long size;  	int vm_shared = dst_vma->vm_flags & VM_SHARED;  	struct hstate *h = hstate_vma(dst_vma);  	pte_t _dst_pte; @@ -4021,13 +4024,24 @@ int hugetlb_mcopy_atomic_pte(struct mm_struct *dst_mm,  	__SetPageUptodate(page);  	set_page_huge_active(page); +	mapping = dst_vma->vm_file->f_mapping; +	idx = vma_hugecache_offset(h, dst_vma, dst_addr); +  	/*  	 * If shared, add to page cache  	 */  	if (vm_shared) { -		struct address_space *mapping = dst_vma->vm_file->f_mapping; -		pgoff_t idx = vma_hugecache_offset(h, dst_vma, dst_addr); +		size = i_size_read(mapping->host) >> huge_page_shift(h); +		ret = -EFAULT; +		if (idx >= size) +			goto out_release_nounlock; +		/* +		 * Serialization between remove_inode_hugepages() and +		 * huge_add_to_page_cache() below happens through the +		 * hugetlb_fault_mutex_table that here must be hold by +		 * the caller. +		 */  		ret = huge_add_to_page_cache(page, mapping, idx);  		if (ret)  			goto out_release_nounlock; @@ -4036,6 +4050,20 @@ int hugetlb_mcopy_atomic_pte(struct mm_struct *dst_mm,  	ptl = huge_pte_lockptr(h, dst_mm, dst_pte);  	spin_lock(ptl); +	/* +	 * Recheck the i_size after holding PT lock to make sure not +	 * to leave any page mapped (as page_mapped()) beyond the end +	 * of the i_size (remove_inode_hugepages() is strict about +	 * enforcing that). If we bail out here, we'll also leave a +	 * page in the radix tree in the vm_shared case beyond the end +	 * of the i_size, but remove_inode_hugepages() will take care +	 * of it as soon as we drop the hugetlb_fault_mutex_table. +	 */ +	size = i_size_read(mapping->host) >> huge_page_shift(h); +	ret = -EFAULT; +	if (idx >= size) +		goto out_release_unlock; +  	ret = -EEXIST;  	if (!huge_pte_none(huge_ptep_get(dst_pte)))  		goto out_release_unlock; |