All of lore.kernel.org
 help / color / mirror / Atom feed
* [patch 1/3] smaps: extract pte walker from smaps code
@ 2007-02-07  5:26 David Rientjes
  2007-02-07  5:27 ` [patch 2/3] smaps: add pages referenced count to smaps David Rientjes
  0 siblings, 1 reply; 7+ messages in thread
From: David Rientjes @ 2007-02-07  5:26 UTC (permalink / raw)
  To: Andrew Morton; +Cc: Hugh Dickins, Paul Mundt, Christoph Lameter, linux-kernel

Extracts the page table entry walker from the smaps-specific code in
fs/proc/task_mmu.c.  This will be used later for clearing the reference
bits on pages to measure the number of pages accessed over a time period
through /proc/pid/smaps.

The new struct pte_walker includes the struct vm_area_struct of the memory
to walk over.  Iteration begins at the start address and completes at the
the end address.  A pointer to another data structure may be stored in the
private field such as the struct mem_size_stats, which acts as the smaps
accumulator.  For each page table entry in the VMA, the func function is
called with the corresponding struct pte_walker, the pte_t, and its
address.

Since the PTE walker is now extracted from the smaps code,
smaps_pte_func() is invoked for each PTE in the VMA.  Its behavior is
identical to the existing implementation, except it is slightly slower
because each PTE now invokes a function call.

Cc: Hugh Dickins <hugh@veritas.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Christoph Lameter <clameter@sgi.com>
Signed-off-by: David Rientjes <rientjes@google.com>
---
 fs/proc/task_mmu.c |  126 ++++++++++++++++++++++++++++++++++------------------
 1 files changed, 82 insertions(+), 44 deletions(-)

diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 55ade0d..e87824b 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -122,6 +122,15 @@ struct mem_size_stats
 	unsigned long private_dirty;
 };
 
+struct pte_walker {
+	struct vm_area_struct *vma;	/* VMA */
+	unsigned long start;		/* start address */
+	unsigned long end;		/* end address */
+	void *private;			/* private data */
+	/* function to invoke for each pte in the above range */
+	void (*func)(struct pte_walker*, pte_t*, unsigned long);
+};
+
 static int show_map_internal(struct seq_file *m, void *v, struct mem_size_stats *mss)
 {
 	struct proc_maps_private *priv = m->private;
@@ -204,98 +213,127 @@ static int show_map(struct seq_file *m, void *v)
 	return show_map_internal(m, v, NULL);
 }
 
-static void smaps_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
-				unsigned long addr, unsigned long end,
-				struct mem_size_stats *mss)
+/*
+ * Walks each PTE in the struct pte_walker address range and calls
+ * walker->func() for each entry.
+ */
+static void walk_ptes(struct pte_walker *walker, pmd_t *pmd)
 {
-	pte_t *pte, ptent;
+	struct vm_area_struct *vma = walker->vma;
+	unsigned long addr = walker->start;
 	spinlock_t *ptl;
-	struct page *page;
+	pte_t *pte;
 
 	pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
 	do {
-		ptent = *pte;
-		if (!pte_present(ptent))
-			continue;
-
-		mss->resident += PAGE_SIZE;
-
-		page = vm_normal_page(vma, addr, ptent);
-		if (!page)
-			continue;
-
-		if (page_mapcount(page) >= 2) {
-			if (pte_dirty(ptent))
-				mss->shared_dirty += PAGE_SIZE;
-			else
-				mss->shared_clean += PAGE_SIZE;
-		} else {
-			if (pte_dirty(ptent))
-				mss->private_dirty += PAGE_SIZE;
-			else
-				mss->private_clean += PAGE_SIZE;
-		}
-	} while (pte++, addr += PAGE_SIZE, addr != end);
+		walker->func(walker, pte, addr);
+	} while (pte++, addr += PAGE_SIZE, addr != walker->end);
 	pte_unmap_unlock(pte - 1, ptl);
 	cond_resched();
 }
 
-static inline void smaps_pmd_range(struct vm_area_struct *vma, pud_t *pud,
-				unsigned long addr, unsigned long end,
-				struct mem_size_stats *mss)
+static inline void walk_pmds(struct pte_walker *walker, pud_t *pud)
 {
-	pmd_t *pmd;
+	unsigned long addr = walker->start;
+	unsigned long end = walker->end;
 	unsigned long next;
+	pmd_t *pmd;
 
 	pmd = pmd_offset(pud, addr);
 	do {
 		next = pmd_addr_end(addr, end);
 		if (pmd_none_or_clear_bad(pmd))
 			continue;
-		smaps_pte_range(vma, pmd, addr, next, mss);
+		walk_ptes(walker, pmd);
 	} while (pmd++, addr = next, addr != end);
 }
 
-static inline void smaps_pud_range(struct vm_area_struct *vma, pgd_t *pgd,
-				unsigned long addr, unsigned long end,
-				struct mem_size_stats *mss)
+static inline void walk_puds(struct pte_walker *walker, pgd_t *pgd)
 {
-	pud_t *pud;
+	unsigned long addr = walker->start;
+	unsigned long end = walker->end;
 	unsigned long next;
+	pud_t *pud;
 
 	pud = pud_offset(pgd, addr);
 	do {
 		next = pud_addr_end(addr, end);
 		if (pud_none_or_clear_bad(pud))
 			continue;
-		smaps_pmd_range(vma, pud, addr, next, mss);
+		walk_pmds(walker, pud);
 	} while (pud++, addr = next, addr != end);
 }
 
-static inline void smaps_pgd_range(struct vm_area_struct *vma,
-				unsigned long addr, unsigned long end,
-				struct mem_size_stats *mss)
+static inline void walk_pgds(struct pte_walker *walker)
 {
-	pgd_t *pgd;
+	unsigned long addr = walker->start;
+	unsigned long end = walker->end;
 	unsigned long next;
+	pgd_t *pgd;
 
-	pgd = pgd_offset(vma->vm_mm, addr);
+	pgd = pgd_offset(walker->vma->vm_mm, addr);
 	do {
 		next = pgd_addr_end(addr, end);
 		if (pgd_none_or_clear_bad(pgd))
 			continue;
-		smaps_pud_range(vma, pgd, addr, next, mss);
+		walk_puds(walker, pgd);
 	} while (pgd++, addr = next, addr != end);
 }
 
+/*
+ * Called for each PTE in the struct pte_walker address range.  For all normal,
+ * present pages, we accumulate the size (in pages) grouped by shared and
+ * private attributes and dirty bits.
+ */
+static void smaps_pte_func(struct pte_walker *walker, pte_t *pte,
+			   unsigned long addr)
+{
+	struct mem_size_stats *mss = walker->private;
+	struct page *page;
+	pte_t ptent;
+
+	ptent = *pte;
+	if (!pte_present(ptent))
+		return;
+
+	mss->resident += PAGE_SIZE;
+
+	page = vm_normal_page(walker->vma, addr, ptent);
+	if (!page)
+		return;
+
+	if (page_mapcount(page) >= 2) {
+		if (pte_dirty(ptent))
+			mss->shared_dirty += PAGE_SIZE;
+		else
+			mss->shared_clean += PAGE_SIZE;
+	} else {
+		if (pte_dirty(ptent))
+			mss->private_dirty += PAGE_SIZE;
+		else
+			mss->private_clean += PAGE_SIZE;
+	}
+}
+
+/*
+ * Displays the smap for the process.  smaps_pte_func() is called for each PTE
+ * in the range from vma->vm_start to vma->vm_end.
+ */
 static int show_smap(struct seq_file *m, void *v)
 {
 	struct vm_area_struct *vma = v;
 	struct mem_size_stats mss;
+	struct pte_walker walker = {
+		.vma		= vma,
+		.start		= vma->vm_start,
+		.end		= vma->vm_end,
+		.private	= &mss,
+		.func		= smaps_pte_func,
+	};
 
 	memset(&mss, 0, sizeof mss);
 	if (vma->vm_mm && !is_vm_hugetlb_page(vma))
-		smaps_pgd_range(vma, vma->vm_start, vma->vm_end, &mss);
+		walk_pgds(&walker);
 	return show_map_internal(m, v, &mss);
 }
 

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [patch 2/3] smaps: add pages referenced count to smaps
  2007-02-07  5:26 [patch 1/3] smaps: extract pte walker from smaps code David Rientjes
@ 2007-02-07  5:27 ` David Rientjes
  2007-02-07  5:27   ` [patch 3/3] smaps: add clear_refs file to clear reference David Rientjes
  2007-02-07  5:44   ` [patch 2/3] smaps: add pages referenced count to smaps Andrew Morton
  0 siblings, 2 replies; 7+ messages in thread
