forked from mirrors/linux
		
	mm/pagewalk.c: walk_page_range should avoid VM_PFNMAP areas
A panic can be caused by simply cat'ing /proc/<pid>/smaps while an application has a VM_PFNMAP range. It happened in-house when a benchmarker was trying to decipher the memory layout of his program. /proc/<pid>/smaps and similar walks through a user page table should not be looking at VM_PFNMAP areas. Certain tests in walk_page_range() (specifically split_huge_page_pmd()) assume that all the mapped PFN's are backed with page structures. And this is not usually true for VM_PFNMAP areas. This can result in panics on kernel page faults when attempting to address those page structures. There are a half dozen callers of walk_page_range() that walk through a task's entire page table (as N. Horiguchi pointed out). So rather than change all of them, this patch changes just walk_page_range() to ignore VM_PFNMAP areas. The logic of hugetlb_vma() is moved back into walk_page_range(), as we want to test any vma in the range. VM_PFNMAP areas are used by: - graphics memory manager gpu/drm/drm_gem.c - global reference unit sgi-gru/grufile.c - sgi special memory char/mspec.c - and probably several out-of-tree modules [akpm@linux-foundation.org: remove now-unused hugetlb_vma() stub] Signed-off-by: Cliff Wickman <cpw@sgi.com> Reviewed-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> Cc: Mel Gorman <mel@csn.ul.ie> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: Dave Hansen <dave.hansen@intel.com> Cc: David Sterba <dsterba@suse.cz> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: KOSAKI Motohiro <kosaki.motohiro@gmail.com> Cc: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
		
							parent
							
								
									43c523bff7
								
							
						
					
					
						commit
						a9ff785e44
					
				
					 1 changed files with 37 additions and 35 deletions
				
			
		|  | @ -127,28 +127,7 @@ static int walk_hugetlb_range(struct vm_area_struct *vma, | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct vm_area_struct* hugetlb_vma(unsigned long addr, struct mm_walk *walk) |  | ||||||
| { |  | ||||||
| 	struct vm_area_struct *vma; |  | ||||||
| 
 |  | ||||||
| 	/* We don't need vma lookup at all. */ |  | ||||||
| 	if (!walk->hugetlb_entry) |  | ||||||
| 		return NULL; |  | ||||||
| 
 |  | ||||||
| 	VM_BUG_ON(!rwsem_is_locked(&walk->mm->mmap_sem)); |  | ||||||
| 	vma = find_vma(walk->mm, addr); |  | ||||||
| 	if (vma && vma->vm_start <= addr && is_vm_hugetlb_page(vma)) |  | ||||||
| 		return vma; |  | ||||||
| 
 |  | ||||||
| 	return NULL; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #else /* CONFIG_HUGETLB_PAGE */ | #else /* CONFIG_HUGETLB_PAGE */ | ||||||
| static struct vm_area_struct* hugetlb_vma(unsigned long addr, struct mm_walk *walk) |  | ||||||
| { |  | ||||||
| 	return NULL; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static int walk_hugetlb_range(struct vm_area_struct *vma, | static int walk_hugetlb_range(struct vm_area_struct *vma, | ||||||
| 			      unsigned long addr, unsigned long end, | 			      unsigned long addr, unsigned long end, | ||||||
| 			      struct mm_walk *walk) | 			      struct mm_walk *walk) | ||||||
|  | @ -198,30 +177,53 @@ int walk_page_range(unsigned long addr, unsigned long end, | ||||||
| 	if (!walk->mm) | 	if (!walk->mm) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
|  | 	VM_BUG_ON(!rwsem_is_locked(&walk->mm->mmap_sem)); | ||||||
|  | 
 | ||||||
| 	pgd = pgd_offset(walk->mm, addr); | 	pgd = pgd_offset(walk->mm, addr); | ||||||
| 	do { | 	do { | ||||||
| 		struct vm_area_struct *vma; | 		struct vm_area_struct *vma = NULL; | ||||||
| 
 | 
 | ||||||
| 		next = pgd_addr_end(addr, end); | 		next = pgd_addr_end(addr, end); | ||||||
| 
 | 
 | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * handle hugetlb vma individually because pagetable walk for | 		 * This function was not intended to be vma based. | ||||||
| 		 * the hugetlb page is dependent on the architecture and | 		 * But there are vma special cases to be handled: | ||||||
| 		 * we can't handled it in the same manner as non-huge pages. | 		 * - hugetlb vma's | ||||||
|  | 		 * - VM_PFNMAP vma's | ||||||
| 		 */ | 		 */ | ||||||
| 		vma = hugetlb_vma(addr, walk); | 		vma = find_vma(walk->mm, addr); | ||||||
| 		if (vma) { | 		if (vma) { | ||||||
| 			if (vma->vm_end < next) |  | ||||||
| 				next = vma->vm_end; |  | ||||||
| 			/*
 | 			/*
 | ||||||
| 			 * Hugepage is very tightly coupled with vma, so | 			 * There are no page structures backing a VM_PFNMAP | ||||||
| 			 * walk through hugetlb entries within a given vma. | 			 * range, so do not allow split_huge_page_pmd(). | ||||||
| 			 */ | 			 */ | ||||||
| 			err = walk_hugetlb_range(vma, addr, next, walk); | 			if ((vma->vm_start <= addr) && | ||||||
| 			if (err) | 			    (vma->vm_flags & VM_PFNMAP)) { | ||||||
| 				break; | 				next = vma->vm_end; | ||||||
| 			pgd = pgd_offset(walk->mm, next); | 				pgd = pgd_offset(walk->mm, next); | ||||||
| 			continue; | 				continue; | ||||||
|  | 			} | ||||||
|  | 			/*
 | ||||||
|  | 			 * Handle hugetlb vma individually because pagetable | ||||||
|  | 			 * walk for the hugetlb page is dependent on the | ||||||
|  | 			 * architecture and we can't handled it in the same | ||||||
|  | 			 * manner as non-huge pages. | ||||||
|  | 			 */ | ||||||
|  | 			if (walk->hugetlb_entry && (vma->vm_start <= addr) && | ||||||
|  | 			    is_vm_hugetlb_page(vma)) { | ||||||
|  | 				if (vma->vm_end < next) | ||||||
|  | 					next = vma->vm_end; | ||||||
|  | 				/*
 | ||||||
|  | 				 * Hugepage is very tightly coupled with vma, | ||||||
|  | 				 * so walk through hugetlb entries within a | ||||||
|  | 				 * given vma. | ||||||
|  | 				 */ | ||||||
|  | 				err = walk_hugetlb_range(vma, addr, next, walk); | ||||||
|  | 				if (err) | ||||||
|  | 					break; | ||||||
|  | 				pgd = pgd_offset(walk->mm, next); | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (pgd_none_or_clear_bad(pgd)) { | 		if (pgd_none_or_clear_bad(pgd)) { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Cliff Wickman
						Cliff Wickman