xref: /openbmc/linux/fs/proc/task_nommu.c (revision fe441980)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds 
31da177e4SLinus Torvalds #include <linux/mm.h>
41da177e4SLinus Torvalds #include <linux/file.h>
5eb28062fSBryan Wu #include <linux/fdtable.h>
65ad4e53bSAl Viro #include <linux/fs_struct.h>
71da177e4SLinus Torvalds #include <linux/mount.h>
85096add8SKees Cook #include <linux/ptrace.h>
95a0e3ad6STejun Heo #include <linux/slab.h>
101da177e4SLinus Torvalds #include <linux/seq_file.h>
116e84f315SIngo Molnar #include <linux/sched/mm.h>
126e84f315SIngo Molnar 
131da177e4SLinus Torvalds #include "internal.h"
141da177e4SLinus Torvalds 
151da177e4SLinus Torvalds /*
161da177e4SLinus Torvalds  * Logic: we've got two memory sums for each process, "shared", and
17025dfdafSFrederik Schwarzer  * "non-shared". Shared memory may get counted more than once, for
181da177e4SLinus Torvalds  * each process that owns it. Non-shared memory is counted
191da177e4SLinus Torvalds  * accurately.
201da177e4SLinus Torvalds  */
task_mem(struct seq_file * m,struct mm_struct * mm)21df5f8314SEric W. Biederman void task_mem(struct seq_file *m, struct mm_struct *mm)
221da177e4SLinus Torvalds {
230c563f14SMatthew Wilcox (Oracle) 	VMA_ITERATOR(vmi, mm, 0);
248feae131SDavid Howells 	struct vm_area_struct *vma;
2538f71479SDavid Howells 	struct vm_region *region;
2638f71479SDavid Howells 	unsigned long bytes = 0, sbytes = 0, slack = 0, size;
271da177e4SLinus Torvalds 
28d8ed45c5SMichel Lespinasse 	mmap_read_lock(mm);
290c563f14SMatthew Wilcox (Oracle) 	for_each_vma(vmi, vma) {
308feae131SDavid Howells 		bytes += kobjsize(vma);
3138f71479SDavid Howells 
3238f71479SDavid Howells 		region = vma->vm_region;
3338f71479SDavid Howells 		if (region) {
3438f71479SDavid Howells 			size = kobjsize(region);
3538f71479SDavid Howells 			size += region->vm_end - region->vm_start;
361da177e4SLinus Torvalds 		} else {
3738f71479SDavid Howells 			size = vma->vm_end - vma->vm_start;
3838f71479SDavid Howells 		}
3938f71479SDavid Howells 
4038f71479SDavid Howells 		if (atomic_read(&mm->mm_count) > 1 ||
41fc4f4be9SDavid Hildenbrand 		    is_nommu_shared_mapping(vma->vm_flags)) {
4238f71479SDavid Howells 			sbytes += size;
4338f71479SDavid Howells 		} else {
4438f71479SDavid Howells 			bytes += size;
4538f71479SDavid Howells 			if (region)
4638f71479SDavid Howells 				slack = region->vm_end - vma->vm_end;
471da177e4SLinus Torvalds 		}
481da177e4SLinus Torvalds 	}
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds 	if (atomic_read(&mm->mm_count) > 1)
511da177e4SLinus Torvalds 		sbytes += kobjsize(mm);
521da177e4SLinus Torvalds 	else
531da177e4SLinus Torvalds 		bytes += kobjsize(mm);
541da177e4SLinus Torvalds 
55498052bbSAl Viro 	if (current->fs && current->fs->users > 1)
561da177e4SLinus Torvalds 		sbytes += kobjsize(current->fs);
571da177e4SLinus Torvalds 	else
581da177e4SLinus Torvalds 		bytes += kobjsize(current->fs);
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds 	if (current->files && atomic_read(&current->files->count) > 1)
611da177e4SLinus Torvalds 		sbytes += kobjsize(current->files);
621da177e4SLinus Torvalds 	else
631da177e4SLinus Torvalds 		bytes += kobjsize(current->files);
641da177e4SLinus Torvalds 
65d036bda7SElena Reshetova 	if (current->sighand && refcount_read(&current->sighand->count) > 1)
661da177e4SLinus Torvalds 		sbytes += kobjsize(current->sighand);
671da177e4SLinus Torvalds 	else
681da177e4SLinus Torvalds 		bytes += kobjsize(current->sighand);
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds 	bytes += kobjsize(current); /* includes kernel stack */
711da177e4SLinus Torvalds 
72341d51c8Slipeifeng 	mmap_read_unlock(mm);
73341d51c8Slipeifeng 
74df5f8314SEric W. Biederman 	seq_printf(m,
751da177e4SLinus Torvalds 		"Mem:\t%8lu bytes\n"
761da177e4SLinus Torvalds 		"Slack:\t%8lu bytes\n"
771da177e4SLinus Torvalds 		"Shared:\t%8lu bytes\n",
781da177e4SLinus Torvalds 		bytes, slack, sbytes);
791da177e4SLinus Torvalds }
801da177e4SLinus Torvalds 
task_vsize(struct mm_struct * mm)811da177e4SLinus Torvalds unsigned long task_vsize(struct mm_struct *mm)
821da177e4SLinus Torvalds {
830c563f14SMatthew Wilcox (Oracle) 	VMA_ITERATOR(vmi, mm, 0);
848feae131SDavid Howells 	struct vm_area_struct *vma;
851da177e4SLinus Torvalds 	unsigned long vsize = 0;
861da177e4SLinus Torvalds 
87d8ed45c5SMichel Lespinasse 	mmap_read_lock(mm);
880c563f14SMatthew Wilcox (Oracle) 	for_each_vma(vmi, vma)
8938f71479SDavid Howells 		vsize += vma->vm_end - vma->vm_start;
90d8ed45c5SMichel Lespinasse 	mmap_read_unlock(mm);
911da177e4SLinus Torvalds 	return vsize;
921da177e4SLinus Torvalds }
931da177e4SLinus Torvalds 
task_statm(struct mm_struct * mm,unsigned long * shared,unsigned long * text,unsigned long * data,unsigned long * resident)94a2ade7b6SAlexey Dobriyan unsigned long task_statm(struct mm_struct *mm,
95a2ade7b6SAlexey Dobriyan 			 unsigned long *shared, unsigned long *text,
96a2ade7b6SAlexey Dobriyan 			 unsigned long *data, unsigned long *resident)
971da177e4SLinus Torvalds {
980c563f14SMatthew Wilcox (Oracle) 	VMA_ITERATOR(vmi, mm, 0);
998feae131SDavid Howells 	struct vm_area_struct *vma;
10038f71479SDavid Howells 	struct vm_region *region;
101a2ade7b6SAlexey Dobriyan 	unsigned long size = kobjsize(mm);
1021da177e4SLinus Torvalds 
103d8ed45c5SMichel Lespinasse 	mmap_read_lock(mm);
1040c563f14SMatthew Wilcox (Oracle) 	for_each_vma(vmi, vma) {
1058feae131SDavid Howells 		size += kobjsize(vma);
10638f71479SDavid Howells 		region = vma->vm_region;
10738f71479SDavid Howells 		if (region) {
10838f71479SDavid Howells 			size += kobjsize(region);
10938f71479SDavid Howells 			size += region->vm_end - region->vm_start;
11038f71479SDavid Howells 		}
1111da177e4SLinus Torvalds 	}
1121da177e4SLinus Torvalds 
1137e1e0ef2SSteven J. Magnani 	*text = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK))
1147e1e0ef2SSteven J. Magnani 		>> PAGE_SHIFT;
1157e1e0ef2SSteven J. Magnani 	*data = (PAGE_ALIGN(mm->start_stack) - (mm->start_data & PAGE_MASK))
1167e1e0ef2SSteven J. Magnani 		>> PAGE_SHIFT;
117d8ed45c5SMichel Lespinasse 	mmap_read_unlock(mm);
1187e1e0ef2SSteven J. Magnani 	size >>= PAGE_SHIFT;
1197e1e0ef2SSteven J. Magnani 	size += *text + *data;
1201da177e4SLinus Torvalds 	*resident = size;
1211da177e4SLinus Torvalds 	return size;
1221da177e4SLinus Torvalds }
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds /*
1258feae131SDavid Howells  * display a single VMA to a sequenced file
1268feae131SDavid Howells  */
nommu_vma_show(struct seq_file * m,struct vm_area_struct * vma)127871305bbSVlastimil Babka static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma)
1288feae131SDavid Howells {
1293c26c9d9SMike Frysinger 	struct mm_struct *mm = vma->vm_mm;
1308feae131SDavid Howells 	unsigned long ino = 0;
1318feae131SDavid Howells 	struct file *file;
1328feae131SDavid Howells 	dev_t dev = 0;
133652586dfSTetsuo Handa 	int flags;
1346260a4b0SKAMEZAWA Hiroyuki 	unsigned long long pgoff = 0;
1358feae131SDavid Howells 
1368feae131SDavid Howells 	flags = vma->vm_flags;
1378feae131SDavid Howells 	file = vma->vm_file;
1388feae131SDavid Howells 
1398feae131SDavid Howells 	if (file) {
140496ad9aaSAl Viro 		struct inode *inode = file_inode(vma->vm_file);
1418feae131SDavid Howells 		dev = inode->i_sb->s_dev;
1428feae131SDavid Howells 		ino = inode->i_ino;
1434c967291SNobuhiro Iwamatsu 		pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT;
1448feae131SDavid Howells 	}
1458feae131SDavid Howells 
146652586dfSTetsuo Handa 	seq_setwidth(m, 25 + sizeof(void *) * 6 - 1);
1478feae131SDavid Howells 	seq_printf(m,
148652586dfSTetsuo Handa 		   "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ",
1498feae131SDavid Howells 		   vma->vm_start,
1508feae131SDavid Howells 		   vma->vm_end,
1518feae131SDavid Howells 		   flags & VM_READ ? 'r' : '-',
1528feae131SDavid Howells 		   flags & VM_WRITE ? 'w' : '-',
1538feae131SDavid Howells 		   flags & VM_EXEC ? 'x' : '-',
1548feae131SDavid Howells 		   flags & VM_MAYSHARE ? flags & VM_SHARED ? 'S' : 's' : 'p',
1556260a4b0SKAMEZAWA Hiroyuki 		   pgoff,
156652586dfSTetsuo Handa 		   MAJOR(dev), MINOR(dev), ino);
1578feae131SDavid Howells 
1588feae131SDavid Howells 	if (file) {
159652586dfSTetsuo Handa 		seq_pad(m, ' ');
1602726d566SMiklos Szeredi 		seq_file_path(m, file, "");
16111250fd1SKefeng Wang 	} else if (mm && vma_is_initial_stack(vma)) {
162652586dfSTetsuo Handa 		seq_pad(m, ' ');
16308b55775SAlexey Dobriyan 		seq_puts(m, "[stack]");
1648feae131SDavid Howells 	}
1658feae131SDavid Howells 
1668feae131SDavid Howells 	seq_putc(m, '\n');
1678feae131SDavid Howells 	return 0;
1688feae131SDavid Howells }
1698feae131SDavid Howells 
1708feae131SDavid Howells /*
171dbf8685cSDavid Howells  * display mapping lines for a particular process's /proc/pid/maps
1721da177e4SLinus Torvalds  */
show_map(struct seq_file * m,void * _p)173871305bbSVlastimil Babka static int show_map(struct seq_file *m, void *_p)
1741da177e4SLinus Torvalds {
1750c563f14SMatthew Wilcox (Oracle) 	return nommu_vma_show(m, _p);
1761da177e4SLinus Torvalds }
177dbf8685cSDavid Howells 
proc_get_vma(struct proc_maps_private * priv,loff_t * ppos)178*fe441980SBen Wolsieffer static struct vm_area_struct *proc_get_vma(struct proc_maps_private *priv,
179*fe441980SBen Wolsieffer 						loff_t *ppos)
180*fe441980SBen Wolsieffer {
181*fe441980SBen Wolsieffer 	struct vm_area_struct *vma = vma_next(&priv->iter);
182*fe441980SBen Wolsieffer 
183*fe441980SBen Wolsieffer 	if (vma) {
184*fe441980SBen Wolsieffer 		*ppos = vma->vm_start;
185*fe441980SBen Wolsieffer 	} else {
186*fe441980SBen Wolsieffer 		*ppos = -1UL;
187*fe441980SBen Wolsieffer 	}
188*fe441980SBen Wolsieffer 
189*fe441980SBen Wolsieffer 	return vma;
190*fe441980SBen Wolsieffer }
191*fe441980SBen Wolsieffer 
m_start(struct seq_file * m,loff_t * ppos)192*fe441980SBen Wolsieffer static void *m_start(struct seq_file *m, loff_t *ppos)
1931da177e4SLinus Torvalds {
194dbf8685cSDavid Howells 	struct proc_maps_private *priv = m->private;
195*fe441980SBen Wolsieffer 	unsigned long last_addr = *ppos;
196dbf8685cSDavid Howells 	struct mm_struct *mm;
1970c563f14SMatthew Wilcox (Oracle) 
198*fe441980SBen Wolsieffer 	/* See proc_get_vma(). Zero at the start or after lseek. */
199*fe441980SBen Wolsieffer 	if (last_addr == -1UL)
2000c563f14SMatthew Wilcox (Oracle) 		return NULL;
201dbf8685cSDavid Howells 
202dbf8685cSDavid Howells 	/* pin the task and mm whilst we play with them */
2032c03376dSOleg Nesterov 	priv->task = get_proc_task(priv->inode);
204dbf8685cSDavid Howells 	if (!priv->task)
205ec6fd8a4SAl Viro 		return ERR_PTR(-ESRCH);
206dbf8685cSDavid Howells 
20727692cd5SOleg Nesterov 	mm = priv->mm;
208578d7699SBen Wolsieffer 	if (!mm || !mmget_not_zero(mm)) {
209578d7699SBen Wolsieffer 		put_task_struct(priv->task);
210578d7699SBen Wolsieffer 		priv->task = NULL;
21127692cd5SOleg Nesterov 		return NULL;
212578d7699SBen Wolsieffer 	}
213dbf8685cSDavid Howells 
214d8ed45c5SMichel Lespinasse 	if (mmap_read_lock_killable(mm)) {
2158a713e7dSKonstantin Khlebnikov 		mmput(mm);
216578d7699SBen Wolsieffer 		put_task_struct(priv->task);
217578d7699SBen Wolsieffer 		priv->task = NULL;
2188a713e7dSKonstantin Khlebnikov 		return ERR_PTR(-EINTR);
2198a713e7dSKonstantin Khlebnikov 	}
2208a713e7dSKonstantin Khlebnikov 
221*fe441980SBen Wolsieffer 	vma_iter_init(&priv->iter, mm, last_addr);
22247fecca1SOleg Nesterov 
223*fe441980SBen Wolsieffer 	return proc_get_vma(priv, ppos);
2241da177e4SLinus Torvalds }
225dbf8685cSDavid Howells 
m_stop(struct seq_file * m,void * v)226578d7699SBen Wolsieffer static void m_stop(struct seq_file *m, void *v)
227dbf8685cSDavid Howells {
228dbf8685cSDavid Howells 	struct proc_maps_private *priv = m->private;
229578d7699SBen Wolsieffer 	struct mm_struct *mm = priv->mm;
230dbf8685cSDavid Howells 
231578d7699SBen Wolsieffer 	if (!priv->task)
232578d7699SBen Wolsieffer 		return;
233578d7699SBen Wolsieffer 
234578d7699SBen Wolsieffer 	mmap_read_unlock(mm);
235578d7699SBen Wolsieffer 	mmput(mm);
236dbf8685cSDavid Howells 	put_task_struct(priv->task);
23747fecca1SOleg Nesterov 	priv->task = NULL;
238dbf8685cSDavid Howells }
239dbf8685cSDavid Howells 
m_next(struct seq_file * m,void * _p,loff_t * ppos)240*fe441980SBen Wolsieffer static void *m_next(struct seq_file *m, void *_p, loff_t *ppos)
241dbf8685cSDavid Howells {
242*fe441980SBen Wolsieffer 	return proc_get_vma(m->private, ppos);
243dbf8685cSDavid Howells }
244dbf8685cSDavid Howells 
24503a44825SJan Engelhardt static const struct seq_operations proc_pid_maps_ops = {
2461da177e4SLinus Torvalds 	.start	= m_start,
2471da177e4SLinus Torvalds 	.next	= m_next,
2481da177e4SLinus Torvalds 	.stop	= m_stop,
249871305bbSVlastimil Babka 	.show	= show_map
250b7643757SSiddhesh Poyarekar };
251b7643757SSiddhesh Poyarekar 
maps_open(struct inode * inode,struct file * file,const struct seq_operations * ops)252b7643757SSiddhesh Poyarekar static int maps_open(struct inode *inode, struct file *file,
253b7643757SSiddhesh Poyarekar 		     const struct seq_operations *ops)
254662795deSEric W. Biederman {
255dbf8685cSDavid Howells 	struct proc_maps_private *priv;
256dbf8685cSDavid Howells 
25727692cd5SOleg Nesterov 	priv = __seq_open_private(file, ops, sizeof(*priv));
258ce34fddbSOleg Nesterov 	if (!priv)
259ce34fddbSOleg Nesterov 		return -ENOMEM;
260ce34fddbSOleg Nesterov 
2612c03376dSOleg Nesterov 	priv->inode = inode;
26227692cd5SOleg Nesterov 	priv->mm = proc_mem_open(inode, PTRACE_MODE_READ);
26327692cd5SOleg Nesterov 	if (IS_ERR(priv->mm)) {
26427692cd5SOleg Nesterov 		int err = PTR_ERR(priv->mm);
26527692cd5SOleg Nesterov 
26627692cd5SOleg Nesterov 		seq_release_private(inode, file);
26727692cd5SOleg Nesterov 		return err;
26827692cd5SOleg Nesterov 	}
26927692cd5SOleg Nesterov 
270ce34fddbSOleg Nesterov 	return 0;
271662795deSEric W. Biederman }
272662795deSEric W. Biederman 
27327692cd5SOleg Nesterov 
map_release(struct inode * inode,struct file * file)27427692cd5SOleg Nesterov static int map_release(struct inode *inode, struct file *file)
27527692cd5SOleg Nesterov {
27627692cd5SOleg Nesterov 	struct seq_file *seq = file->private_data;
27727692cd5SOleg Nesterov 	struct proc_maps_private *priv = seq->private;
27827692cd5SOleg Nesterov 
27927692cd5SOleg Nesterov 	if (priv->mm)
28027692cd5SOleg Nesterov 		mmdrop(priv->mm);
28127692cd5SOleg Nesterov 
28227692cd5SOleg Nesterov 	return seq_release_private(inode, file);
28327692cd5SOleg Nesterov }
28427692cd5SOleg Nesterov 
pid_maps_open(struct inode * inode,struct file * file)285b7643757SSiddhesh Poyarekar static int pid_maps_open(struct inode *inode, struct file *file)
286b7643757SSiddhesh Poyarekar {
287b7643757SSiddhesh Poyarekar 	return maps_open(inode, file, &proc_pid_maps_ops);
288b7643757SSiddhesh Poyarekar }
289b7643757SSiddhesh Poyarekar 
290b7643757SSiddhesh Poyarekar const struct file_operations proc_pid_maps_operations = {
291b7643757SSiddhesh Poyarekar 	.open		= pid_maps_open,
292b7643757SSiddhesh Poyarekar 	.read		= seq_read,
293b7643757SSiddhesh Poyarekar 	.llseek		= seq_lseek,
29427692cd5SOleg Nesterov 	.release	= map_release,
295b7643757SSiddhesh Poyarekar };
296b7643757SSiddhesh Poyarekar 
297