blob: b0e7bce60cf463296b0b8fa6695c2d7c82fcc671 [file] [log] [blame]
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