Change pagemap output format to allow for future reporting of huge pages
[sfrench/cifs-2.6.git] / fs / proc / task_mmu.c
index 38338ed98cc6df167febbe2fc962f55a700bfb19..9dfb5ff24209be91f2a3bd87048ab130c46ae2dd 100644 (file)
@@ -9,13 +9,14 @@
 #include <linux/mempolicy.h>
 #include <linux/swap.h>
 #include <linux/swapops.h>
+#include <linux/seq_file.h>
 
 #include <asm/elf.h>
 #include <asm/uaccess.h>
 #include <asm/tlbflush.h>
 #include "internal.h"
 
-char *task_mem(struct mm_struct *mm, char *buffer)
+void task_mem(struct seq_file *m, struct mm_struct *mm)
 {
        unsigned long data, text, lib;
        unsigned long hiwater_vm, total_vm, hiwater_rss, total_rss;
@@ -37,7 +38,7 @@ char *task_mem(struct mm_struct *mm, char *buffer)
        data = mm->total_vm - mm->shared_vm - mm->stack_vm;
        text = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK)) >> 10;
        lib = (mm->exec_vm << (PAGE_SHIFT-10)) - text;
-       buffer += sprintf(buffer,
+       seq_printf(m,
                "VmPeak:\t%8lu kB\n"
                "VmSize:\t%8lu kB\n"
                "VmLck:\t%8lu kB\n"
@@ -56,7 +57,6 @@ char *task_mem(struct mm_struct *mm, char *buffer)
                data << (PAGE_SHIFT-10),
                mm->stack_vm << (PAGE_SHIFT-10), text, lib,
                (PTRS_PER_PTE*sizeof(pte_t)*mm->nr_ptes) >> 10);
-       return buffer;
 }
 
 unsigned long task_vsize(struct mm_struct *mm)
@@ -75,7 +75,7 @@ int task_statm(struct mm_struct *mm, int *shared, int *text,
        return mm->total_vm;
 }
 
