162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/mm.h> 462306a36Sopenharmony_ci#include <linux/file.h> 562306a36Sopenharmony_ci#include <linux/fdtable.h> 662306a36Sopenharmony_ci#include <linux/fs_struct.h> 762306a36Sopenharmony_ci#include <linux/mount.h> 862306a36Sopenharmony_ci#include <linux/ptrace.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/seq_file.h> 1162306a36Sopenharmony_ci#include <linux/sched/mm.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "internal.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* 1662306a36Sopenharmony_ci * Logic: we've got two memory sums for each process, "shared", and 1762306a36Sopenharmony_ci * "non-shared". Shared memory may get counted more than once, for 1862306a36Sopenharmony_ci * each process that owns it. Non-shared memory is counted 1962306a36Sopenharmony_ci * accurately. 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_civoid task_mem(struct seq_file *m, struct mm_struct *mm) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci VMA_ITERATOR(vmi, mm, 0); 2462306a36Sopenharmony_ci struct vm_area_struct *vma; 2562306a36Sopenharmony_ci struct vm_region *region; 2662306a36Sopenharmony_ci unsigned long bytes = 0, sbytes = 0, slack = 0, size; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci mmap_read_lock(mm); 2962306a36Sopenharmony_ci for_each_vma(vmi, vma) { 3062306a36Sopenharmony_ci bytes += kobjsize(vma); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci region = vma->vm_region; 3362306a36Sopenharmony_ci if (region) { 3462306a36Sopenharmony_ci size = kobjsize(region); 3562306a36Sopenharmony_ci size += region->vm_end - region->vm_start; 3662306a36Sopenharmony_ci } else { 3762306a36Sopenharmony_ci size = vma->vm_end - vma->vm_start; 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (atomic_read(&mm->mm_count) > 1 || 4162306a36Sopenharmony_ci is_nommu_shared_mapping(vma->vm_flags)) { 4262306a36Sopenharmony_ci sbytes += size; 4362306a36Sopenharmony_ci } else { 4462306a36Sopenharmony_ci bytes += size; 4562306a36Sopenharmony_ci if (region) 4662306a36Sopenharmony_ci slack = region->vm_end - vma->vm_end; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (atomic_read(&mm->mm_count) > 1) 5162306a36Sopenharmony_ci sbytes += kobjsize(mm); 5262306a36Sopenharmony_ci else 5362306a36Sopenharmony_ci bytes += kobjsize(mm); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (current->fs && current->fs->users > 1) 5662306a36Sopenharmony_ci sbytes += kobjsize(current->fs); 5762306a36Sopenharmony_ci else 5862306a36Sopenharmony_ci bytes += kobjsize(current->fs); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (current->files && atomic_read(¤t->files->count) > 1) 6162306a36Sopenharmony_ci sbytes += kobjsize(current->files); 6262306a36Sopenharmony_ci else 6362306a36Sopenharmony_ci bytes += kobjsize(current->files); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (current->sighand && refcount_read(¤t->sighand->count) > 1) 6662306a36Sopenharmony_ci sbytes += kobjsize(current->sighand); 6762306a36Sopenharmony_ci else 6862306a36Sopenharmony_ci bytes += kobjsize(current->sighand); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci bytes += kobjsize(current); /* includes kernel stack */ 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci mmap_read_unlock(mm); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci seq_printf(m, 7562306a36Sopenharmony_ci "Mem:\t%8lu bytes\n" 7662306a36Sopenharmony_ci "Slack:\t%8lu bytes\n" 7762306a36Sopenharmony_ci "Shared:\t%8lu bytes\n", 7862306a36Sopenharmony_ci bytes, slack, sbytes); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ciunsigned long task_vsize(struct mm_struct *mm) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci VMA_ITERATOR(vmi, mm, 0); 8462306a36Sopenharmony_ci struct vm_area_struct *vma; 8562306a36Sopenharmony_ci unsigned long vsize = 0; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci mmap_read_lock(mm); 8862306a36Sopenharmony_ci for_each_vma(vmi, vma) 8962306a36Sopenharmony_ci vsize += vma->vm_end - vma->vm_start; 9062306a36Sopenharmony_ci mmap_read_unlock(mm); 9162306a36Sopenharmony_ci return vsize; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ciunsigned long task_statm(struct mm_struct *mm, 9562306a36Sopenharmony_ci unsigned long *shared, unsigned long *text, 9662306a36Sopenharmony_ci unsigned long *data, unsigned long *resident) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci VMA_ITERATOR(vmi, mm, 0); 9962306a36Sopenharmony_ci struct vm_area_struct *vma; 10062306a36Sopenharmony_ci struct vm_region *region; 10162306a36Sopenharmony_ci unsigned long size = kobjsize(mm); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci mmap_read_lock(mm); 10462306a36Sopenharmony_ci for_each_vma(vmi, vma) { 10562306a36Sopenharmony_ci size += kobjsize(vma); 10662306a36Sopenharmony_ci region = vma->vm_region; 10762306a36Sopenharmony_ci if (region) { 10862306a36Sopenharmony_ci size += kobjsize(region); 10962306a36Sopenharmony_ci size += region->vm_end - region->vm_start; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci *text = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK)) 11462306a36Sopenharmony_ci >> PAGE_SHIFT; 11562306a36Sopenharmony_ci *data = (PAGE_ALIGN(mm->start_stack) - (mm->start_data & PAGE_MASK)) 11662306a36Sopenharmony_ci >> PAGE_SHIFT; 11762306a36Sopenharmony_ci mmap_read_unlock(mm); 11862306a36Sopenharmony_ci size >>= PAGE_SHIFT; 11962306a36Sopenharmony_ci size += *text + *data; 12062306a36Sopenharmony_ci *resident = size; 12162306a36Sopenharmony_ci return size; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* 12562306a36Sopenharmony_ci * display a single VMA to a sequenced file 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_cistatic int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct mm_struct *mm = vma->vm_mm; 13062306a36Sopenharmony_ci unsigned long ino = 0; 13162306a36Sopenharmony_ci struct file *file; 13262306a36Sopenharmony_ci dev_t dev = 0; 13362306a36Sopenharmony_ci int flags; 13462306a36Sopenharmony_ci unsigned long long pgoff = 0; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci flags = vma->vm_flags; 13762306a36Sopenharmony_ci file = vma->vm_file; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (file) { 14062306a36Sopenharmony_ci struct inode *inode = file_inode(vma->vm_file); 14162306a36Sopenharmony_ci dev = inode->i_sb->s_dev; 14262306a36Sopenharmony_ci ino = inode->i_ino; 14362306a36Sopenharmony_ci pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci seq_setwidth(m, 25 + sizeof(void *) * 6 - 1); 14762306a36Sopenharmony_ci seq_printf(m, 14862306a36Sopenharmony_ci "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ", 14962306a36Sopenharmony_ci vma->vm_start, 15062306a36Sopenharmony_ci vma->vm_end, 15162306a36Sopenharmony_ci flags & VM_READ ? 'r' : '-', 15262306a36Sopenharmony_ci flags & VM_WRITE ? 'w' : '-', 15362306a36Sopenharmony_ci flags & VM_EXEC ? 'x' : '-', 15462306a36Sopenharmony_ci flags & VM_MAYSHARE ? flags & VM_SHARED ? 'S' : 's' : 'p', 15562306a36Sopenharmony_ci pgoff, 15662306a36Sopenharmony_ci MAJOR(dev), MINOR(dev), ino); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (file) { 15962306a36Sopenharmony_ci seq_pad(m, ' '); 16062306a36Sopenharmony_ci seq_file_path(m, file, ""); 16162306a36Sopenharmony_ci } else if (mm && vma_is_initial_stack(vma)) { 16262306a36Sopenharmony_ci seq_pad(m, ' '); 16362306a36Sopenharmony_ci seq_puts(m, "[stack]"); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci seq_putc(m, '\n'); 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* 17162306a36Sopenharmony_ci * display mapping lines for a particular process's /proc/pid/maps 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_cistatic int show_map(struct seq_file *m, void *_p) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci return nommu_vma_show(m, _p); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic struct vm_area_struct *proc_get_vma(struct proc_maps_private *priv, 17962306a36Sopenharmony_ci loff_t *ppos) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct vm_area_struct *vma = vma_next(&priv->iter); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (vma) { 18462306a36Sopenharmony_ci *ppos = vma->vm_start; 18562306a36Sopenharmony_ci } else { 18662306a36Sopenharmony_ci *ppos = -1UL; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return vma; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void *m_start(struct seq_file *m, loff_t *ppos) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct proc_maps_private *priv = m->private; 19562306a36Sopenharmony_ci unsigned long last_addr = *ppos; 19662306a36Sopenharmony_ci struct mm_struct *mm; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* See proc_get_vma(). Zero at the start or after lseek. */ 19962306a36Sopenharmony_ci if (last_addr == -1UL) 20062306a36Sopenharmony_ci return NULL; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* pin the task and mm whilst we play with them */ 20362306a36Sopenharmony_ci priv->task = get_proc_task(priv->inode); 20462306a36Sopenharmony_ci if (!priv->task) 20562306a36Sopenharmony_ci return ERR_PTR(-ESRCH); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci mm = priv->mm; 20862306a36Sopenharmony_ci if (!mm || !mmget_not_zero(mm)) { 20962306a36Sopenharmony_ci put_task_struct(priv->task); 21062306a36Sopenharmony_ci priv->task = NULL; 21162306a36Sopenharmony_ci return NULL; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (mmap_read_lock_killable(mm)) { 21562306a36Sopenharmony_ci mmput(mm); 21662306a36Sopenharmony_ci put_task_struct(priv->task); 21762306a36Sopenharmony_ci priv->task = NULL; 21862306a36Sopenharmony_ci return ERR_PTR(-EINTR); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci vma_iter_init(&priv->iter, mm, last_addr); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci return proc_get_vma(priv, ppos); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic void m_stop(struct seq_file *m, void *v) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct proc_maps_private *priv = m->private; 22962306a36Sopenharmony_ci struct mm_struct *mm = priv->mm; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (!priv->task) 23262306a36Sopenharmony_ci return; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci mmap_read_unlock(mm); 23562306a36Sopenharmony_ci mmput(mm); 23662306a36Sopenharmony_ci put_task_struct(priv->task); 23762306a36Sopenharmony_ci priv->task = NULL; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic void *m_next(struct seq_file *m, void *_p, loff_t *ppos) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci return proc_get_vma(m->private, ppos); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic const struct seq_operations proc_pid_maps_ops = { 24662306a36Sopenharmony_ci .start = m_start, 24762306a36Sopenharmony_ci .next = m_next, 24862306a36Sopenharmony_ci .stop = m_stop, 24962306a36Sopenharmony_ci .show = show_map 25062306a36Sopenharmony_ci}; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic int maps_open(struct inode *inode, struct file *file, 25362306a36Sopenharmony_ci const struct seq_operations *ops) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct proc_maps_private *priv; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci priv = __seq_open_private(file, ops, sizeof(*priv)); 25862306a36Sopenharmony_ci if (!priv) 25962306a36Sopenharmony_ci return -ENOMEM; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci priv->inode = inode; 26262306a36Sopenharmony_ci priv->mm = proc_mem_open(inode, PTRACE_MODE_READ); 26362306a36Sopenharmony_ci if (IS_ERR(priv->mm)) { 26462306a36Sopenharmony_ci int err = PTR_ERR(priv->mm); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci seq_release_private(inode, file); 26762306a36Sopenharmony_ci return err; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic int map_release(struct inode *inode, struct file *file) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct seq_file *seq = file->private_data; 27762306a36Sopenharmony_ci struct proc_maps_private *priv = seq->private; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (priv->mm) 28062306a36Sopenharmony_ci mmdrop(priv->mm); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return seq_release_private(inode, file); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int pid_maps_open(struct inode *inode, struct file *file) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci return maps_open(inode, file, &proc_pid_maps_ops); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ciconst struct file_operations proc_pid_maps_operations = { 29162306a36Sopenharmony_ci .open = pid_maps_open, 29262306a36Sopenharmony_ci .read = seq_read, 29362306a36Sopenharmony_ci .llseek = seq_lseek, 29462306a36Sopenharmony_ci .release = map_release, 29562306a36Sopenharmony_ci}; 29662306a36Sopenharmony_ci 297