diff options
Diffstat (limited to 'fs/proc/task_mmu.c')
| -rw-r--r-- | fs/proc/task_mmu.c | 245 | 
1 files changed, 223 insertions, 22 deletions
| diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 2e7addfd980..25b6a887adb 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -211,10 +211,10 @@ static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma)  {  	struct mm_struct *mm = vma->vm_mm;  	struct file *file = vma->vm_file; -	int flags = vma->vm_flags; +	vm_flags_t flags = vma->vm_flags;  	unsigned long ino = 0;  	unsigned long long pgoff = 0; -	unsigned long start; +	unsigned long start, end;  	dev_t dev = 0;  	int len; @@ -227,13 +227,15 @@ static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma)  	/* We don't show the stack guard page in /proc/maps */  	start = vma->vm_start; -	if (vma->vm_flags & VM_GROWSDOWN) -		if (!vma_stack_continue(vma->vm_prev, vma->vm_start)) -			start += PAGE_SIZE; +	if (stack_guard_page_start(vma, start)) +		start += PAGE_SIZE; +	end = vma->vm_end; +	if (stack_guard_page_end(vma, end)) +		end -= PAGE_SIZE;  	seq_printf(m, "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu %n",  			start, -			vma->vm_end, +			end,  			flags & VM_READ ? 'r' : '-',  			flags & VM_WRITE ? 'w' : '-',  			flags & VM_EXEC ? 'x' : '-', @@ -534,15 +536,17 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf,  	char buffer[PROC_NUMBUF];  	struct mm_struct *mm;  	struct vm_area_struct *vma; -	long type; +	int type; +	int rv;  	memset(buffer, 0, sizeof(buffer));  	if (count > sizeof(buffer) - 1)  		count = sizeof(buffer) - 1;  	if (copy_from_user(buffer, buf, count))  		return -EFAULT; -	if (strict_strtol(strstrip(buffer), 10, &type)) -		return -EINVAL; +	rv = kstrtoint(strstrip(buffer), 10, &type); +	if (rv < 0) +		return rv;  	if (type < CLEAR_REFS_ALL || type > CLEAR_REFS_MAPPED)  		return -EINVAL;  	task = get_proc_task(file->f_path.dentry->d_inode); @@ -767,18 +771,12 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,  	if (!task)  		goto out; -	mm = mm_for_maps(task); -	ret = PTR_ERR(mm); -	if (!mm || IS_ERR(mm)) -		goto out_task; -  	ret = -EINVAL;  	/* file position must be aligned */  	if ((*ppos % PM_ENTRY_BYTES) || (count % PM_ENTRY_BYTES))  		goto out_task;  	ret = 0; -  	if (!count)  		goto out_task; @@ -786,7 +784,12 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,  	pm.buffer = kmalloc(pm.len, GFP_TEMPORARY);  	ret = -ENOMEM;  	if (!pm.buffer) -		goto out_mm; +		goto out_task; + +	mm = mm_for_maps(task); +	ret = PTR_ERR(mm); +	if (!mm || IS_ERR(mm)) +		goto out_free;  	pagemap_walk.pmd_entry = pagemap_pte_range;  	pagemap_walk.pte_hole = pagemap_pte_hole; @@ -829,7 +832,7 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,  		len = min(count, PM_ENTRY_BYTES * pm.pos);  		if (copy_to_user(buf, pm.buffer, len)) {  			ret = -EFAULT; -			goto out_free; +			goto out_mm;  		}  		copied += len;  		buf += len; @@ -839,10 +842,10 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,  	if (!ret || ret == PM_END_OF_BUFFER)  		ret = copied; -out_free: -	kfree(pm.buffer);  out_mm:  	mmput(mm); +out_free: +	kfree(pm.buffer);  out_task:  	put_task_struct(task);  out: @@ -856,7 +859,192 @@ const struct file_operations proc_pagemap_operations = {  #endif /* CONFIG_PROC_PAGE_MONITOR */  #ifdef CONFIG_NUMA -extern int show_numa_map(struct seq_file *m, void *v); + +struct numa_maps { +	struct vm_area_struct *vma; +	unsigned long pages; +	unsigned long anon; +	unsigned long active; +	unsigned long writeback; +	unsigned long mapcount_max; +	unsigned long dirty; +	unsigned long swapcache; +	unsigned long node[MAX_NUMNODES]; +}; + +struct numa_maps_private { +	struct proc_maps_private proc_maps; +	struct numa_maps md; +}; + +static void gather_stats(struct page *page, struct numa_maps *md, int pte_dirty) +{ +	int count = page_mapcount(page); + +	md->pages++; +	if (pte_dirty || PageDirty(page)) +		md->dirty++; + +	if (PageSwapCache(page)) +		md->swapcache++; + +	if (PageActive(page) || PageUnevictable(page)) +		md->active++; + +	if (PageWriteback(page)) +		md->writeback++; + +	if (PageAnon(page)) +		md->anon++; + +	if (count > md->mapcount_max) +		md->mapcount_max = count; + +	md->node[page_to_nid(page)]++; +} + +static int gather_pte_stats(pmd_t *pmd, unsigned long addr, +		unsigned long end, struct mm_walk *walk) +{ +	struct numa_maps *md; +	spinlock_t *ptl; +	pte_t *orig_pte; +	pte_t *pte; + +	md = walk->private; +	orig_pte = pte = pte_offset_map_lock(walk->mm, pmd, addr, &ptl); +	do { +		struct page *page; +		int nid; + +		if (!pte_present(*pte)) +			continue; + +		page = vm_normal_page(md->vma, addr, *pte); +		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)); + +	} while (pte++, addr += PAGE_SIZE, addr != end); +	pte_unmap_unlock(orig_pte, ptl); +	return 0; +} +#ifdef CONFIG_HUGETLB_PAGE +static int gather_hugetbl_stats(pte_t *pte, unsigned long hmask, +		unsigned long addr, unsigned long end, struct mm_walk *walk) +{ +	struct numa_maps *md; +	struct page *page; + +	if (pte_none(*pte)) +		return 0; + +	page = pte_page(*pte); +	if (!page) +		return 0; + +	md = walk->private; +	gather_stats(page, md, pte_dirty(*pte)); +	return 0; +} + +#else +static int gather_hugetbl_stats(pte_t *pte, unsigned long hmask, +		unsigned long addr, unsigned long end, struct mm_walk *walk) +{ +	return 0; +} +#endif + +/* + * Display pages allocated per node and memory policy via /proc. + */ +static int show_numa_map(struct seq_file *m, void *v) +{ +	struct numa_maps_private *numa_priv = m->private; +	struct proc_maps_private *proc_priv = &numa_priv->proc_maps; +	struct vm_area_struct *vma = v; +	struct numa_maps *md = &numa_priv->md; +	struct file *file = vma->vm_file; +	struct mm_struct *mm = vma->vm_mm; +	struct mm_walk walk = {}; +	struct mempolicy *pol; +	int n; +	char buffer[50]; + +	if (!mm) +		return 0; + +	/* Ensure we start with an empty set of numa_maps statistics. */ +	memset(md, 0, sizeof(*md)); + +	md->vma = vma; + +	walk.hugetlb_entry = gather_hugetbl_stats; +	walk.pmd_entry = gather_pte_stats; +	walk.private = md; +	walk.mm = mm; + +	pol = get_vma_policy(proc_priv->task, vma, vma->vm_start); +	mpol_to_str(buffer, sizeof(buffer), pol, 0); +	mpol_cond_put(pol); + +	seq_printf(m, "%08lx %s", vma->vm_start, buffer); + +	if (file) { +		seq_printf(m, " file="); +		seq_path(m, &file->f_path, "\n\t= "); +	} else if (vma->vm_start <= mm->brk && vma->vm_end >= mm->start_brk) { +		seq_printf(m, " heap"); +	} else if (vma->vm_start <= mm->start_stack && +			vma->vm_end >= mm->start_stack) { +		seq_printf(m, " stack"); +	} + +	walk_page_range(vma->vm_start, vma->vm_end, &walk); + +	if (!md->pages) +		goto out; + +	if (md->anon) +		seq_printf(m, " anon=%lu", md->anon); + +	if (md->dirty) +		seq_printf(m, " dirty=%lu", md->dirty); + +	if (md->pages != md->anon && md->pages != md->dirty) +		seq_printf(m, " mapped=%lu", md->pages); + +	if (md->mapcount_max > 1) +		seq_printf(m, " mapmax=%lu", md->mapcount_max); + +	if (md->swapcache) +		seq_printf(m, " swapcache=%lu", md->swapcache); + +	if (md->active < md->pages && !is_vm_hugetlb_page(vma)) +		seq_printf(m, " active=%lu", md->active); + +	if (md->writeback) +		seq_printf(m, " writeback=%lu", md->writeback); + +	for_each_node_state(n, N_HIGH_MEMORY) +		if (md->node[n]) +			seq_printf(m, " N%d=%lu", n, md->node[n]); +out: +	seq_putc(m, '\n'); + +	if (m->count < m->size) +		m->version = (vma != proc_priv->tail_vma) ? vma->vm_start : 0; +	return 0; +}  static const struct seq_operations proc_pid_numa_maps_op = {          .start  = m_start, @@ -867,7 +1055,20 @@ static const struct seq_operations proc_pid_numa_maps_op = {  static int numa_maps_open(struct inode *inode, struct file *file)  { -	return do_maps_open(inode, file, &proc_pid_numa_maps_op); +	struct numa_maps_private *priv; +	int ret = -ENOMEM; +	priv = kzalloc(sizeof(*priv), GFP_KERNEL); +	if (priv) { +		priv->proc_maps.pid = proc_pid(inode); +		ret = seq_open(file, &proc_pid_numa_maps_op); +		if (!ret) { +			struct seq_file *m = file->private_data; +			m->private = priv; +		} else { +			kfree(priv); +		} +	} +	return ret;  }  const struct file_operations proc_numa_maps_operations = { @@ -876,4 +1077,4 @@ const struct file_operations proc_numa_maps_operations = {  	.llseek		= seq_lseek,  	.release	= seq_release_private,  }; -#endif +#endif /* CONFIG_NUMA */ | 