-int proc_exe_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt)
+int proc_exe_link(struct inode *inode, struct path *path)
 {
        struct vm_area_struct * vma;
        int result = -ENOENT;
@@ -98,8 +98,8 @@ int proc_exe_link(struct inode *inode, struct dentry **dentry, struct vfsmount *
        }
 
        if (vma) {
-               *mnt = mntget(vma->vm_file->f_path.mnt);
-               *dentry = dget(vma->vm_file->f_path.dentry);
+               *path = vma->vm_file->f_path;
+               path_get(&vma->vm_file->f_path);
                result = 0;
        }
 
@@ -216,7 +216,7 @@ static void m_stop(struct seq_file *m, void *v)
 }
 
 static int do_maps_open(struct inode *inode, struct file *file,
-                       struct seq_operations *ops)
+                       const struct seq_operations *ops)
 {
        struct proc_maps_private *priv;
        int ret = -ENOMEM;
@@ -271,7 +271,7 @@ static int show_map(struct seq_file *m, void *v)
         */
        if (file) {
                pad_len_spaces(m, len);
-               seq_path(m, file->f_path.mnt, file->f_path.dentry, "\n");
+               seq_path(m, &file->f_path, "\n");
        } else {
                const char *name = arch_vma_name(vma);
                if (!name) {
@@ -299,7 +299,7 @@ static int show_map(struct seq_file *m, void *v)
        return 0;
 }
 
-static struct seq_operations proc_pid_maps_op = {
+static const struct seq_operations proc_pid_maps_op = {
        .start  = m_start,
        .next   = m_next,
        .stop   = m_stop,
@@ -434,7 +434,7 @@ static int show_smap(struct seq_file *m, void *v)
        return ret;
 }
 
-static struct seq_operations proc_pid_smaps_op = {
+static const struct seq_operations proc_pid_smaps_op = {
        .start  = m_start,
        .next   = m_next,
        .stop   = m_stop,
@@ -527,13 +527,21 @@ struct pagemapread {
        char __user *out, *end;
 };
 
-#define PM_ENTRY_BYTES sizeof(u64)
-#define PM_RESERVED_BITS    3
-#define PM_RESERVED_OFFSET  (64 - PM_RESERVED_BITS)
-#define PM_RESERVED_MASK    (((1LL<<PM_RESERVED_BITS)-1) << PM_RESERVED_OFFSET)
-#define PM_SPECIAL(nr)      (((nr) << PM_RESERVED_OFFSET) | PM_RESERVED_MASK)
-#define PM_NOT_PRESENT      PM_SPECIAL(1LL)
-#define PM_SWAP             PM_SPECIAL(2LL)
+#define PM_ENTRY_BYTES      sizeof(u64)
+#define PM_STATUS_BITS      3
+#define PM_STATUS_OFFSET    (64 - PM_STATUS_BITS)
+#define PM_STATUS_MASK      (((1LL << PM_STATUS_BITS) - 1) << PM_STATUS_OFFSET)
+#define PM_STATUS(nr)       (((nr) << PM_STATUS_OFFSET) & PM_STATUS_MASK)
+#define PM_PSHIFT_BITS      6
+#define PM_PSHIFT_OFFSET    (PM_STATUS_OFFSET - PM_PSHIFT_BITS)
+#define PM_PSHIFT_MASK      (((1LL << PM_PSHIFT_BITS) - 1) << PM_PSHIFT_OFFSET)
+#define PM_PSHIFT(x)        (((u64) (x) << PM_PSHIFT_OFFSET) & PM_PSHIFT_MASK)
+#define PM_PFRAME_MASK      ((1LL << PM_PSHIFT_OFFSET) - 1)
+#define PM_PFRAME(x)        ((x) & PM_PFRAME_MASK)
+
+#define PM_PRESENT          PM_STATUS(4LL)
+#define PM_SWAP             PM_STATUS(2LL)
+#define PM_NOT_PRESENT      PM_PSHIFT(PAGE_SHIFT)
 #define PM_END_OF_BUFFER    1
 
 static int add_to_pagemap(unsigned long addr, u64 pfn,
@@ -574,7 +582,7 @@ static int pagemap_pte_hole(unsigned long start, unsigned long end,
 u64 swap_pte_to_pagemap_entry(pte_t pte)
 {
        swp_entry_t e = pte_to_swp_entry(pte);
-       return PM_SWAP | swp_type(e) | (swp_offset(e) << MAX_SWAPFILES_SHIFT);
+       return swp_type(e) | (swp_offset(e) << MAX_SWAPFILES_SHIFT);
 }
 
 static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
@@ -588,9 +596,11 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
                u64 pfn = PM_NOT_PRESENT;
                pte = pte_offset_map(pmd, addr);
                if (is_swap_pte(*pte))
-                       pfn = swap_pte_to_pagemap_entry(*pte);
+                       pfn = PM_PFRAME(swap_pte_to_pagemap_entry(*pte))
+                               | PM_PSHIFT(PAGE_SHIFT) | PM_SWAP;
                else if (pte_present(*pte))
-                       pfn = pte_pfn(*pte);
+                       pfn = PM_PFRAME(pte_pfn(*pte))
+                               | PM_PSHIFT(PAGE_SHIFT) | PM_PRESENT;
                /* unmap so we're not in atomic when we copy to userspace */
                pte_unmap(pte);
                err = add_to_pagemap(addr, pfn, pm);
@@ -611,12 +621,20 @@ static struct mm_walk pagemap_walk = {
 /*
  * /proc/pid/pagemap - an array mapping virtual pages to pfns
  *
- * For each page in the address space, this file contains one 64-bit
- * entry representing the corresponding physical page frame number
- * (PFN) if the page is present. If there is a swap entry for the
- * physical page, then an encoding of the swap file number and the
- * page's offset into the swap file are returned. If no page is
- * present at all, PM_NOT_PRESENT is returned. This allows determining
+ * For each page in the address space, this file contains one 64-bit entry
+ * consisting of the following:
+ *
+ * Bits 0-55  page frame number (PFN) if present
+ * Bits 0-4   swap type if swapped
+ * Bits 5-55  swap offset if swapped
+ * Bits 55-60 page shift (page size = 1<<page shift)
+ * Bit  61    reserved for future use
+ * Bit  62    page swapped
+ * Bit  63    page present
+ *
+ * If the page is not present but in swap, then the PFN contains an
+ * encoding of the swap file number and the page's offset into the
+ * swap. Unmapped pages return a null PFN. This allows determining
  * precisely which pages are mapped (or in swap) and comparing mapped
  * pages between processes.
  *
@@ -640,17 +658,17 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
 
        ret = -EACCES;
        if (!ptrace_may_attach(task))
-               goto out;
+               goto out_task;
 
        ret = -EINVAL;
        /* file position must be aligned */
        if (*ppos % PM_ENTRY_BYTES)
-               goto out;
+               goto out_task;
 
        ret = 0;
        mm = get_task_mm(task);
        if (!mm)
-               goto out;
+               goto out_task;
 
        ret = -ENOMEM;
        uaddr = (unsigned long)buf & PAGE_MASK;
@@ -658,7 +676,7 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
        pagecount = (PAGE_ALIGN(uend) - uaddr) / PAGE_SIZE;
        pages = kmalloc(pagecount * sizeof(struct page *), GFP_KERNEL);
        if (!pages)
-               goto out_task;
+               goto out_mm;
 
        down_read(&current->mm->mmap_sem);
        ret = get_user_pages(current, current->mm, uaddr, pagecount,
@@ -668,6 +686,12 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
        if (ret < 0)
                goto out_free;
 
+       if (ret != pagecount) {
+               pagecount = ret;
+               ret = -EFAULT;
+               goto out_pages;
+       }
+
        pm.out = buf;
        pm.end = buf + count;
 
@@ -699,15 +723,17 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
                        ret = pm.out - buf;
        }
 
+out_pages:
        for (; pagecount; pagecount--) {
                page = pages[pagecount-1];
                if (!PageReserved(page))
                        SetPageDirty(page);
                page_cache_release(page);
        }
-       mmput(mm);
 out_free:
        kfree(pages);
+out_mm:
+       mmput(mm);
 out_task:
        put_task_struct(task);
 out:
@@ -734,7 +760,7 @@ static int show_numa_map_checked(struct seq_file *m, void *v)
        return show_numa_map(m, v);
 }
 
-static struct seq_operations proc_pid_numa_maps_op = {
+static const struct seq_operations proc_pid_numa_maps_op = {
         .start  = m_start,
         .next   = m_next,
         .stop   = m_stop,