| From b17a2783e52a9b1f5433bfd20349ab077cc5b37a Mon Sep 17 00:00:00 2001 |
| From: Sonny Rao <sonnyrao@chromium.org> |
| Date: Tue, 16 Jul 2013 13:06:45 -0700 |
| Subject: [PATCH] CHROMIUM: mm: implement /proc/<pid>/totmaps |
| |
| This is based on earlier work by Thiago Goncales. It implements a new |
| per process proc file which summarizes the contents of the smaps file |
| but doesn't display any addresses. It gives more detailed information |
| than statm like the PSS (proprotional set size). It differs from the |
| original implementation in that it doesn't use the full blown set of |
| seq operations, uses a different termination condition, and doesn't |
| displayed "Locked" as that was broken on the original implemenation. |
| |
| BUG=chromium:258160 |
| TEST=manual, cat /proc/self/totmaps and verify information is present |
| |
| Change-Id: I6f631162709efc075897f366dc11a76abfa09cd1 |
| Signed-off-by: Sonny Rao <sonnyrao@chromium.org> |
| Reviewed-on: https://gerrit.chromium.org/gerrit/62217 |
| |
| [benzh: 3.14 rebase. Resolved trivial conflicts] |
| [bfreed: 3.18 rebase. Resolved compile failure] |
| Signed-off-by: Ben Zhang <benzh@chromium.org> |
| Signed-off-by: Bryan Freed <bfreed@chromium.org> |
| |
| [rebase44(filbranden): Use walk_page_vma() and remove the assignment of |
| mss.vma which is no longer necessary after upstream commit |
| 14eb6fdd4204d2 ("smaps: remove mem_size_stats->vma and use |
| walk_page_vma()") that dropped that field. |
| Squashed these commits together: |
| - CHROMIUM: mm: implement /proc/<pid>/totmaps |
| - CHROMIUM: procfs: fix leak in totmaps_release() |
| ] |
| Signed-off-by: Filipe Brandenburger <filbranden@chromium.org> |
| |
| [rebase419(groeck): Context conflicts] |
| Signed-off-by: Guenter Roeck <groeck@chromium.org> |
| [rebase54(groeck): walk_page() parameter changes] |
| Signed-off-by: Guenter Roeck <groeck@chromium.org> |
| [rebase510(groeck): mmap locking changes] |
| Signed-off-by: Guenter Roeck <groeck@chromium.org> |
| --- |
| fs/proc/base.c | 1 + |
| fs/proc/internal.h | 4 ++ |
| fs/proc/task_mmu.c | 121 +++++++++++++++++++++++++++++++++++++++++++++ |
| 3 files changed, 126 insertions(+) |
| |
| diff --git a/fs/proc/base.c b/fs/proc/base.c |
| index 9e479d7d202b12c0ddf53e00975858162156db8a..99710b2a76f51f05b636e5886e20f79df6fdb096 100644 |
| --- a/fs/proc/base.c |
| +++ b/fs/proc/base.c |
| @@ -3281,6 +3281,7 @@ static const struct pid_entry tgid_base_stuff[] = { |
| REG("smaps", S_IRUGO, proc_pid_smaps_operations), |
| REG("smaps_rollup", S_IRUGO, proc_pid_smaps_rollup_operations), |
| REG("pagemap", S_IRUSR, proc_pagemap_operations), |
| + REG("totmaps", S_IRUGO, proc_totmaps_operations), |
| #endif |
| #ifdef CONFIG_SECURITY |
| DIR("attr", S_IRUGO|S_IXUGO, proc_attr_dir_inode_operations, proc_attr_dir_operations), |
| diff --git a/fs/proc/internal.h b/fs/proc/internal.h |
| index b701d0207edf098814a70400850e3675aee448f1..0873dae3585ad852eb3a35d43978aafa80e8a799 100644 |
| --- a/fs/proc/internal.h |
| +++ b/fs/proc/internal.h |
| @@ -95,6 +95,9 @@ union proc_op { |
| const char *lsm; |
| }; |
| |
| + |
| +extern const struct file_operations proc_totmaps_operations; |
| + |
| struct proc_inode { |
| struct pid *pid; |
| unsigned int fd; |
| @@ -291,6 +294,7 @@ struct proc_maps_private { |
| struct mm_struct *mm; |
| #ifdef CONFIG_MMU |
| struct vma_iterator iter; |
| + struct mem_size_stats *mss; |
| #endif |
| #ifdef CONFIG_NUMA |
| struct mempolicy *task_mempolicy; |
| diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c |
| index 8a74cdcc9af00f7217f8a5ab3700537b5af6913d..4215cbb8308f8ae313181f873567fec771cc0165 100644 |
| --- a/fs/proc/task_mmu.c |
| +++ b/fs/proc/task_mmu.c |
| @@ -876,6 +876,76 @@ static int show_smap(struct seq_file *m, void *v) |
| return 0; |
| } |
| |
| +static void add_smaps_sum(struct mem_size_stats *mss, |
| + struct mem_size_stats *mss_sum) |
| +{ |
| + mss_sum->resident += mss->resident; |
| + mss_sum->pss += mss->pss; |
| + mss_sum->shared_clean += mss->shared_clean; |
| + mss_sum->shared_dirty += mss->shared_dirty; |
| + mss_sum->private_clean += mss->private_clean; |
| + mss_sum->private_dirty += mss->private_dirty; |
| + mss_sum->referenced += mss->referenced; |
| + mss_sum->anonymous += mss->anonymous; |
| + mss_sum->anonymous_thp += mss->anonymous_thp; |
| + mss_sum->swap += mss->swap; |
| +} |
| + |
| +static int totmaps_proc_show(struct seq_file *m, void *data) |
| +{ |
| + struct proc_maps_private *priv = m->private; |
| + struct mm_struct *mm; |
| + struct vm_area_struct *vma; |
| + struct mem_size_stats *mss_sum = priv->mss; |
| + |
| + /* reference to priv->task already taken */ |
| + /* but need to get the mm here because */ |
| + /* task could be in the process of exiting */ |
| + mm = get_task_mm(priv->task); |
| + if (!mm || IS_ERR(mm)) |
| + return -EINVAL; |
| + |
| + mmap_read_lock(mm); |
| + hold_task_mempolicy(priv); |
| + |
| + for (vma = mm->mmap; vma != priv->tail_vma; vma = vma->vm_next) { |
| + struct mem_size_stats mss; |
| + |
| + if (vma->vm_mm && !is_vm_hugetlb_page(vma)) { |
| + memset(&mss, 0, sizeof(mss)); |
| + walk_page_vma(vma, &smaps_walk_ops, &mss); |
| + add_smaps_sum(&mss, mss_sum); |
| + } |
| + } |
| + seq_printf(m, |
| + "Rss: %8lu kB\n" |
| + "Pss: %8lu kB\n" |
| + "Shared_Clean: %8lu kB\n" |
| + "Shared_Dirty: %8lu kB\n" |
| + "Private_Clean: %8lu kB\n" |
| + "Private_Dirty: %8lu kB\n" |
| + "Referenced: %8lu kB\n" |
| + "Anonymous: %8lu kB\n" |
| + "AnonHugePages: %8lu kB\n" |
| + "Swap: %8lu kB\n", |
| + mss_sum->resident >> 10, |
| + (unsigned long)(mss_sum->pss >> (10 + PSS_SHIFT)), |
| + mss_sum->shared_clean >> 10, |
| + mss_sum->shared_dirty >> 10, |
| + mss_sum->private_clean >> 10, |
| + mss_sum->private_dirty >> 10, |
| + mss_sum->referenced >> 10, |
| + mss_sum->anonymous >> 10, |
| + mss_sum->anonymous_thp >> 10, |
| + mss_sum->swap >> 10); |
| + |
| + release_task_mempolicy(priv); |
| + mmap_read_unlock(mm); |
| + mmput(mm); |
| + |
| + return 0; |
| +} |
| + |
| static int show_smaps_rollup(struct seq_file *m, void *v) |
| { |
| struct proc_maps_private *priv = m->private; |
| @@ -1050,6 +1120,50 @@ static int smaps_rollup_release(struct inode *inode, struct file *file) |
| return single_release(inode, file); |
| } |
| |
| +static int totmaps_open(struct inode *inode, struct file *file) |
| +{ |
| + struct proc_maps_private *priv; |
| + int ret = -ENOMEM; |
| + priv = kzalloc(sizeof(*priv), GFP_KERNEL); |
| + if (priv) { |
| + priv->mss = kzalloc(sizeof(*priv->mss), GFP_KERNEL); |
| + if (!priv->mss) |
| + return -ENOMEM; |
| + |
| + /* we need to grab references to the task_struct */ |
| + /* at open time, because there's a potential information */ |
| + /* leak where the totmaps file is opened and held open */ |
| + /* while the underlying pid to task mapping changes */ |
| + /* underneath it */ |
| + priv->task = get_pid_task(proc_pid(inode), PIDTYPE_PID); |
| + if (!priv->task) { |
| + kfree(priv->mss); |
| + kfree(priv); |
| + return -ESRCH; |
| + } |
| + |
| + ret = single_open(file, totmaps_proc_show, priv); |
| + if (ret) { |
| + put_task_struct(priv->task); |
| + kfree(priv->mss); |
| + kfree(priv); |
| + } |
| + } |
| + return ret; |
| +} |
| + |
| +static int totmaps_release(struct inode *inode, struct file *file) |
| +{ |
| + struct seq_file *m = file->private_data; |
| + struct proc_maps_private *priv = m->private; |
| + |
| + put_task_struct(priv->task); |
| + kfree(priv->mss); |
| + kfree(priv); |
| + m->private = NULL; |
| + return single_release(inode, file); |
| +} |
| + |
| const struct file_operations proc_pid_smaps_operations = { |
| .open = pid_smaps_open, |
| .read = seq_read, |
| @@ -1064,6 +1178,13 @@ const struct file_operations proc_pid_smaps_rollup_operations = { |
| .release = smaps_rollup_release, |
| }; |
| |
| +const struct file_operations proc_totmaps_operations = { |
| + .open = totmaps_open, |
| + .read = seq_read, |
| + .llseek = seq_lseek, |
| + .release = totmaps_release, |
| +}; |
| + |
| enum clear_refs_types { |
| CLEAR_REFS_ALL = 1, |
| CLEAR_REFS_ANON, |
| -- |
| 2.38.1.584.g0f3c55d4c2-goog |
| |