diff options
Diffstat (limited to 'fs/proc/task_mmu.c')
| -rw-r--r-- | fs/proc/task_mmu.c | 80 | 
1 files changed, 55 insertions, 25 deletions
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 25b6a887adb..5afaa58a863 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -877,30 +877,54 @@ struct numa_maps_private {  	struct numa_maps md;  }; -static void gather_stats(struct page *page, struct numa_maps *md, int pte_dirty) +static void gather_stats(struct page *page, struct numa_maps *md, int pte_dirty, +			unsigned long nr_pages)  {  	int count = page_mapcount(page); -	md->pages++; +	md->pages += nr_pages;  	if (pte_dirty || PageDirty(page)) -		md->dirty++; +		md->dirty += nr_pages;  	if (PageSwapCache(page)) -		md->swapcache++; +		md->swapcache += nr_pages;  	if (PageActive(page) || PageUnevictable(page)) -		md->active++; +		md->active += nr_pages;  	if (PageWriteback(page)) -		md->writeback++; +		md->writeback += nr_pages;  	if (PageAnon(page)) -		md->anon++; +		md->anon += nr_pages;  	if (count > md->mapcount_max)  		md->mapcount_max = count; -	md->node[page_to_nid(page)]++; +	md->node[page_to_nid(page)] += nr_pages; +} + +static struct page *can_gather_numa_stats(pte_t pte, struct vm_area_struct *vma, +		unsigned long addr) +{ +	struct page *page; +	int nid; + +	if (!pte_present(pte)) +		return NULL; + +	page = vm_normal_page(vma, addr, pte); +	if (!page) +		return NULL; + +	if (PageReserved(page)) +		return NULL; + +	nid = page_to_nid(page); +	if (!node_isset(nid, node_states[N_HIGH_MEMORY])) +		return NULL; + +	return page;  }  static int gather_pte_stats(pmd_t *pmd, unsigned long addr, @@ -912,26 +936,32 @@ static int gather_pte_stats(pmd_t *pmd, unsigned long addr,  	pte_t *pte;  	md = walk->private; -	orig_pte = pte = pte_offset_map_lock(walk->mm, pmd, addr, &ptl); -	do { -		struct page *page; -		int nid; +	spin_lock(&walk->mm->page_table_lock); +	if (pmd_trans_huge(*pmd)) { +		if (pmd_trans_splitting(*pmd)) { +			spin_unlock(&walk->mm->page_table_lock); +			wait_split_huge_page(md->vma->anon_vma, pmd); +		} else { +			pte_t huge_pte = *(pte_t *)pmd; +			struct page *page; -		if (!pte_present(*pte)) -			continue; +			page = can_gather_numa_stats(huge_pte, md->vma, addr); +			if (page) +				gather_stats(page, md, pte_dirty(huge_pte), +						HPAGE_PMD_SIZE/PAGE_SIZE); +			spin_unlock(&walk->mm->page_table_lock); +			return 0; +		} +	} else { +		spin_unlock(&walk->mm->page_table_lock); +	} -		page = vm_normal_page(md->vma, addr, *pte); +	orig_pte = pte = pte_offset_map_lock(walk->mm, pmd, addr, &ptl); +	do { +		struct page *page = can_gather_numa_stats(*pte, md->vma, addr);  		if (!page)  			continue; - -		if (PageReserved(page)) -			continue; - -		nid = page_to_nid(page); -		if (!node_isset(nid, node_states[N_HIGH_MEMORY])) -			continue; - -		gather_stats(page, md, pte_dirty(*pte)); +		gather_stats(page, md, pte_dirty(*pte), 1);  	} while (pte++, addr += PAGE_SIZE, addr != end);  	pte_unmap_unlock(orig_pte, ptl); @@ -952,7 +982,7 @@ static int gather_hugetbl_stats(pte_t *pte, unsigned long hmask,  		return 0;  	md = walk->private; -	gather_stats(page, md, pte_dirty(*pte)); +	gather_stats(page, md, pte_dirty(*pte), 1);  	return 0;  }  |