diff options
Diffstat (limited to 'mm/hugetlb.c')
| -rw-r--r-- | mm/hugetlb.c | 67 | 
1 files changed, 47 insertions, 20 deletions
diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 67a71191136..ce8cbb29860 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -262,7 +262,7 @@ struct resv_map {  	struct list_head regions;  }; -struct resv_map *resv_map_alloc(void) +static struct resv_map *resv_map_alloc(void)  {  	struct resv_map *resv_map = kmalloc(sizeof(*resv_map), GFP_KERNEL);  	if (!resv_map) @@ -274,7 +274,7 @@ struct resv_map *resv_map_alloc(void)  	return resv_map;  } -void resv_map_release(struct kref *ref) +static void resv_map_release(struct kref *ref)  {  	struct resv_map *resv_map = container_of(ref, struct resv_map, refs); @@ -289,7 +289,7 @@ static struct resv_map *vma_resv_map(struct vm_area_struct *vma)  	if (!(vma->vm_flags & VM_SHARED))  		return (struct resv_map *)(get_vma_private_data(vma) &  							~HPAGE_RESV_MASK); -	return 0; +	return NULL;  }  static void set_vma_resv_map(struct vm_area_struct *vma, struct resv_map *map) @@ -1459,11 +1459,11 @@ int hugetlb_report_meminfo(char *buf)  {  	struct hstate *h = &default_hstate;  	return sprintf(buf, -			"HugePages_Total: %5lu\n" -			"HugePages_Free:  %5lu\n" -			"HugePages_Rsvd:  %5lu\n" -			"HugePages_Surp:  %5lu\n" -			"Hugepagesize:    %5lu kB\n", +			"HugePages_Total:   %5lu\n" +			"HugePages_Free:    %5lu\n" +			"HugePages_Rsvd:    %5lu\n" +			"HugePages_Surp:    %5lu\n" +			"Hugepagesize:   %8lu kB\n",  			h->nr_huge_pages,  			h->free_huge_pages,  			h->resv_huge_pages, @@ -1747,10 +1747,8 @@ void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,   * from other VMAs and let the children be SIGKILLed if they are faulting the   * same region.   */ -int unmap_ref_private(struct mm_struct *mm, -					struct vm_area_struct *vma, -					struct page *page, -					unsigned long address) +static int unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma, +				struct page *page, unsigned long address)  {  	struct vm_area_struct *iter_vma;  	struct address_space *mapping; @@ -2008,7 +2006,7 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,  	entry = huge_ptep_get(ptep);  	if (huge_pte_none(entry)) {  		ret = hugetlb_no_page(mm, vma, address, ptep, write_access); -		goto out_unlock; +		goto out_mutex;  	}  	ret = 0; @@ -2024,7 +2022,7 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,  	if (write_access && !pte_write(entry)) {  		if (vma_needs_reservation(h, vma, address) < 0) {  			ret = VM_FAULT_OOM; -			goto out_unlock; +			goto out_mutex;  		}  		if (!(vma->vm_flags & VM_SHARED)) @@ -2034,10 +2032,23 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,  	spin_lock(&mm->page_table_lock);  	/* Check for a racing update before calling hugetlb_cow */ -	if (likely(pte_same(entry, huge_ptep_get(ptep)))) -		if (write_access && !pte_write(entry)) +	if (unlikely(!pte_same(entry, huge_ptep_get(ptep)))) +		goto out_page_table_lock; + + +	if (write_access) { +		if (!pte_write(entry)) {  			ret = hugetlb_cow(mm, vma, address, ptep, entry,  							pagecache_page); +			goto out_page_table_lock; +		} +		entry = pte_mkdirty(entry); +	} +	entry = pte_mkyoung(entry); +	if (huge_ptep_set_access_flags(vma, address, ptep, entry, write_access)) +		update_mmu_cache(vma, address, entry); + +out_page_table_lock:  	spin_unlock(&mm->page_table_lock);  	if (pagecache_page) { @@ -2045,7 +2056,7 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,  		put_page(pagecache_page);  	} -out_unlock: +out_mutex:  	mutex_unlock(&hugetlb_instantiation_mutex);  	return ret; @@ -2060,6 +2071,14 @@ follow_huge_pud(struct mm_struct *mm, unsigned long address,  	return NULL;  } +static int huge_zeropage_ok(pte_t *ptep, int write, int shared) +{ +	if (!ptep || write || shared) +		return 0; +	else +		return huge_pte_none(huge_ptep_get(ptep)); +} +  int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,  			struct page **pages, struct vm_area_struct **vmas,  			unsigned long *position, int *length, int i, @@ -2069,6 +2088,8 @@ int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,  	unsigned long vaddr = *position;  	int remainder = *length;  	struct hstate *h = hstate_vma(vma); +	int zeropage_ok = 0; +	int shared = vma->vm_flags & VM_SHARED;  	spin_lock(&mm->page_table_lock);  	while (vaddr < vma->vm_end && remainder) { @@ -2081,8 +2102,11 @@ int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,  		 * first, for the page indexing below to work.  		 */  		pte = huge_pte_offset(mm, vaddr & huge_page_mask(h)); +		if (huge_zeropage_ok(pte, write, shared)) +			zeropage_ok = 1; -		if (!pte || huge_pte_none(huge_ptep_get(pte)) || +		if (!pte || +		    (huge_pte_none(huge_ptep_get(pte)) && !zeropage_ok) ||  		    (write && !pte_write(huge_ptep_get(pte)))) {  			int ret; @@ -2102,8 +2126,11 @@ int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,  		page = pte_page(huge_ptep_get(pte));  same_page:  		if (pages) { -			get_page(page); -			pages[i] = page + pfn_offset; +			if (zeropage_ok) +				pages[i] = ZERO_PAGE(0); +			else +				pages[i] = page + pfn_offset; +			get_page(pages[i]);  		}  		if (vmas)  |