Skip to contentSkip to conversation

mm: inactive-clean list

PATCH In review Peter Zijlstra 7 participants 27 messages Jul 17, 2006 – Jul 27, 2006

Subsystems (13)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 mm: inactive-clean list

This patch implements the inactive_clean list spoken of during the VM summit. The LRU tail pages will be unmapped and ready to free, but not freeed. This gives reclaim an extra chance.

The only down-side to this patch is that it puts another requirement for the zone lock into mark_page_accessed(), meaning that the use-once cleanup cannot get fully rid of that zone lock there.

---
 include/linux/mm_inline.h  |   23 +++++++++
 include/linux/mmzone.h     |    5 ++
 include/linux/page-flags.h |    6 ++
 include/linux/swap.h       |    1 
 include/linux/sysctl.h     |    1 
 kernel/sysctl.c            |   11 ++++
 mm/page_alloc.c            |   62 ++++++++++++++++++++++++-
 mm/swap.c                  |   44 ++++++++++--------
 mm/swapfile.c              |    4 -
 mm/vmscan.c                |  108 ++++++++++++++++++++++++++++++++++++++-------
 10 files changed, 225 insertions(+), 40 deletions(-)
include/linux/swap.h +0 -1
Index: linux-2.6-dirty/include/linux/swap.h
===================================================================
@@ -173,7 +173,6 @@ extern unsigned int nr_free_pagecache_pa
 /* linux/mm/swap.c */
 extern void FASTCALL(lru_cache_add(struct page *));
 extern void FASTCALL(lru_cache_add_active(struct page *));
-extern void FASTCALL(activate_page(struct page *));
 extern void FASTCALL(mark_page_accessed(struct page *));
 extern void lru_add_drain(void);
 extern int lru_add_drain_all(void);
mm/swapfile.c +2 -2
Index: linux-2.6-dirty/mm/swapfile.c
===================================================================
@@ -499,7 +499,7 @@ static void unuse_pte(struct vm_area_str
 	 * Move the page to the active list so it is not
 	 * immediately swapped out again after swapon.
 	 */
-	activate_page(page);
+	mark_page_accessed(page);
 }
 
 static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
