mm: add /proc/pid/smaps_rollup
[sfrench/cifs-2.6.git] / fs / proc / task_mmu.c
index fe8f3265e8779ac18a5694ef600c024f9e88f281..b2330aedc63f01ef36988295a655899be74ffb2a 100644 (file)
@@ -253,6 +253,7 @@ static int proc_map_release(struct inode *inode, struct file *file)
        if (priv->mm)
                mmdrop(priv->mm);
 
+       kfree(priv->rollup);
        return seq_release_private(inode, file);
 }
 
@@ -279,6 +280,23 @@ static int is_stack(struct proc_maps_private *priv,
                vma->vm_end >= vma->vm_mm->start_stack;
 }
 
+static void show_vma_header_prefix(struct seq_file *m,
+                                  unsigned long start, unsigned long end,
+                                  vm_flags_t flags, unsigned long long pgoff,
+                                  dev_t dev, unsigned long ino)
+{
+       seq_setwidth(m, 25 + sizeof(void *) * 6 - 1);
+       seq_printf(m, "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ",
+                  start,
+                  end,
+                  flags & VM_READ ? 'r' : '-',
+                  flags & VM_WRITE ? 'w' : '-',
+                  flags & VM_EXEC ? 'x' : '-',
+                  flags & VM_MAYSHARE ? 's' : 'p',
+                  pgoff,
+                  MAJOR(dev), MINOR(dev), ino);
+}
+
 static void
 show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
 {
@@ -301,17 +319,7 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
 
        start = vma->vm_start;
        end = vma->vm_end;
-
-       seq_setwidth(m, 25 + sizeof(void *) * 6 - 1);
-       seq_printf(m, "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ",
-                       start,
-                       end,
-                       flags & VM_READ ? 'r' : '-',
-                       flags & VM_WRITE ? 'w' : '-',
-                       flags & VM_EXEC ? 'x' : '-',
-                       flags & VM_MAYSHARE ? 's' : 'p',
-                       pgoff,
-                       MAJOR(dev), MINOR(dev), ino);
+       show_vma_header_prefix(m, start, end, flags, pgoff, dev, ino);
 
        /*
         * Print the dentry name for named mappings, and a
@@ -430,6 +438,7 @@ const struct file_operations proc_tid_maps_operations = {
 
 #ifdef CONFIG_PROC_PAGE_MONITOR
 struct mem_size_stats {
+       bool first;
        unsigned long resident;
        unsigned long shared_clean;
        unsigned long shared_dirty;
@@ -443,7 +452,9 @@ struct mem_size_stats {
        unsigned long swap;
        unsigned long shared_hugetlb;
        unsigned long private_hugetlb;
+       unsigned long first_vma_start;
        u64 pss;
+       u64 pss_locked;
        u64 swap_pss;
        bool check_shmem_swap;
 };
@@ -719,18 +730,36 @@ void __weak arch_show_smap(struct seq_file *m, struct vm_area_struct *vma)
 
 static int show_smap(struct seq_file *m, void *v, int is_pid)
 {
+       struct proc_maps_private *priv = m->private;
        struct vm_area_struct *vma = v;
-       struct mem_size_stats mss;
+       struct mem_size_stats mss_stack;
+       struct mem_size_stats *mss;
        struct mm_walk smaps_walk = {
                .pmd_entry = smaps_pte_range,
 #ifdef CONFIG_HUGETLB_PAGE
                .hugetlb_entry = smaps_hugetlb_range,
 #endif
                .mm = vma->vm_mm,
-               .private = &mss,
        };
+       int ret = 0;
+       bool rollup_mode;
+       bool last_vma;
+
+       if (priv->rollup) {
+               rollup_mode = true;
+               mss = priv->rollup;
+               if (mss->first) {
+                       mss->first_vma_start = vma->vm_start;
+                       mss->first = false;
+               }
+               last_vma = !m_next_vma(priv, vma);
+       } else {
+               rollup_mode = false;
+               memset(&mss_stack, 0, sizeof(mss_stack));
+               mss = &mss_stack;
+       }
 
-       memset(&mss, 0, sizeof mss);
+       smaps_walk.private = mss;
 
 #ifdef CONFIG_SHMEM
        if (vma->vm_file && shmem_mapping(vma->vm_file->f_mapping)) {
@@ -748,9 +777,9 @@ static int show_smap(struct seq_file *m, void *v, int is_pid)
 
                if (!shmem_swapped || (vma->vm_flags & VM_SHARED) ||
                                        !(vma->vm_flags & VM_WRITE)) {
-                       mss.swap = shmem_swapped;
+                       mss->swap = shmem_swapped;
                } else {
-                       mss.check_shmem_swap = true;
+                       mss->check_shmem_swap = true;
                        smaps_walk.pte_hole = smaps_pte_hole;
                }
        }
@@ -758,54 +787,71 @@ static int show_smap(struct seq_file *m, void *v, int is_pid)
 
        /* mmap_sem is held in m_start */
        walk_page_vma(vma, &smaps_walk);
+       if (vma->vm_flags & VM_LOCKED)
+               mss->pss_locked += mss->pss;
+
+       if (!rollup_mode) {
+               show_map_vma(m, vma, is_pid);
+       } else if (last_vma) {
+               show_vma_header_prefix(
+                       m, mss->first_vma_start, vma->vm_end, 0, 0, 0, 0);
+               seq_pad(m, ' ');
+               seq_puts(m, "[rollup]\n");
+       } else {
+               ret = SEQ_SKIP;
+       }
 
-       show_map_vma(m, vma, is_pid);
-
-       seq_printf(m,
-                  "Size:           %8lu kB\n"
-                  "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"
-                  "LazyFree:       %8lu kB\n"
-                  "AnonHugePages:  %8lu kB\n"
-                  "ShmemPmdMapped: %8lu kB\n"
-                  "Shared_Hugetlb: %8lu kB\n"
-                  "Private_Hugetlb: %7lu kB\n"
-                  "Swap:           %8lu kB\n"
-                  "SwapPss:        %8lu kB\n"
-                  "KernelPageSize: %8lu kB\n"
-                  "MMUPageSize:    %8lu kB\n"
-                  "Locked:         %8lu kB\n",
-                  (vma->vm_end - vma->vm_start) >> 10,
-                  mss.resident >> 10,
-                  (unsigned long)(mss.pss >> (10 + PSS_SHIFT)),
-                  mss.shared_clean  >> 10,
-                  mss.shared_dirty  >> 10,
-                  mss.private_clean >> 10,
-                  mss.private_dirty >> 10,
-                  mss.referenced >> 10,
-                  mss.anonymous >> 10,
-                  mss.lazyfree >> 10,
-                  mss.anonymous_thp >> 10,
-                  mss.shmem_thp >> 10,
-                  mss.shared_hugetlb >> 10,
-                  mss.private_hugetlb >> 10,
-                  mss.swap >> 10,
-                  (unsigned long)(mss.swap_pss >> (10 + PSS_SHIFT)),
-                  vma_kernel_pagesize(vma) >> 10,
-                  vma_mmu_pagesize(vma) >> 10,
-                  (vma->vm_flags & VM_LOCKED) ?
-                       (unsigned long)(mss.pss >> (10 + PSS_SHIFT)) : 0);
-
-       arch_show_smap(m, vma);
-       show_smap_vma_flags(m, vma);
+       if (!rollup_mode)
+               seq_printf(m,
+                          "Size:           %8lu kB\n"
+                          "KernelPageSize: %8lu kB\n"
+                          "MMUPageSize:    %8lu kB\n",
+                          (vma->vm_end - vma->vm_start) >> 10,
+                          vma_kernel_pagesize(vma) >> 10,
+                          vma_mmu_pagesize(vma) >> 10);
+
+
+       if (!rollup_mode || last_vma)
+               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"
+                          "LazyFree:       %8lu kB\n"
+                          "AnonHugePages:  %8lu kB\n"
+                          "ShmemPmdMapped: %8lu kB\n"
+                          "Shared_Hugetlb: %8lu kB\n"
+                          "Private_Hugetlb: %7lu kB\n"
+                          "Swap:           %8lu kB\n"
+                          "SwapPss:        %8lu kB\n"
+                          "Locked:         %8lu kB\n",
+                          mss->resident >> 10,
+                          (unsigned long)(mss->pss >> (10 + PSS_SHIFT)),
+                          mss->shared_clean  >> 10,
+                          mss->shared_dirty  >> 10,
+                          mss->private_clean >> 10,
+                          mss->private_dirty >> 10,
+                          mss->referenced >> 10,
+                          mss->anonymous >> 10,
+                          mss->lazyfree >> 10,
+                          mss->anonymous_thp >> 10,
+                          mss->shmem_thp >> 10,
+                          mss->shared_hugetlb >> 10,
+                          mss->private_hugetlb >> 10,
+                          mss->swap >> 10,
+                          (unsigned long)(mss->swap_pss >> (10 + PSS_SHIFT)),
+                          (unsigned long)(mss->pss >> (10 + PSS_SHIFT)));
+
+       if (!rollup_mode) {
+               arch_show_smap(m, vma);
+               show_smap_vma_flags(m, vma);
+       }
        m_cache_vma(m, vma);