From: David Rientjes @ 2007-02-07  5:27 UTC (permalink / raw)
  To: Andrew Morton; +Cc: Hugh Dickins, Paul Mundt, Christoph Lameter, linux-kernel

Adds an additional unsigned long field to struct mem_size_stats:
referenced.  For each page table entry that is walked for the VMA in the
smaps code, this field is incremented if PG_referenced is set.

An additional line was added to the /proc/pid/smaps output for each VMA
to indicate how many pages within it are currently marked as accessed
(referenced).

Cc: Hugh Dickins <hugh@veritas.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Christoph Lameter <clameter@sgi.com>
Signed-off-by: David Rientjes <rientjes@google.com>
---
 fs/proc/task_mmu.c |   20 +++++++++++++-------
 1 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index e87824b..a1f5e87 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -120,6 +120,7 @@ struct mem_size_stats
 	unsigned long shared_dirty;
 	unsigned long private_clean;
 	unsigned long private_dirty;
+	unsigned long referenced;
 };
 
 struct pte_walker {
@@ -190,18 +191,20 @@ static int show_map_internal(struct seq_file *m, void *v, struct mem_size_stats
 
 	if (mss)
 		seq_printf(m,
-			   "Size:          %8lu kB\n"
-			   "Rss:           %8lu kB\n"
-			   "Shared_Clean:  %8lu kB\n"
-			   "Shared_Dirty:  %8lu kB\n"
-			   "Private_Clean: %8lu kB\n"
-			   "Private_Dirty: %8lu kB\n",
+			   "Size:           %8lu kB\n"
+			   "Rss:            %8lu kB\n"
+			   "Shared_Clean:   %8lu kB\n"
+			   "Shared_Dirty:   %8lu kB\n"
+			   "Private_Clean:  %8lu kB\n"
+			   "Private_Dirty:  %8lu kB\n"
+			   "Pgs_Referenced: %8li\n",
 			   (vma->vm_end - vma->vm_start) >> 10,
 			   mss->resident >> 10,
 			   mss->shared_clean  >> 10,
 			   mss->shared_dirty  >> 10,
 			   mss->private_clean >> 10,
-			   mss->private_dirty >> 10);
+			   mss->private_dirty >> 10,
+			   mss->referenced);
 
 	if (m->count < m->size)  /* vma is copied successfully */
 		m->version = (vma != get_gate_vma(task))? vma->vm_start: 0;
@@ -302,6 +305,9 @@ static void smaps_pte_func(struct pte_walker *walker, pte_t *pte,
 	if (!page)
 		return;
 
+	/* Accumulate the number of pages that have been accessed. */
+	if (PageReferenced(page))
+		mss->referenced++;
 	if (page_mapcount(page) >= 2) {
 		if (pte_dirty(ptent))
 			mss->shared_dirty += PAGE_SIZE;

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [patch 3/3] smaps: add clear_refs file to clear reference
  2007-02-07  5:27 ` [patch 2/3] smaps: add pages referenced count to smaps David Rientjes
@ 2007-02-07  5:27   ` David Rientjes
  2007-02-07  5:44   ` [patch 2/3] smaps: add pages referenced count to smaps Andrew Morton
  1 sibling, 0 replies; 7+ messages in thread
From: David Rientjes @ 2007-02-07  5:27 UTC (permalink / raw)
  To: Andrew Morton; +Cc: Hugh Dickins, Paul Mundt, Christoph Lameter, linux-kernel

Adds an additional file to /proc/pid: clear_refs.  When any non-zero
number is written to this file, all the PG_referenced flags (meaning the
page has been accessed) are cleared within each VMA for the corresponding
task.

It is now possible to measure how much memory a task is using by clearing
the reference bits with

	echo 1 > /proc/pid/clear_refs

and checking the reference count for each VMA from the /proc/pid/smaps
output at a time interval later.

The /proc/pid/clear_refs file is only writable by the user who owns the
task.

Cc: Hugh Dickins <hugh@veritas.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Christoph Lameter <clameter@sgi.com>
Signed-off-by: David Rientjes <rientjes@google.com>
---
 fs/proc/base.c          |   31 +++++++++++++++++++++++++++++++
 fs/proc/task_mmu.c      |   36 ++++++++++++++++++++++++++++++++++++
 include/linux/proc_fs.h |    1 +
 3 files changed, 68 insertions(+), 0 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index 1a979ea..b50315f 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -715,6 +715,35 @@ static struct file_operations proc_oom_adjust_operations = {
 	.write		= oom_adjust_write,
 };
 
+static ssize_t clear_refs_write(struct file *file, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct task_struct *task;
+	char buffer[PROC_NUMBUF], *end;
+
+	memset(buffer, 0, sizeof(buffer));
+	if (count > sizeof(buffer) - 1)
+		count = sizeof(buffer) - 1;
+	if (copy_from_user(buffer, buf, count))
+		return -EFAULT;
+	if (!simple_strtol(buffer, &end, 0))
+		return -EINVAL;
+	if (*end == '\n')
+		end++;
+	task = get_proc_task(file->f_path.dentry->d_inode);
+	if (!task)
+		return -ESRCH;
+	clear_refs_smap(task->mm->mmap);
+	put_task_struct(task);
+	if (end - buffer == 0)
+		return -EIO;
+	return end - buffer;
+}
+
+static struct file_operations proc_clear_refs_operations = {
+	.write		= clear_refs_write,
+};
+
 #ifdef CONFIG_AUDITSYSCALL
 #define TMPBUFLEN 21
 static ssize_t proc_loginuid_read(struct file * file, char __user * buf,
@@ -1856,6 +1885,7 @@ static struct pid_entry tgid_base_stuff[] = {
 	REG("mounts",     S_IRUGO, mounts),
 	REG("mountstats", S_IRUSR, mountstats),
 #ifdef CONFIG_MMU
+	REG("clear_refs", S_IWUSR, clear_refs),
 	REG("smaps",      S_IRUGO, smaps),
 #endif
 #ifdef CONFIG_SECURITY
@@ -2137,6 +2167,7 @@ static struct pid_entry tid_base_stuff[] = {
 	LNK("exe",       exe),
 	REG("mounts",    S_IRUGO, mounts),
 #ifdef CONFIG_MMU
+	REG("clear_refs", S_IWUSR, clear_refs),
 	REG("smaps",     S_IRUGO, smaps),
 #endif
 #ifdef CONFIG_SECURITY
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index a1f5e87..10714c9 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -322,6 +322,26 @@ static void smaps_pte_func(struct pte_walker *walker, pte_t *pte,
 }
 
 /*
+ * Called for each PTE in the struct pte_walker address range.  For all normal,
+ * present pages, we clear their referenced bits.
+ */
+static void clear_refs_pte_func(struct pte_walker *walker, pte_t *pte,
+				unsigned long addr)
+{
+	struct page *page;
+	pte_t ptent;
+
+	ptent = *pte;
+	if (!pte_present(ptent))
+		return;
+
+	page = vm_normal_page(walker->vma, addr, ptent);
+	if (!page)
+		return;
+	ClearPageReferenced(page);
+}
+
+/*
  * Displays the smap for the process.  smaps_pte_func() is called for each PTE
  * in the range from vma->vm_start to vma->vm_end.
  */
@@ -343,6 +363,22 @@ static int show_smap(struct seq_file *m, void *v)
 	return show_map_internal(m, v, &mss);
 }
 
+void clear_refs_smap(struct vm_area_struct *vma)
+{
+	for (; vma; vma = vma->vm_next) {
+		struct pte_walker walker = {
+			.vma		= vma,
+			.start		= vma->vm_start,
+			.end		= vma->vm_end,
+			.private	= NULL,
+			.func		= clear_refs_pte_func,
+		};
+
+		if (vma->vm_mm && !is_vm_hugetlb_page(vma))
+			walk_pgds(&walker);
+	};
+}
+
 static void *m_start(struct seq_file *m, loff_t *pos)
 {
 	struct proc_maps_private *priv = m->private;
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index 87dec8f..f3d426b 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -104,6 +104,7 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir);
 unsigned long task_vsize(struct mm_struct *);
 int task_statm(struct mm_struct *, int *, int *, int *, int *);
 char *task_mem(struct mm_struct *, char *);
+void clear_refs_smap(struct vm_area_struct *);
 
 extern struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
 						struct proc_dir_entry *parent);

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [patch 2/3] smaps: add pages referenced count to smaps
  2007-02-07  5:27 ` [patch 2/3] smaps: add pages referenced count to smaps David Rientjes
  2007-02-07  5:27   ` [patch 3/3] smaps: add clear_refs file to clear reference David Rientjes
@ 2007-02-07  5:44   ` Andrew Morton
  2007-02-07  5:56     ` David Rientjes
  1 sibling, 1 reply; 7+ messages in thread
From: Andrew Morton @ 2007-02-07  5:44 UTC (permalink / raw)
  To: David Rientjes; +Cc: Hugh Dickins, Paul Mundt, Christoph Lameter, linux-kernel

On Tue, 6 Feb 2007 21:27:03 -0800 (PST) David Rientjes <rientjes@google.com> wrote:

> @@ -190,18 +191,20 @@ static int show_map_internal(struct seq_file *m, void *v, struct mem_size_stats
>  
>  	if (mss)
>  		seq_printf(m,
> -			   "Size:          %8lu kB\n"
> -			   "Rss:           %8lu kB\n"
> -			   "Shared_Clean:  %8lu kB\n"
> -			   "Shared_Dirty:  %8lu kB\n"
> -			   "Private_Clean: %8lu kB\n"
> -			   "Private_Dirty: %8lu kB\n",
> +			   "Size:           %8lu kB\n"
> +			   "Rss:            %8lu kB\n"
> +			   "Shared_Clean:   %8lu kB\n"
> +			   "Shared_Dirty:   %8lu kB\n"
> +			   "Private_Clean:  %8lu kB\n"
> +			   "Private_Dirty:  %8lu kB\n"
> +			   "Pgs_Referenced: %8li\n",

I guess we might as well make this %8lu kB as well?

>  			   (vma->vm_end - vma->vm_start) >> 10,
>  			   mss->resident >> 10,
>  			   mss->shared_clean  >> 10,
>  			   mss->shared_dirty  >> 10,
>  			   mss->private_clean >> 10,
> -			   mss->private_dirty >> 10);
> +			   mss->private_dirty >> 10,
> +			   mss->referenced);
>
> ...
>
> +	/* Accumulate the number of pages that have been accessed. */
> +	if (PageReferenced(page))
> +		mss->referenced++;

This is testing the software-referenced bit.  We want to be counting the
pte-referenced bits (pte_young).

Hence poking clear_refs should run pte_mkold().

Perhaps we should also be running ClearPageReferenced and counting pages
which are pte_young()||PageReferenced.


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [patch 2/3] smaps: add pages referenced count to smaps
  2007-02-07  5:44   ` [patch 2/3] smaps: add pages referenced count to smaps Andrew Morton
@ 2007-02-07  5:56     ` David Rientjes
  2007-02-07  6:06       ` Andrew Morton
  0 siblings, 1 reply; 7+ messages in thread
From: David Rientjes @ 2007-02-07  5:56 UTC (permalink / raw)
  To: Andrew Morton; +Cc: Hugh Dickins, Paul Mundt, Christoph Lameter, linux-kernel

On Tue, 6 Feb 2007, Andrew Morton wrote:

> > @@ -190,18 +191,20 @@ static int show_map_internal(struct seq_file *m, void *v, struct mem_size_stats
> >  
> >  	if (mss)
> >  		seq_printf(m,
> > -			   "Size:          %8lu kB\n"
> > -			   "Rss:           %8lu kB\n"
> > -			   "Shared_Clean:  %8lu kB\n"
> > -			   "Shared_Dirty:  %8lu kB\n"
> > -			   "Private_Clean: %8lu kB\n"
> > -			   "Private_Dirty: %8lu kB\n",
> > +			   "Size:           %8lu kB\n"
> > +			   "Rss:            %8lu kB\n"
> > +			   "Shared_Clean:   %8lu kB\n"
> > +			   "Shared_Dirty:   %8lu kB\n"
> > +			   "Private_Clean:  %8lu kB\n"
> > +			   "Private_Dirty:  %8lu kB\n"
> > +			   "Pgs_Referenced: %8li\n",
> 
> I guess we might as well make this %8lu kB as well?
> 

Ah, %8lu would work since it's a count of pages and not a size.

> This is testing the software-referenced bit.  We want to be counting the
> pte-referenced bits (pte_young).
> 
> Hence poking clear_refs should run pte_mkold().
> 
> Perhaps we should also be running ClearPageReferenced and counting pages
> which are pte_young()||PageReferenced.
> 

Ok.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [patch 2/3] smaps: add pages referenced count to smaps
  2007-02-07  5:56     ` David Rientjes
@ 2007-02-07  6:06       ` Andrew Morton
  0 siblings, 0 replies; 7+ messages in thread
From: Andrew Morton @ 2007-02-07  6:06 UTC (permalink / raw)
  To: David Rientjes; +Cc: Hugh Dickins, Paul Mundt, Christoph Lameter, linux-kernel

On Tue, 6 Feb 2007 21:56:23 -0800 (PST) David Rientjes <rientjes@google.com> wrote:

> On Tue, 6 Feb 2007, Andrew Morton wrote:
> 
> > > @@ -190,18 +191,20 @@ static int show_map_internal(struct seq_file *m, void *v, struct mem_size_stats
> > >  
> > >  	if (mss)
> > >  		seq_printf(m,
> > > -			   "Size:          %8lu kB\n"
> > > -			   "Rss:           %8lu kB\n"
> > > -			   "Shared_Clean:  %8lu kB\n"
> > > -			   "Shared_Dirty:  %8lu kB\n"
> > > -			   "Private_Clean: %8lu kB\n"
> > > -			   "Private_Dirty: %8lu kB\n",
> > > +			   "Size:           %8lu kB\n"
> > > +			   "Rss:            %8lu kB\n"
> > > +			   "Shared_Clean:   %8lu kB\n"
> > > +			   "Shared_Dirty:   %8lu kB\n"
> > > +			   "Private_Clean:  %8lu kB\n"
> > > +			   "Private_Dirty:  %8lu kB\n"
> > > +			   "Pgs_Referenced: %8li\n",
> > 
> > I guess we might as well make this %8lu kB as well?
> > 
> 
> Ah, %8lu would work since it's a count of pages and not a size.

All the other fields in there are using kbytes.  We might as well be
consistent.

Plus if we express the quantity in bytes, userspace doesn't need to know
what the pagesize is.  If we were to express it in pages, an app on a 64k
pagesize kernel will show very different numbers from the same app on a 4k
pagesize kernel.


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [patch 3/3] smaps: add clear_refs file to clear reference
  2007-02-09 19:36 ` [patch 2/3] smaps: add pages referenced count to smaps David Rientjes
@ 2007-02-09 19:36   ` David Rientjes
  0 siblings, 0 replies; 7+ messages in thread
From: David Rientjes @ 2007-02-09 19:36 UTC (permalink / raw)
  To: Andrew Morton; +Cc: Hugh Dickins, Paul Mundt, Christoph Lameter, linux-kernel

Adds /proc/pid/clear_refs.  When any non-zero number is written to this
file, pte_mkold() and ClearPageReferenced() is called for each pte and its
corresponding page, respectively, in that task's VMAs.  This file is only
writable by the user who owns the task.

It is now possible to measure _approximately_ how much memory a task is
using by clearing the reference bits with

	echo 1 > /proc/pid/clear_refs

and checking the reference count for each VMA from the /proc/pid/smaps
output at a measured time interval.  For example, to observe the
approximate change in memory footprint for a task, write a script that
clears the references (echo 1 > /proc/pid/clear_refs), sleeps, and then
greps for Pgs_Referenced and extracts the size in kB.  Add the sizes for
each VMA together for the total referenced footprint.  Moments later,
repeat the process and observe the difference.

For example, using an efficient Mozilla:

	accumulated time		referenced memory
	----------------		-----------------
		 0 s				 408 kB
		 1 s				 408 kB
		 2 s				 556 kB
		 3 s				1028 kB
		 4 s				 872 kB
		 5 s				1956 kB
		 6 s				 416 kB
		 7 s				1560 kB
		 8 s				2336 kB
		 9 s				1044 kB
		10 s				 416 kB

This is a valuable tool to get an approximate measurement of the memory
footprint for a task.

Cc: Hugh Dickins <hugh@veritas.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Christoph Lameter <clameter@sgi.com>
Signed-off-by: David Rientjes <rientjes@google.com>
---
 fs/proc/base.c          |   31 +++++++++++++++++++++++++++++++
 fs/proc/task_mmu.c      |   37 +++++++++++++++++++++++++++++++++++++
 include/linux/proc_fs.h |    1 +
 3 files changed, 69 insertions(+), 0 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -715,6 +715,35 @@ static struct file_operations proc_oom_adjust_operations = {
 	.write		= oom_adjust_write,
 };
 
+static ssize_t clear_refs_write(struct file *file, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct task_struct *task;
+	char buffer[PROC_NUMBUF], *end;
+
+	memset(buffer, 0, sizeof(buffer));
+	if (count > sizeof(buffer) - 1)
+		count = sizeof(buffer) - 1;
+	if (copy_from_user(buffer, buf, count))
+		return -EFAULT;
+	if (!simple_strtol(buffer, &end, 0))
+		return -EINVAL;
+	if (*end == '\n')
+		end++;
+	task = get_proc_task(file->f_path.dentry->d_inode);
+	if (!task)
+		return -ESRCH;
+	clear_refs_smap(task->mm->mmap);
+	put_task_struct(task);
+	if (end - buffer == 0)
+		return -EIO;
+	return end - buffer;
+}
+
+static struct file_operations proc_clear_refs_operations = {
+	.write		= clear_refs_write,
+};
+
 #ifdef CONFIG_AUDITSYSCALL
 #define TMPBUFLEN 21
 static ssize_t proc_loginuid_read(struct file * file, char __user * buf,
@@ -1856,6 +1885,7 @@ static struct pid_entry tgid_base_stuff[] = {
 	REG("mounts",     S_IRUGO, mounts),
 	REG("mountstats", S_IRUSR, mountstats),
 #ifdef CONFIG_MMU
+	REG("clear_refs", S_IWUSR, clear_refs),
 	REG("smaps",      S_IRUGO, smaps),
 #endif
 #ifdef CONFIG_SECURITY
@@ -2137,6 +2167,7 @@ static struct pid_entry tid_base_stuff[] = {
 	LNK("exe",       exe),
 	REG("mounts",    S_IRUGO, mounts),
 #ifdef CONFIG_MMU
+	REG("clear_refs", S_IWUSR, clear_refs),
 	REG("smaps",     S_IRUGO, smaps),
 #endif
 #ifdef CONFIG_SECURITY
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -254,6 +254,36 @@ static void smaps_one_pmd(struct vm_area_struct *vma, pmd_t *pmd,
 	cond_resched();
 }
 
+static void clear_refs_one_pmd(struct vm_area_struct *vma, pmd_t *pmd,
+			       unsigned long addr, unsigned long end,
+			       void *private)
+{
+	pte_t *pte, ptent;
+	spinlock_t *ptl;
+	struct page *page;
+
+	pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
+	for (; addr != end; pte++, addr += PAGE_SIZE) {
+		ptent = *pte;
+		if (!pte_present(ptent))
+			continue;
+
+		page = vm_normal_page(vma, addr, ptent);
+		if (!page)
+			continue;
+
+		/* Clear accessed and referenced bits. */
+		if (pte_young(ptent)) {
+			ptent = pte_mkold(ptent);
+			set_pte_at(vma->vm_mm, addr, pte, ptent);
+			flush_tlb_page(vma, addr);
+		}
+		ClearPageReferenced(page);
+	}
+	pte_unmap_unlock(pte - 1, ptl);
+	cond_resched();
+}
+
 static inline void for_each_pmd_in_pud(struct pmd_walker *walker, pud_t *pud,
 				       unsigned long addr, unsigned long end)
 {
@@ -320,6 +350,13 @@ static int show_smap(struct seq_file *m, void *v)
 	return show_map_internal(m, v, &mss);
 }
 
+void clear_refs_smap(struct vm_area_struct *vma)
+{
+	for (; vma; vma = vma->vm_next)
+		if (vma->vm_mm && !is_vm_hugetlb_page(vma))
+			for_each_pmd(vma, clear_refs_one_pmd, NULL);
+}
+
 static void *m_start(struct seq_file *m, loff_t *pos)
 {
 	struct proc_maps_private *priv = m->private;
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -104,6 +104,7 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir);
 unsigned long task_vsize(struct mm_struct *);
 int task_statm(struct mm_struct *, int *, int *, int *, int *);
 char *task_mem(struct mm_struct *, char *);
+void clear_refs_smap(struct vm_area_struct *);
 
 extern struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
 						struct proc_dir_entry *parent);

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2007-02-09 19:36 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-02-07  5:26 [patch 1/3] smaps: extract pte walker from smaps code David Rientjes
2007-02-07  5:27 ` [patch 2/3] smaps: add pages referenced count to smaps David Rientjes
2007-02-07  5:27   ` [patch 3/3] smaps: add clear_refs file to clear reference David Rientjes
2007-02-07  5:44   ` [patch 2/3] smaps: add pages referenced count to smaps Andrew Morton
2007-02-07  5:56     ` David Rientjes
2007-02-07  6:06       ` Andrew Morton
2007-02-09 19:35 [patch 1/3] smaps: extract pmd walker from smaps code David Rientjes
2007-02-09 19:36 ` [patch 2/3] smaps: add pages referenced count to smaps David Rientjes
2007-02-09 19:36   ` [patch 3/3] smaps: add clear_refs file to clear reference David Rientjes

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.