| From 983dd2810c37f2d24adc2c7057eb15f72d21b377 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> |
| |
| [rebase61(tzungbi): |
| Squashed: |
| FIXUP: CHROMIUM: mm: implement /proc/<pid>/totmaps |
| ] |
| Signed-off-by: Tzung-Bi Shih <tzungbi@chromium.org> |
| --- |
| fs/proc/base.c | 1 + |
| fs/proc/internal.h | 6 +++ |
| fs/proc/task_mmu.c | 123 +++++++++++++++++++++++++++++++++++++++++++++ |
| 3 files changed, 130 insertions(+) |
| |
| diff --git a/fs/proc/base.c b/fs/proc/base.c |
| index ffd54617c35478e92a9f6bef67013e16e6cd3183..f9c701f149df653ef37f81b9a7fbeb2dd0437f8f 100644 |
| --- a/fs/proc/base.c |
| +++ b/fs/proc/base.c |
| @@ -3284,6 +3284,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 9a8f32f21ff569d0dc40e1d7c31b63b9aea293bc..d16ec48623078f83efa59d941bf8acca7d5098c3 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; |
| @@ -290,6 +293,9 @@ struct proc_maps_private { |
| struct task_struct *task; |
| struct mm_struct *mm; |
| struct vma_iterator iter; |
| +#ifdef CONFIG_MMU |
| + struct mem_size_stats *mss; |
| +#endif |
| #ifdef CONFIG_NUMA |
| struct mempolicy *task_mempolicy; |
| #endif |
| diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c |
| index 3dd5be96691b4cc234454353ec1b8a324760e0c2..ee48c9d106895ef1f64974a8dfebe5d75c4ab0d2 100644 |
| --- a/fs/proc/task_mmu.c |
| +++ b/fs/proc/task_mmu.c |
| @@ -874,6 +874,78 @@ 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; |
| + struct vma_iterator vmi; |
| + |
| + /* 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); |
| + |
| + vma_iter_init(&vmi, mm, 0); |
| + for_each_vma(vmi, vma) { |
| + 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; |
| @@ -1047,6 +1119,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, |
| @@ -1061,6 +1177,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.34.1 |
| |