-       return 0;
+       return ret;
 }
 
 static int show_pid_smap(struct seq_file *m, void *v)
@@ -837,6 +883,25 @@ static int pid_smaps_open(struct inode *inode, struct file *file)
        return do_maps_open(inode, file, &proc_pid_smaps_op);
 }
 
+static int pid_smaps_rollup_open(struct inode *inode, struct file *file)
+{
+       struct seq_file *seq;
+       struct proc_maps_private *priv;
+       int ret = do_maps_open(inode, file, &proc_pid_smaps_op);
+
+       if (ret < 0)
+               return ret;
+       seq = file->private_data;
+       priv = seq->private;
+       priv->rollup = kzalloc(sizeof(*priv->rollup), GFP_KERNEL);
+       if (!priv->rollup) {
+               proc_map_release(inode, file);
+               return -ENOMEM;
+       }
+       priv->rollup->first = true;
+       return 0;
+}
+
 static int tid_smaps_open(struct inode *inode, struct file *file)
 {
        return do_maps_open(inode, file, &proc_tid_smaps_op);
@@ -849,6 +914,13 @@ const struct file_operations proc_pid_smaps_operations = {
        .release        = proc_map_release,
 };
 
+const struct file_operations proc_pid_smaps_rollup_operations = {
+       .open           = pid_smaps_rollup_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = proc_map_release,
+};
+
 const struct file_operations proc_tid_smaps_operations = {
        .open           = tid_smaps_open,
        .read           = seq_read,