@@ -601,7 +601,7 @@ static int unuse_mm(struct mm_struct *mm
 		 * Activate page so shrink_cache is unlikely to unmap its
 		 * ptes while lock is dropped, so swapoff can make progress.
 		 */
-		activate_page(page);
+		mark_page_accessed(page);
 		unlock_page(page);
 		down_read(&mm->mmap_sem);
 		lock_page(page);
mm/swap.c +26 -18

63 lines not on this page · raw message

include/linux/mm_inline.h +23 -0
Index: linux-2.6-dirty/include/linux/mm_inline.h
===================================================================
@@ -14,6 +14,13 @@ add_page_to_inactive_list(struct zone *z
 }
 
 static inline void
+add_page_to_clean_list(struct zone *zone, struct page *page)
+{
+	list_add(&page->lru, &zone->clean_list);
+	zone->nr_clean++;
+}
+
+static inline void
 del_page_from_active_list(struct zone *zone, struct page *page)
 {
 	list_del(&page->lru);
@@ -27,6 +34,17 @@ del_page_from_inactive_list(struct zone 
 	zone->nr_inactive--;
 }
 
+void wakeup_kswapd(struct zone *zone, int order);
+
+static inline void
+del_page_from_clean_list(struct zone *zone, struct page *page)
+{
+	list_del(&page->lru);
+	zone->nr_clean--;
+	if (zone->nr_clean + zone->free_pages < zone->clean_low)
+		wakeup_kswapd(zone, 0);
+}
+
 static inline void
 del_page_from_lru(struct zone *zone, struct page *page)
 {
@@ -34,6 +52,11 @@ del_page_from_lru(struct zone *zone, str
 	if (PageActive(page)) {
 		__ClearPageActive(page);
 		zone->nr_active--;
+	} else if (PageClean(page)) {
+		__ClearPageClean(page);
+		zone->nr_clean--;
+		if (zone->nr_clean + zone->free_pages < zone->clean_low)
+			wakeup_kswapd(zone, 0);
 	} else {
 		zone->nr_inactive--;
 	}
include/linux/mmzone.h +5 -0
Index: linux-2.6-dirty/include/linux/mmzone.h
===================================================================
@@ -155,10 +155,13 @@ struct zone {
 	spinlock_t		lru_lock;	
 	struct list_head	active_list;
 	struct list_head	inactive_list;
+	struct list_head	clean_list;
 	unsigned long		nr_scan_active;
 	unsigned long		nr_scan_inactive;
 	unsigned long		nr_active;
 	unsigned long		nr_inactive;
+	unsigned long		nr_clean;
+	unsigned long		clean_low, clean_high;
 	unsigned long		pages_scanned;	   /* since last reclaim */
 	int			all_unreclaimable; /* All pages pinned */
 
@@ -397,6 +400,8 @@ struct ctl_table;
 struct file;
 int min_free_kbytes_sysctl_handler(struct ctl_table *, int, struct file *, 
 					void __user *, size_t *, loff_t *);
+int min_clean_kbytes_sysctl_handler(struct ctl_table *, int, struct file *,
+					void __user *, size_t *, loff_t *);
 extern int sysctl_lowmem_reserve_ratio[MAX_NR_ZONES-1];
 int lowmem_reserve_ratio_sysctl_handler(struct ctl_table *, int, struct file *,
 					void __user *, size_t *, loff_t *);
include/linux/page-flags.h +6 -0
Index: linux-2.6-dirty/include/linux/page-flags.h
===================================================================
@@ -90,6 +90,7 @@
 #define PG_nosave_free		18	/* Free, should not be written */
 #define PG_buddy		19	/* Page is free, on buddy lists */
 
+#define PG_clean		20	/* Page is on the clean list */
 
 #if (BITS_PER_LONG > 32)
 /*
@@ -372,6 +373,11 @@ extern void __mod_page_state_offset(unsi
 #define SetPageUncached(page)	set_bit(PG_uncached, &(page)->flags)
 #define ClearPageUncached(page)	clear_bit(PG_uncached, &(page)->flags)
 
+#define PageClean(page)		test_bit(PG_clean, &(page)->flags)
+#define SetPageClean(page)	set_bit(PG_clean, &(page)->flags)
+#define ClearPageClean(page)	clear_bit(PG_clean, &(page)->flags)
+#define __ClearPageClean(page)	__clear_bit(PG_clean, &(page)->flags)
+
 struct page;	/* forward declaration */
 
 int test_clear_page_dirty(struct page *page);
mm/page_alloc.c +59 -3

138 lines not on this page · raw message

mm/vmscan.c +92 -16

184 lines not on this page · raw message

include/linux/sysctl.h +1 -0
Index: linux-2.6-dirty/include/linux/sysctl.h
===================================================================
@@ -191,6 +191,7 @@ enum
 	VM_ZONE_RECLAIM_INTERVAL=32, /* time period to wait after reclaim failure */
 	VM_PANIC_ON_OOM=33,	/* panic at out-of-memory */
 	VM_VDSO_ENABLED=34,	/* map VDSO into new processes? */
+	VM_MIN_CLEAN_KBYTES=35, /* Minimun clean kilobytes to maintain */
 };
 
 
kernel/sysctl.c +11 -0
Index: linux-2.6-dirty/kernel/sysctl.c
===================================================================
@@ -68,6 +68,7 @@ extern char core_pattern[];
 extern int cad_pid;
 extern int pid_max;
 extern int min_free_kbytes;
+extern int min_clean_kbytes;
 extern int printk_ratelimit_jiffies;
 extern int printk_ratelimit_burst;
 extern int pid_max_min, pid_max_max;
@@ -851,6 +852,16 @@ static ctl_table vm_table[] = {
 		.extra1		= &zero,
 	},
 	{
+		.ctl_name	= VM_MIN_CLEAN_KBYTES,
+		.procname	= "min_clean_kbytes",
+		.data		= &min_clean_kbytes,
+		.maxlen		= sizeof(min_clean_kbytes),
+		.mode		= 0644,
+		.proc_handler	= &min_clean_kbytes_sysctl_handler,
+		.strategy	= &sysctl_intvec,
+		.extra1		= &zero,
+	},
+	{
 		.ctl_name	= VM_PERCPU_PAGELIST_FRACTION,
 		.procname	= "percpu_pagelist_fraction",
 		.data		= &percpu_pagelist_fraction,

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#1 lore raw
Quoted 3 lines · Peter Zijlstra

This patch implements the inactive_clean list spoken of during the VM summit. The LRU tail pages will be unmapped and ready to free, but not freeed. This gives reclaim an extra chance.

I thought we wanted to just track the number of unmapped clean pages and insure that they do not go under a certain limit? That would not require any locking changes but just a new zoned counter and a check in the dirty handling path.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#2 lore raw
Quoted 10 lines · Christoph Lameter
Quoted 3 lines · Peter Zijlstra

This patch implements the inactive_clean list spoken of during the VM summit. The LRU tail pages will be unmapped and ready to free, but not freeed. This gives reclaim an extra chance.

I thought we wanted to just track the number of unmapped clean pages and insure that they do not go under a certain limit? That would not require any locking changes but just a new zoned counter and a check in the dirty handling path.

The problem I see with that is that we cannot create new unmapped clean pages. Where will we get new pages to satisfy our demand when there is nothing mmap'ed.

This approach will generate them by forceing some pages into swap space.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#3 lore raw
Quoted 8 lines · Peter Zijlstra
Quoted 4 lines

I thought we wanted to just track the number of unmapped clean pages and insure that they do not go under a certain limit? That would not require any locking changes but just a new zoned counter and a check in the dirty handling path.

The problem I see with that is that we cannot create new unmapped clean pages. Where will we get new pages to satisfy our demand when there is nothing mmap'ed.

Hmmm... I am not sure that we both have this straight yet.

Adding logic to determine the number of clean pages is not necessary. The number of clean pages in the pagecache can be determined by:

global_page_state(NR_FILE_PAGES) - global_page_state(NR_FILE_DIRTY)

That number can be increased by writeout and so I think we want this to be checked in the throttling path. Swapout is only useful for anonymous pages. Dirty anonymous pages are not tracked and do not contribute to the NR_FILE_DIRTY (formerly nr_dirty). We only track the number of anonymous pages in NR_ANON_PAGES. Swapout could be used to reduce NR_ANON_PAGES if memory becomes tight.

The intend of insuring that a certain number of clean pages exist seems to be to guarantee that a certain amount of memory is freeable without having to go through a filesystem.

Pages that are available without file system activity are:

1. The already free pages.

2. The clean pagecache pages.

For a zone this is

zone->free_pages + zone_page_state(zone, NR_FILE_PAGES) - zone_page_state(zone, NR_FILE_DIRTY)

If this goes below a certain limit then we either have to:

1. If NR_FILE_DIRTY is significant then we can increase the number of reclaimable pages by writing them out.

2. If NR_FILE_DIRTY and NR_FILE_PAGES are low then writeout does not help us. NR_ANON_PAGES is likely big. So we could swap some anonymous pages out to increase zone->free_pages instead. Performance wise this is a bad move. So we should prefer writeout.

However, the above scheme assumes that all pagecache pages can ne unmapped if necessary. This may not be desirable since we may then have no executable pages available anymore and create a significant amount of disk traffic. If we would track the number of dirty unmapped pages (by addding NR_UNMAPPED_DIRTY) then we could guarantee available memory that would leave the pages in use by processes alone.

If we impose a limit on the number of free pages + the number of unmapped clean pagecache pages then we have a reserve memory pool that can be accessed without too much impact on performance. Its basically another trigger for writeout.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#4 lore raw
Quoted 12 lines · Christoph Lameter
Quoted 7 lines · Peter Zijlstra
Quoted 3 lines

I thought we wanted to just track the number of unmapped clean pages and insure that they do not go under a certain limit? That would not require any locking changes but just a new zoned counter and a check in the dirty
handling path.

The problem I see with that is that we cannot create new unmapped clean pages. Where will we get new pages to satisfy our demand when there is nothing mmap'ed.

Quoted 5 lines

Hmmm... I am not sure that we both have this straight yet.

Adding logic to determine the number of clean pages is not necessary. The number of clean pages in the pagecache can be determined by:

global_page_state(NR_FILE_PAGES) - global_page_state(NR_FILE_DIRTY)

It's not that simple. We also need to deal with other types of non-freeable pages, such as memlocked.

Someone remind me why we can't remove the memlocked pages from the LRU again? Apart from needing a refcount of how many times they're memlocked (or we just shove them back whenever they're unlocked, and let it fall out again when we walk the list, but that doesn't fix the accounting problem).

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#5 lore raw
Quoted 5 lines · Martin J. Bligh

Someone remind me why we can't remove the memlocked pages from the LRU again? Apart from needing a refcount of how many times they're memlocked (or we just shove them back whenever they're unlocked, and let it fall out again when we walk the list, but that doesn't fix the accounting problem).

We simply do not unmap memlocked pages (see try_to_unmap). And therefore they are not reclaimable.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#6 lore raw
Quoted 10 lines · Christoph Lameter
Quoted 5 lines · Martin J. Bligh

Someone remind me why we can't remove the memlocked pages from the LRU again? Apart from needing a refcount of how many times they're memlocked (or we just shove them back whenever they're unlocked, and let it fall out again when we walk the list, but that doesn't fix the accounting problem).

Quoted 2 lines

We simply do not unmap memlocked pages (see try_to_unmap). And therefore they are not reclaimable.

The point is that they're still going to be included in your counts.

Quoted 14 lines · Andrew Morton
Quoted 4 lines
Quoted 4 lines
Quoted 1 line · Christoph Lameter
Quoted 1 line

What other types of non freeable pages could exist?

PageWriteback() pages (potentially all of memory)

Doesnt write throttling take care of that?

Quoted 1 line
Quoted 1 line

Pinned pages (various transient conditions, mainly get_user_pages())

Hmm....

Quoted 1 line
Quoted 1 line

Some pages whose buffers are attached to an ext3 journal.

Quoted 7 lines

These are just pinned by an increased refcount right?

Quoted 1 line
Quoted 1 line

Possibly NFS unstable pages.

These are tracked by NR_NFS_UNSTABLE.

Maybe we need a NR_UNSTABLE that includes pinned pages?

The point of what we decided on Sunday was that we want to count the pages that we KNOW are easy to free. So all of these should be taken out of the count before we take it.

M.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#7 lore raw
Quoted 5 lines · Martin J. Bligh
Quoted 1 line

Maybe we need a NR_UNSTABLE that includes pinned pages?

The point of what we decided on Sunday was that we want to count the pages that we KNOW are easy to free. So all of these should be taken out of the count before we take it.

Unmapped clean pages are easily freeable and do not have these issues. Could we just use that for now? Otherwise we have to add counters to the categories that we do not track for now and take them out of the count.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#8 lore raw
Quoted 11 lines · Christoph Lameter
Quoted 5 lines · Martin J. Bligh
Quoted 1 line

Maybe we need a NR_UNSTABLE that includes pinned pages?

The point of what we decided on Sunday was that we want to count the pages that we KNOW are easy to free. So all of these should be taken out of the count before we take it.

Unmapped clean pages are easily freeable and do not have these issues.
Could we just use that for now? Otherwise we have to add counters to the categories that we do not track for now and take them out of the count.

Yup, I think that covers everything.

M.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#9 lore raw
In reply to Martin J. Bligh
Quoted 7 lines · Martin J. Bligh
Quoted 4 lines

Adding logic to determine the number of clean pages is not necessary. The number of clean pages in the pagecache can be determined by:

global_page_state(NR_FILE_PAGES) - global_page_state(NR_FILE_DIRTY)

It's not that simple. We also need to deal with other types of non-freeable pages, such as memlocked.

mlocked is an exceptional case. The problem is that the information if a page is mlocked is only available via the vma. One has to scan the reverse list and check all the vmas for the flag.

Is mlock that important?

What other types of non freeable pages could exist?

Maybe slab allocations and direct kernel allocations? We have only limited means to reclaim those pages.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#10 lore raw
Quoted 1 line · On Tue, 18 Jul 2006 07:03:12 -0700 (PDT) Christoph Lameter <[email protected]>…

What other types of non freeable pages could exist?

PageWriteback() pages (potentially all of memory)

Pinned pages (various transient conditions, mainly get_user_pages())

Some pages whose buffers are attached to an ext3 journal.

Possibly NFS unstable pages.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#11 lore raw
Quoted 4 lines · Andrew Morton
Quoted 1 line · Christoph Lameter

What other types of non freeable pages could exist?

PageWriteback() pages (potentially all of memory)

Doesnt write throttling take care of that?

Quoted 1 line

Pinned pages (various transient conditions, mainly get_user_pages())

Hmm....

Quoted 1 line

Some pages whose buffers are attached to an ext3 journal.

These are just pinned by an increased refcount right?

Quoted 1 line

Possibly NFS unstable pages.

These are tracked by NR_NFS_UNSTABLE.

Maybe we need a NR_UNSTABLE that includes pinned pages?

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#12 lore raw
Quoted 15 lines · On Tue, 18 Jul 2006 07:45:21 -0700 (PDT) Christoph Lameter <[email protected]>…
Quoted 4 lines · Andrew Morton
Quoted 1 line · Christoph Lameter

What other types of non freeable pages could exist?

PageWriteback() pages (potentially all of memory)

Doesnt write throttling take care of that?

Quoted 1 line

Pinned pages (various transient conditions, mainly get_user_pages())

Hmm....

Quoted 1 line

Some pages whose buffers are attached to an ext3 journal.

Quoted 8 lines

These are just pinned by an increased refcount right?

Quoted 1 line

Possibly NFS unstable pages.

These are tracked by NR_NFS_UNSTABLE.

Maybe we need a NR_UNSTABLE that includes pinned pages?

I'm not sure what was discussed in VM summit. If I miss the point, sorry.

I think the important thing here is the amount of free pages we try to keep. Remaining unused pages as page cache(and not free them) may help performance if lucky but increases uncertainity.

Now, enlarging min_free_kbytes or mempool is useful to avoid page allocation failure in device drivers, which does write-back of dirty pages.
I think controlling free memory itself is better than controlling *used* inactive clean pages.

Bye
-Kame

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#13 lore raw
In reply to Peter Zijlstra
Quoted 3 lines · Peter Zijlstra

This patch implements the inactive_clean list spoken of during the VM summit. The LRU tail pages will be unmapped and ready to free, but not freeed. This gives reclaim an extra chance.

This patch makes it possible to implement Martin Schwidefsky's hypervisor-based fast page reclaiming for architectures without millicode - ie. Xen, UML and all other non-s390 architectures.

That could be a big help in heavily loaded virtualized environments.

The fact that it helps prevent the iSCSI memory deadlock is a huge bonus too, of course :)

Signature · 1 line
The answer is 42.  What is *your* question?
#14 lore raw
Quoted 8 lines · Rik van Riel

This patch makes it possible to implement Martin Schwidefsky's hypervisor-based fast page reclaiming for architectures without millicode - ie. Xen, UML and all other non-s390 architectures.

That could be a big help in heavily loaded virtualized environments.

The fact that it helps prevent the iSCSI memory deadlock is a huge bonus too, of course :)

I think there may be a way with less changes to the way the VM functions to get there:

Add NR_MAPPED_DIRTY and functions to determine easily reclaimable pages.

This patch adds a new counter NR_MAPPED_DIRTY. It tracks the number of dirty mapped pages. With such a counter we can determine the number of unmapped clean file backed pages and possibly use that knowledge to enforce limits in the VM.

Add some new functions:

global_dirty() = Amount of dirty pages in the system as a whole
zone_dirty(zone) = Amount of dirty pages in a specific zone

global_easily_reclaimable() = Amount of clean unmapped pages
(for now. We may want to add other categories later if we can track more page categories).

zone_easily_reclaimable(zone) = Amount of clean unmapped pages in a zone.

I plan to add later:

global_reclaimable()/zone_reclaimable(zone)

This would be the number of pages that are reclaimable at all regardless of the effort to be expended:

So this would be

NR_MAPPED + NR_ANON - NR_UNSTABLE_NFS - <mlocked pages> - <pinned pages>

Patch under development. This is just the basis for a discussion.

Other patches could add /proc limits after this one to guarantee that a certain number of pages is easily reclaimable and so on.

drivers/base/node.c +4 -2
Index: linux-2.6.18-rc1/drivers/base/node.c
===================================================================
@@ -58,10 +58,11 @@ static ssize_t node_read_meminfo(struct 
 		       "Node %d HighFree:     %8lu kB\n"
 		       "Node %d LowTotal:     %8lu kB\n"
 		       "Node %d LowFree:      %8lu kB\n"
-		       "Node %d Dirty:        %8lu kB\n"
 		       "Node %d Writeback:    %8lu kB\n"
 		       "Node %d FilePages:    %8lu kB\n"
+		       "Node %d Dirty:        %8lu kB\n"
 		       "Node %d Mapped:       %8lu kB\n"
+		       "Node %d DirtyMapped:  %8lu kB\n"
 		       "Node %d AnonPages:    %8lu kB\n"
 		       "Node %d PageTables:   %8lu kB\n"
 		       "Node %d NFS Unstable: %8lu kB\n"
@@ -76,10 +77,11 @@ static ssize_t node_read_meminfo(struct 
 		       nid, K(i.freehigh),
 		       nid, K(i.totalram - i.totalhigh),
 		       nid, K(i.freeram - i.freehigh),
-		       nid, K(node_page_state(nid, NR_FILE_DIRTY)),
 		       nid, K(node_page_state(nid, NR_WRITEBACK)),
 		       nid, K(node_page_state(nid, NR_FILE_PAGES)),
+		       nid, K(node_page_state(nid, NR_FILE_DIRTY)),
 		       nid, K(node_page_state(nid, NR_FILE_MAPPED)),
+		       nid, K(node_page_state(nid, NR_FILE_MAPPED_DIRTY)),
 		       nid, K(node_page_state(nid, NR_ANON_PAGES)),
 		       nid, K(node_page_state(nid, NR_PAGETABLE)),
 		       nid, K(node_page_state(nid, NR_UNSTABLE_NFS)),
mm/vmstat.c +1 -0
Index: linux-2.6.18-rc1/mm/vmstat.c
===================================================================
@@ -389,6 +389,7 @@ static char *vmstat_text[] = {
 	"nr_slab",
 	"nr_page_table_pages",
 	"nr_dirty",
+	"nr_mapped_dirty",
 	"nr_writeback",
 	"nr_unstable",
 	"nr_bounce",
fs/proc/proc_misc.c +2 -0
Index: linux-2.6.18-rc1/fs/proc/proc_misc.c
===================================================================
@@ -167,6 +167,7 @@ static int meminfo_read_proc(char *page,
 		"Writeback:    %8lu kB\n"
 		"AnonPages:    %8lu kB\n"
 		"Mapped:       %8lu kB\n"
+		"DirtyMapped:  %8lu kB\n"
 		"Slab:         %8lu kB\n"
 		"PageTables:   %8lu kB\n"
 		"NFS Unstable: %8lu kB\n"
@@ -193,6 +194,7 @@ static int meminfo_read_proc(char *page,
 		K(global_page_state(NR_WRITEBACK)),
 		K(global_page_state(NR_ANON_PAGES)),
 		K(global_page_state(NR_FILE_MAPPED)),
+		K(global_page_state(NR_FILE_MAPPED_DIRTY)),
 		K(global_page_state(NR_SLAB)),
 		K(global_page_state(NR_PAGETABLE)),
 		K(global_page_state(NR_UNSTABLE_NFS)),
include/linux/vmstat.h +57 -0

61 lines not on this page · raw message

mm/page-writeback.c +8 -15
Index: linux-2.6.18-rc1/mm/page-writeback.c
===================================================================
@@ -191,8 +191,7 @@ static void balance_dirty_pages(struct a
 		};
 
 		get_dirty_limits(&background_thresh, &dirty_thresh, mapping);
-		nr_reclaimable = global_page_state(NR_FILE_DIRTY) +
-					global_page_state(NR_UNSTABLE_NFS);
+		nr_reclaimable = global_dirty();
 		if (nr_reclaimable + global_page_state(NR_WRITEBACK) <=
 			dirty_thresh)
 				break;
@@ -210,8 +209,7 @@ static void balance_dirty_pages(struct a
 			writeback_inodes(&wbc);
 			get_dirty_limits(&background_thresh,
 					 	&dirty_thresh, mapping);
-			nr_reclaimable = global_page_state(NR_FILE_DIRTY) +
-					global_page_state(NR_UNSTABLE_NFS);
+			nr_reclaimable = global_dirty();
 			if (nr_reclaimable +
 				global_page_state(NR_WRITEBACK)
 					<= dirty_thresh)
@@ -328,9 +326,7 @@ static void background_writeout(unsigned
 		long dirty_thresh;
 
 		get_dirty_limits(&background_thresh, &dirty_thresh, NULL);
-		if (global_page_state(NR_FILE_DIRTY) +
-			global_page_state(NR_UNSTABLE_NFS) < background_thresh
-				&& min_pages <= 0)
+		if (global_dirty() < background_thresh && min_pages <= 0)
 			break;
 		wbc.encountered_congestion = 0;
 		wbc.nr_to_write = MAX_WRITEBACK_PAGES;
@@ -354,8 +350,7 @@ static void background_writeout(unsigned
 int wakeup_pdflush(long nr_pages)
 {
 	if (nr_pages == 0)
-		nr_pages = global_page_state(NR_FILE_DIRTY) +
-				global_page_state(NR_UNSTABLE_NFS);
+		nr_pages = global_dirty();
 	return pdflush_operation(background_writeout, nr_pages);
 }
 
@@ -401,8 +396,7 @@ static void wb_kupdate(unsigned long arg
 	oldest_jif = jiffies - dirty_expire_interval;
 	start_jif = jiffies;
 	next_jif = start_jif + dirty_writeback_interval;
-	nr_to_write = global_page_state(NR_FILE_DIRTY) +
-			global_page_state(NR_UNSTABLE_NFS) +
+	nr_to_write = global_dirty() +
 			(inodes_stat.nr_inodes - inodes_stat.nr_unused);
 	while (nr_to_write > 0) {
 		wbc.encountered_congestion = 0;
@@ -624,8 +618,7 @@ int __set_page_dirty_nobuffers(struct pa
 			if (mapping2) { /* Race with truncate? */
 				BUG_ON(mapping2 != mapping);
 				if (mapping_cap_account_dirty(mapping))
-					__inc_zone_page_state(page,
-								NR_FILE_DIRTY);
+					__inc_zone_page_dirty(page);
 				radix_tree_tag_set(&mapping->page_tree,
 					page_index(page), PAGECACHE_TAG_DIRTY);
 			}
@@ -713,7 +706,7 @@ int test_clear_page_dirty(struct page *p
 						page_index(page),
 						PAGECACHE_TAG_DIRTY);
 			if (mapping_cap_account_dirty(mapping))
-				__dec_zone_page_state(page, NR_FILE_DIRTY);
+				__dec_zone_page_dirty(page);
 			write_unlock_irqrestore(&mapping->tree_lock, flags);
 			return 1;
 		}
@@ -745,7 +738,7 @@ int clear_page_dirty_for_io(struct page 
 	if (mapping) {
 		if (TestClearPageDirty(page)) {
 			if (mapping_cap_account_dirty(mapping))
-				dec_zone_page_state(page, NR_FILE_DIRTY);
+				dec_zone_page_dirty(page);
 			return 1;
 		}
 		return 0;
include/linux/mmzone.h +1 -0
Index: linux-2.6.18-rc1/include/linux/mmzone.h
===================================================================
@@ -54,6 +54,7 @@ enum zone_stat_item {
 	NR_SLAB,	/* Pages used by slab allocator */
 	NR_PAGETABLE,	/* used for pagetables */
 	NR_FILE_DIRTY,
+	NR_FILE_MAPPED_DIRTY,
 	NR_WRITEBACK,
 	NR_UNSTABLE_NFS,	/* NFS unstable pages */
 	NR_BOUNCE,
mm/vmscan.c +1 -2
Index: linux-2.6.18-rc1/mm/vmscan.c
===================================================================
@@ -1600,8 +1600,7 @@ int zone_reclaim(struct zone *zone, gfp_
 	 * if less than a specified percentage of the zone is used by
 	 * unmapped file backed pages.
 	 */
-	if (zone_page_state(zone, NR_FILE_PAGES) -
-	    zone_page_state(zone, NR_FILE_MAPPED) <= zone->min_unmapped_ratio)
+	if (zone_easily_reclaimable(zone) <= zone->min_unmapped_ratio)
 		return 0;
 
 	/*
fs/fs-writeback.c +2 -5
Index: linux-2.6.18-rc1/fs/fs-writeback.c
===================================================================
@@ -464,12 +464,9 @@ void sync_inodes_sb(struct super_block *
 		.range_start	= 0,
 		.range_end	= LLONG_MAX,
 	};
-	unsigned long nr_dirty = global_page_state(NR_FILE_DIRTY);
-	unsigned long nr_unstable = global_page_state(NR_UNSTABLE_NFS);
 
-	wbc.nr_to_write = nr_dirty + nr_unstable +
-			(inodes_stat.nr_inodes - inodes_stat.nr_unused) +
-			nr_dirty + nr_unstable;
+	wbc.nr_to_write = 2 * global_dirty() +
+			(inodes_stat.nr_inodes - inodes_stat.nr_unused);
 	wbc.nr_to_write += wbc.nr_to_write / 2;		/* Bit more for luck */
 	spin_lock(&inode_lock);
 	sync_sb_inodes(sb, &wbc);
fs/buffer.c +1 -1
Index: linux-2.6.18-rc1/fs/buffer.c
===================================================================
@@ -851,7 +851,7 @@ int __set_page_dirty_buffers(struct page
 		write_lock_irq(&mapping->tree_lock);
 		if (page->mapping) {	/* Race with truncate? */
 			if (mapping_cap_account_dirty(mapping))
-				__inc_zone_page_state(page, NR_FILE_DIRTY);
+				__inc_zone_page_dirty(page);
 			radix_tree_tag_set(&mapping->page_tree,
 						page_index(page),
 						PAGECACHE_TAG_DIRTY);
fs/nfs/write.c +1 -1
Index: linux-2.6.18-rc1/fs/nfs/write.c
===================================================================
@@ -496,7 +496,7 @@ nfs_mark_request_dirty(struct nfs_page *
 	nfs_list_add_request(req, &nfsi->dirty);
 	nfsi->ndirty++;
 	spin_unlock(&nfsi->req_lock);
-	inc_zone_page_state(req->wb_page, NR_FILE_DIRTY);
+	inc_zone_page_dirty(req->wb_page);
 	mark_inode_dirty(inode);
 }
 
fs/nfs/pagelist.c +1 -1
Index: linux-2.6.18-rc1/fs/nfs/pagelist.c
===================================================================
@@ -314,7 +314,7 @@ nfs_scan_lock_dirty(struct nfs_inode *nf
 						req->wb_index, NFS_PAGE_TAG_DIRTY);
 				nfs_list_remove_request(req);
 				nfs_list_add_request(req, dst);
-				dec_zone_page_state(req->wb_page, NR_FILE_DIRTY);
+				dec_zone_page_dirty(req->wb_page);
 				res++;
 			}
 		}
mm/rmap.c +9 -3
Index: linux-2.6.18-rc1/mm/rmap.c
===================================================================
@@ -498,8 +498,11 @@ void page_add_new_anon_rmap(struct page 
  */
 void page_add_file_rmap(struct page *page)
 {
-	if (atomic_inc_and_test(&page->_mapcount))
+	if (atomic_inc_and_test(&page->_mapcount)) {
 		__inc_zone_page_state(page, NR_FILE_MAPPED);
+		if (PageDirty(page))
+			__inc_zone_page_state(page, NR_FILE_MAPPED_DIRTY);
+	}
 }
 
 /**
@@ -520,6 +523,7 @@ void page_remove_rmap(struct page *page)
 		}
 #endif
 		BUG_ON(page_mapcount(page) < 0);
+
 		/*
 		 * It would be tidy to reset the PageAnon mapping here,
 		 * but that might overwrite a racing page_add_anon_rmap
@@ -529,10 +533,12 @@ void page_remove_rmap(struct page *page)
 		 * Leaving it set also helps swapoff to reinstate ptes
 		 * faster for those pages still in swapcache.
 		 */
-		if (page_test_and_clear_dirty(page))
+		if (page_test_and_clear_dirty(page)) {
+			__dec_zone_page_state(page, NR_FILE_MAPPED_DIRTY);
 			set_page_dirty(page);
+		}
 		__dec_zone_page_state(page,
-				PageAnon(page) ? NR_ANON_PAGES : NR_FILE_MAPPED);
+			PageAnon(page) ? NR_ANON_PAGES : NR_FILE_MAPPED);
 	}
 }
 

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#15 lore raw
Quoted 6 lines · Christoph Lameter
Quoted 4 lines · Rik van Riel

This patch makes it possible to implement Martin Schwidefsky's hypervisor-based fast page reclaiming for architectures without millicode - ie. Xen, UML and all other non-s390 architectures.

Quoted 6 lines
Quoted 4 lines

That could be a big help in heavily loaded virtualized environments.

The fact that it helps prevent the iSCSI memory deadlock is a huge bonus too, of course :)

I think there may be a way with less changes to the way the VM functions to get there:

That approach probably has way too many state changes going between the guest OS and the hypervisor...

Signature · 9 lines
"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it." - Brian W. Kernighan

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#16 lore raw
Quoted 5 lines · Rik van Riel
Quoted 2 lines

I think there may be a way with less changes to the way the VM functions to get there:

That approach probably has way too many state changes going between the guest OS and the hypervisor...

An increment of a VM counter causes a state change in the hypervisor?

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#17 lore raw
Quoted 8 lines · Christoph Lameter
Quoted 4 lines · Rik van Riel
Quoted 2 lines

I think there may be a way with less changes to the way the VM functions to get there:

That approach probably has way too many state changes going between the guest OS and the hypervisor...

An increment of a VM counter causes a state change in the hypervisor?

Christoph, please read more than the first 5 words in each email before replying.

Signature · 9 lines
"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it." - Brian W. Kernighan

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#18 lore raw
Quoted 4 lines · Rik van Riel
Quoted 1 line

An increment of a VM counter causes a state change in the hypervisor?

Christoph, please read more than the first 5 words in each email before replying.

Well, I read the whole thing before I replied and I could not figure this one out. Maybe I am too dumb to understand. Could you please explain yourself in more detail

I am also not sure why I should be running a hypervisor in the first place and so I may not be up to date on the whole technology.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#19 lore raw
Quoted 7 lines · Christoph Lameter
Quoted 3 lines · Rik van Riel
Quoted 1 line

An increment of a VM counter causes a state change in the hypervisor?

Christoph, please read more than the first 5 words in each email before replying.

Well, I read the whole thing before I replied and I could not figure this one out. Maybe I am too dumb to understand. Could you please explain yourself in more detail

Page state transitions can be very expensive in a virtualized environment, so it would be good if we had fewer transitions.

Quoted 1 line

I am also not sure why I should be running a hypervisor in the first place and so I may not be up to date on the whole technology.

You may not, but IMHO it would be good if whatever new VM things we implement in Linux would at least be virtualization friendly. Especially if that can be achieved without hurting native performance...

Signature · 9 lines
"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it." - Brian W. Kernighan

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#20 lore raw
Quoted 6 lines · Rik van Riel
Quoted 3 lines

Well, I read the whole thing before I replied and I could not figure this one out. Maybe I am too dumb to understand. Could you please explain yourself in more detail

Page state transitions can be very expensive in a virtualized environment, so it would be good if we had fewer transitions.

So the hypervisor indeed tracks each individual page state? Note that I do not propose to change the page state but a counter for page states. I am bit confused about how not touching a page can cause page state transitions. But then I do not know much about hypervisors. What magic is going on in the background that could enable the hypervisor to track counter increments?

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#21 lore raw
In reply to Rik van Riel
Quoted 8 lines · Rik van Riel
Quoted 3 lines · Peter Zijlstra

This patch implements the inactive_clean list spoken of during the VM summit. The LRU tail pages will be unmapped and ready to free, but not freeed. This gives reclaim an extra chance.

This patch makes it possible to implement Martin Schwidefsky's hypervisor-based fast page reclaiming for architectures without millicode - ie. Xen, UML and all other non-s390 architectures.

Hmm, I wonder how the inactive clean list helps in regard to the fast host reclaim
scheme. In particular since the memory pressure that triggers the reclaim is in the
host, not in the guest. So all pages might be on the active list but the host still
wants to be able to discard pages.

Signature · 8 lines
blue skies,
 Martin

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#22 lore raw
Quoted 10 lines · Martin Schwidefsky
Quoted 8 lines · Rik van Riel
Quoted 3 lines · Peter Zijlstra

This patch implements the inactive_clean list spoken of during the VM summit. The LRU tail pages will be unmapped and ready to free, but not freeed. This gives reclaim an extra chance.

This patch makes it possible to implement Martin Schwidefsky's hypervisor-based fast page reclaiming for architectures without millicode - ie. Xen, UML and all other non-s390 architectures.

Quoted 8 lines

Hmm, I wonder how the inactive clean list helps in regard to the fast host reclaim
scheme. In particular since the memory pressure that triggers the reclaim is in the
host, not in the guest. So all pages might be on the active list but the host still
wants to be able to discard pages.

I think Rik would want to set all the already unmapped pages to volatile state in the hypervisor.

These pages can be dropped without loss of information on the guest system since they are all already on a backing-store, be it regular files or swap.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#23 lore raw
Quoted 9 lines · Peter Zijlstra
Quoted 8 lines

Hmm, I wonder how the inactive clean list helps in regard to the fast host reclaim
scheme. In particular since the memory pressure that triggers the reclaim is in the
host, not in the guest. So all pages might be on the active list but the host still
wants to be able to discard pages.

Quoted 6 lines

I think Rik would want to set all the already unmapped pages to volatile state in the hypervisor.

These pages can be dropped without loss of information on the guest system since they are all already on a backing-store, be it regular files or swap.

I guessed that as well. It isn't good enough. Consider a guest with a large (virtual) memory size and a host with a small physical memory size. The guest will never put any page on the inactive_clean list because it does not have memory pressure. vmscan will never run. The host wants to reclaim memory of the guest, but since the inactive_clean list is empty it will find only stable pages.

Signature · 8 lines
blue skies,
 Martin

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#24 lore raw
Quoted 17 lines · Martin Schwidefsky
Quoted 9 lines · Peter Zijlstra
Quoted 8 lines

Hmm, I wonder how the inactive clean list helps in regard to the fast host reclaim
scheme. In particular since the memory pressure that triggers the reclaim is in the
host, not in the guest. So all pages might be on the active list but the host still
wants to be able to discard pages.

Quoted 6 lines

I think Rik would want to set all the already unmapped pages to volatile state in the hypervisor.

These pages can be dropped without loss of information on the guest system since they are all already on a backing-store, be it regular files or swap.

Quoted 7 lines

I guessed that as well. It isn't good enough. Consider a guest with a large (virtual) memory size and a host with a small physical memory size. The guest will never put any page on the inactive_clean list because it does not have memory pressure. vmscan will never run. The host wants to reclaim memory of the guest, but since the inactive_clean list is empty it will find only stable pages.

Wouldn't we typically have all free pages > min_free in state U? Also wouldn't all R/O mapped pages not also be V, all R/W mapped pages and unmapped page-cache pages P like you state in your paper.

This patch would just increase the number of V pages with the tail end of the guest LRU, which are typically the pages you would want to evict (perhaps even add 5th guest state to indicate that these V pages are preferable over the others?)

But isn't it so that for the gross over-commit scenario you outline the host OS will have to swap out S pages eventually?

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#25 lore raw
Quoted 3 lines · Peter Zijlstra

Wouldn't we typically have all free pages > min_free in state U? Also wouldn't all R/O mapped pages not also be V, all R/W mapped pages and unmapped page-cache pages P like you state in your paper.

Ahh, ok, I misunderstood. You want to keep the state changes for clean page cache pages, I assumed that you only want to make pages volatile if the get on the inactive_clean list and leave them stable if they are on one of the other two lists.

Quoted 4 lines

This patch would just increase the number of V pages with the tail end of the guest LRU, which are typically the pages you would want to evict (perhaps even add 5th guest state to indicate that these V pages are preferable over the others?)

Yes, that would help for architectures that cannot implement the potential-volatile state.

Quoted 2 lines

But isn't it so that for the gross over-commit scenario you outline the host OS will have to swap out S pages eventually?

My point was that you really have to distinguish between host memory pressure and guest memory pressure.

Signature · 8 lines
blue skies,
 Martin

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#26 lore raw
In reply to Martin Schwidefsky
Quoted 9 lines · Martin Schwidefsky
Quoted 2 lines · Peter Zijlstra

I think Rik would want to set all the already unmapped pages to volatile state in the hypervisor.

I guessed that as well. It isn't good enough. Consider a guest with a large (virtual) memory size and a host with a small physical memory size. The guest will never put any page on the inactive_clean list because it does not have memory pressure.

Well, the management software running on top of everything should tweak the inactive_clean targets in the various guests so the total amount of volatile memory is large enough...

Signature · 9 lines
"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it." - Brian W. Kernighan

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>