VM pageout scalability improvements (V8)
Subsystems (17)
cover VM pageout scalability improvements (V8)
On large memory systems, the VM can spend way too much time scanning through pages that it cannot (or should not) evict from memory. Not only does it use up CPU time, but it also provokes lock contention and can leave large systems under memory presure in a catatonic state.
Against 2.6.26-rc2-mm1
This patch series improves VM scalability by:
1) putting filesystem backed, swap backed and non-reclaimable pages onto their own LRUs, so the system only scans the pages that it can/should evict from memory
2) switching to SEQ replacement for the anonymous LRUs, so the number of pages that need to be scanned when the system starts swapping is bound to a reasonable number
3) keeping non-reclaimable pages off the LRU completely, so the VM does not waste CPU time scanning them. Currently only ramfs and SHM_LOCKED pages are kept on the noreclaim list, mlock()ed VMAs will be added later
More info on the overall design can be found at:
http://linux-mm.org/PageReplacementDesign
An all-in-one patch can be found at:
http://people.redhat.com/riel/splitvm/
Changelog:
- make sure previously active pagecache pages get reactivated on the first access (Rik van Riel)
- compile fix when !CONFIG_SWAP (MinChan Kim)
- clean up page-flags.h defines when !CONFIG_NORECLAIM_LRU (Lee Schermerhorn)
- fix some race conditions around moving pages to and from the noreclaim list (Lee Schermerhorn, KOSAKI Motohiro)
- use putback_lru_page() for page migration (Lee Schermerhorn)
- fix potential SHM_UNLOCK race in scan_mapping_noreclaim_pages() (Lee Schermerhorn, KOSAKI Motohiro)
- improve swap space freeing to deal with COW shared space
(Lee Schermerhorn, Daisuke Nishimura & Minchan Kim)
- clean up PG_swapbacked setting in swapin path (Minchan Kim)
- properly invoke shrink_active_list for background aging (Minchan Kim)
- add authorship info to all patches (Rik van Riel)
- clean up (or move below ---) the comments for the commit logs (Rik van Riel)
- after some tests, reduce default swappiness to 20 for now (Rik van Riel)
- several code cleanups (minchan Kim)
- noreclaim patch refactoring and improvements (Lee Schermerhorn)
- several PROT_NONE and vma merging fixes (KOSAKI Motohiro)
- SMP bugfixes and efficiency improvements (Rik van Riel, Lee Schermerhorn)
- fix NUMA node stats printing (Lee Schermerhorn)
- remove the mlocked-VMA-noreclaim code for now, it still has bugs on IA64 and is holding up the merge (Rik van Riel)
- make page_alloc.c compile without CONFIG_NORECLAIM_MLOCK (minchan Kim)
- BUG() does not take an argument (minchan Kim)
- clean up is_active_lru and is_file_lru (Andy Whitcroft)
- clean up shrink_active_list temp list names (KOSAKI Motohiro)
- add total active & inactive memory totals for vmstat -a (KOSAKI Motohiro)
- only try global anon page aging on global lru scans (KOSAKI Motohiro)
- make function descriptions follow the kernel-doc format (Rik van Riel)
- simplify mlock_vma_pages_range and munlock_vma_pages_range (Lee Schermerhorn)
- remove some more arguments, rename to mlock_vma_pages_all (Lee Schermerhorn)
- many code cleanups (Lee Schermerhorn)
- pass correct vma arg to mlock_vma_pages_range from do_brk (Rik van Riel)
- port to 2.6.25-rc3-mm1
- pull the memcontrol lru arrayification earlier into the patch series
- use a pagevec array similar to the lru array
- clean up the code in various places
- improved pageout balancing and reduced pageout cpu use
- fix compilation on PPC and without memcontrol
- make page_is_pagecache more readable
- replace get_scan_ratio with correct version
- merge memcontroller split LRU code into the main split LRU patch, since it is not functionally different (it was split up only to help people who had seen the last version of the patch series review it)
- drop the page_file_cache debugging patch, since it never triggered
- reintroduce code to not scan anon list if swap is full
- add code to scan anon list if page cache is very small already
- use lumpy reclaim more aggressively for smaller order > 1 allocations
Signature · 1 line
All Rights Reversed
PATCH 1/16 move isolate_lru_page() to vmscan.c
From: Nick Piggin <[email protected]>
isolate_lru_page logically belongs to be in vmscan.c than migrate.c.
It is tough, because we don't need that function without memory migration so there is a valid argument to have it in migrate.c. However a subsequent patch needs to make use of it in the core mm, so we can happily move it to vmscan.c.
Also, make the function a little more generic by not requiring that it adds an isolated page to a given list. Callers can do that.
Note that we now have '__isolate_lru_page()', that does something quite different, visible outside of vmscan.c for use with memory controller. Methinks we need to
rationalize these names/purposes. --lts
---
V1 -> V2 [lts]:
+ fix botched merge -- add back "get_page_unless_zero()"
From: Nick Piggin <[email protected]>
To: Linux Memory Management <[email protected]>
Subject: [patch 1/4] mm: move and rework isolate_lru_page
Date: Mon, 12 Mar 2007 07:38:44 +0100 (CET)
include/linux/migrate.h | 3 --- mm/internal.h | 2 ++ mm/mempolicy.c | 9 +++++++-- mm/migrate.c | 34 +++------------------------------- mm/vmscan.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 57 insertions(+), 36 deletions(-)
include/linux/migrate.h +0 -3
@@ -25,7 +25,6 @@ static inline int vma_migratable(structreturn 1 ; } -extern int isolate_lru_page (struct page *p,struct list_head *pagelist);extern int putback_lru_pages (struct list_head *l);extern int migrate_page (struct address_space *,struct page *,struct page *);
@@ -42,8 +41,6 @@ extern int migrate_vmas(struct mm_structstatic inline int vma_migratable (struct vm_area_struct *vma) {return 0 ; } -static inline int isolate_lru_page (struct page *p,struct list_head *list) - {return -ENOSYS ; }static inline int putback_lru_pages (struct list_head *l) {return 0 ; }static inline int migrate_pages (struct list_head *l,new_page_t x,unsigned long private) {return -ENOSYS ; }
mm/internal.h +2 -0
@@ -34,6 +34,8 @@ static inline void __put_page(struct pagatomic_dec (&page->_count); } +extern int isolate_lru_page (struct page *page); +extern void __free_pages_bootmem (struct page *page,unsigned int order);/*
mm/migrate.c +3 -31
@@ -37,36 +37,6 @@#define lru_to_page (_head) (list_entry((_head)->prev, struct page, lru))/* -* Isolate one page from the LRU lists. If successful put it onto -* the indicated list with elevated page count. -* -* Result: -* -EBUSY: page not on LRU list -* 0: page removed from LRU list and added to the specified list. -*/ -int isolate_lru_page (struct page *page,struct list_head *pagelist) -{ -int ret = -EBUSY ; - -if (PageLRU (page)) { -struct zone *zone =page_zone (page); - -spin_lock_irq (&zone->lru_lock); -if (PageLRU (page) &&get_page_unless_zero (page)) { - ret =0 ; -ClearPageLRU (page); -if (PageActive (page)) -del_page_from_active_list (zone, page); -else -del_page_from_inactive_list (zone, page); -list_add_tail (&page->lru, pagelist); - } -spin_unlock_irq (&zone->lru_lock); - } -return ret; -} - -/* * migrate_prep() needs to be called before we start compiling a list of pages * to be migrated using isolate_lru_page(). */
@@ -895,7 +865,9 @@ static int do_move_pages(struct mm_struc !migrate_all) goto put_and_set; - err =isolate_lru_page (page, &pagelist); + err =isolate_lru_page (page); +if (!err) +list_add_tail (&page->lru, &pagelist); put_and_set:/* * Either remove the duplicate refcount from
mm/vmscan.c +45 -0
51 lines not on this page · raw message
mm/mempolicy.c +7 -2
@@ -93,6 +93,8 @@#include <asm/tlbflush.h> #include <asm/uaccess.h> +#include "internal.h" +/* Internal flags */ #define MPOL_MF_DISCONTIG_OK (MPOL_MF_INTERNAL << 0)/* Skip checks for continuous vmas */ #define MPOL_MF_INVERT (MPOL_MF_INTERNAL << 1)/* Invert check for nodemask */
@@ -758,8 +760,11 @@ static void migrate_page_add(struct page/* * Avoid migrating a page that is shared with others. */ -if ((flags &MPOL_MF_MOVE_ALL ) ||page_mapcount (page) ==1 ) -isolate_lru_page (page, pagelist); +if ((flags &MPOL_MF_MOVE_ALL ) ||page_mapcount (page) ==1 ) { +if (!isolate_lru_page (page)) { +list_add_tail (&page->lru, pagelist); + } + } }static struct page *new_node_page (struct page *page,unsigned long node,int **x)
Signature · 1 line
All Rights Reversed
PATCH 2/16 Use an indexed array for LRU variables
In reply to Rik van RielFrom: Christoph Lameter <[email protected]>
Currently we are defining explicit variables for the inactive and active list. An indexed array can be more generic and avoid repeating similar code in several places in the reclaim code.
We are saving a few bytes in terms of code size:
Before:
text data bss dec hex filename
4097753 573120 4092484 8763357 85b7dd vmlinux
After:
text data bss dec hex filename
4097729 573120 4092484 8763333 85b7c5 vmlinux
Having an easy way to add new lru lists may ease future work on the reclaim code.
---
V3 [riel]: memcontrol LRU arrayification
V1 -> V2 [lts]:
+ Remove extraneous __dec_zone_state(zone, NR_ACTIVE) pointed out by Mel G.
include/linux/memcontrol.h | 17 +----- include/linux/mm_inline.h | 33 ++++++++---- include/linux/mmzone.h | 17 ++++-- mm/memcontrol.c | 115 ++++++++++++++++--------------------------- mm/page_alloc.c | 9 +-- mm/swap.c | 2 mm/vmscan.c | 120 +++++++++++++++++++++------------------------ mm/vmstat.c | 3 - 8 files changed, 146 insertions(+), 170 deletions(-)
include/linux/mmzone.h +11 -6
@@ -81,8 +81,8 @@ struct zone_padding {enum zone_stat_item {/* First 128 byte cacheline (assuming 64 bit words) */ NR_FREE_PAGES , -NR_INACTIVE , -NR_ACTIVE , +NR_INACTIVE ,/* must match order of LRU_[IN]ACTIVE */ +NR_ACTIVE ,/* " " " " " */ NR_ANON_PAGES ,/* Mapped anonymous pages */ NR_FILE_MAPPED ,/* pagecache pages mapped into pagetables. only modified from process context */
@@ -107,6 +107,13 @@ enum zone_stat_item {#endif NR_VM_ZONE_STAT_ITEMS }; +enum lru_list { +LRU_INACTIVE ,/* must match order of NR_[IN]ACTIVE */ +LRU_ACTIVE ,/* " " " " " */ +NR_LRU_LISTS }; + +#define for_each_lru (l) for (l = 0; l < NR_LRU_LISTS; l++) +struct per_cpu_pages {int count;/* number of pages in the list */ int high;/* high watermark, emptying needed */
@@ -251,10 +258,8 @@ struct zone {/* Fields commonly accessed by the page reclaim scanner */ spinlock_t lru_lock; -struct list_head active_list; -struct list_head inactive_list; -unsigned long nr_scan_active; -unsigned long nr_scan_inactive; +struct list_head list[NR_LRU_LISTS ]; +unsigned long nr_scan[NR_LRU_LISTS ];unsigned long pages_scanned;/* since last reclaim */ unsigned long flags;/* zone flags, see below */
include/linux/mm_inline.h +0 -0
Re: [PATCH -mm 02/16] Use an indexed array for LRU variables
include/linux/mm_inline.h
Quoted 92 lines · Rik van Riel
plain text document attachment
(cl-use-indexed-array-of-lru-lists.patch)
From: Christoph Lameter <[email protected]>Currently we are defining explicit variables for the inactive and active list. An indexed array can be more generic and avoid repeating similar code in several places in the reclaim code.
We are saving a few bytes in terms of code size:
Before:
text data bss dec hex filename
4097753 573120 4092484 8763357 85b7dd vmlinuxAfter:
text data bss dec hex filename
4097729 573120 4092484 8763333 85b7c5 vmlinuxHaving an easy way to add new lru lists may ease future work on the reclaim code.
Signed-off-by Rik van Riel <[email protected]>Signed-off-by Lee Schermerhorn <[email protected]>Signed-off-by Christoph Lameter <[email protected]>---
V3 [riel]: memcontrol LRU arrayificationV1 -> V2 [lts]:
+ Remove extraneous__dec_zone_state(zone, NR_ACTIVE)pointed out by Mel G.include/linux/memcontrol.h | 17 +----- include/linux/mm_inline.h | 33 ++++++++---- include/linux/mmzone.h | 17 ++++-- mm/memcontrol.c | 115 ++++++++++++++++--------------------------- mm/page_alloc.c | 9 +-- mm/swap.c | 2 mm/vmscan.c | 120 +++++++++++++++++++++------------------------ mm/vmstat.c | 3 - 8 files changed, 146 insertions(+), 170 deletions(-)Index:
linux-2.6.26-rc2-mm1/include/linux/mmzone.h
===================================================================include/linux/mmzone.h +11
-6@@ -81,8 +81,8 @@ struct zone_padding {enum zone_stat_item {/* First 128 byte cacheline (assuming 64 bit words) */ NR_FREE_PAGES , -NR_INACTIVE , -NR_ACTIVE , +NR_INACTIVE ,/* must match order of LRU_[IN]ACTIVE */ +NR_ACTIVE ,/* " " " " " */ NR_ANON_PAGES ,/* Mapped anonymous pages */ NR_FILE_MAPPED ,/* pagecache pages mapped into pagetables. only modified from process context */ @@ -107,6 +107,13 @@ enum zone_stat_item {#endif NR_VM_ZONE_STAT_ITEMS }; +enum lru_list { +LRU_INACTIVE ,/* must match order of NR_[IN]ACTIVE */ +LRU_ACTIVE ,/* " " " " " */ +NR_LRU_LISTS }; + +#define for_each_lru (l) for (l = 0; l < NR_LRU_LISTS; l++) +struct per_cpu_pages {int count;/* number of pages in the list */ int high;/* high watermark, emptying needed */ @@ -251,10 +258,8 @@ struct zone {/* Fields commonly accessed by the page reclaim scanner */ spinlock_t lru_lock; -struct list_head active_list; -struct list_head inactive_list; -unsigned long nr_scan_active; -unsigned long nr_scan_inactive; +struct list_head list[NR_LRU_LISTS ]; +unsigned long nr_scan[NR_LRU_LISTS ];unsigned long pages_scanned;/* since last reclaim */ unsigned long flags;/* zone flags, see below */ Index:
linux-2.6.26-rc2-mm1/include/linux/mm_inline.h
===================================================================include/linux/mm_inline.h +0
-0
Rik: looks like this patch got truncated somewhere along the way.
Lee
Re: [PATCH -mm 02/16] Use an indexed array for LRU variables
Quoted 7 lines · On Tue, 27 May 2008 12:54:22 -0400 Lee Schermerhorn <[email protected]>…
include/linux/mm_inline.h
Quoted 5 lines
Index:
linux-2.6.26-rc2-mm1/include/linux/mm_inline.h
===================================================================include/linux/mm_inline.h +0
-0Rik: looks like this patch got truncated somewhere along the way.
Ohhh, so that's why formail initially stopped after mailing out this patch and I had to restart it from the 3rd message! I didn't get any NFS IO errors, so I guess it just got an EOF :(
I've just resent patch 02/16.
Signature · 1 line
All Rights Reversed
PATCH 3/16 use an array for the LRU pagevecs
In reply to Rik van RielFrom: KOSAKI Motohiro <[email protected]>
Turn the pagevecs into an array just like the LRUs. This significantly cleans up the source code and reduces the size of the kernel by about 13kB after all the LRU lists have been created further down in the split VM patch series.
--- include/linux/mmzone.h | 13 +++++- include/linux/pagevec.h | 13 +++++- include/linux/swap.h | 18 ++++++++- mm/migrate.c | 11 ----- mm/swap.c | 96 ++++++++++++++++++++++-------------------------- 5 files changed, 83 insertions(+), 68 deletions(-)
include/linux/mmzone.h +11 -2
@@ -107,13 +107,22 @@ enum zone_stat_item {#endif NR_VM_ZONE_STAT_ITEMS }; +#define LRU_BASE 0 +enum lru_list { -LRU_INACTIVE ,/* must match order of NR_[IN]ACTIVE */ -LRU_ACTIVE ,/* " " " " " */ +LRU_INACTIVE =LRU_BASE ,/* must match order of NR_[IN]ACTIVE */ +LRU_ACTIVE ,/* " " " " " */ NR_LRU_LISTS };#define for_each_lru (l) for (l = 0; l < NR_LRU_LISTS; l++) +static inline int is_active_lru (enum lru_list l) +{ +return (l ==LRU_ACTIVE ); +} + +enum lru_list page_lru (struct page *page); +struct per_cpu_pages {int count;/* number of pages in the list */ int high;/* high watermark, emptying needed */
mm/swap.c +44 -52
149 lines not on this page · raw message
include/linux/pagevec.h +11 -2
@@ -23,8 +23,7 @@ struct pagevec {void __pagevec_release (struct pagevec *pvec);void __pagevec_release_nonlru (struct pagevec *pvec);void __pagevec_free (struct pagevec *pvec); -void __pagevec_lru_add (struct pagevec *pvec); -void __pagevec_lru_add_active (struct pagevec *pvec); +void ____pagevec_lru_add (struct pagevec *pvec,enum lru_list lru);void pagevec_strip (struct pagevec *pvec);unsigned pagevec_lookup (struct pagevec *pvec,struct address_space *mapping,pgoff_t start,unsigned nr_pages);
@@ -81,6 +80,16 @@ static inline void pagevec_free(struct p__pagevec_free (pvec); } +static inline void __pagevec_lru_add (struct pagevec *pvec) +{ +____pagevec_lru_add (pvec,LRU_INACTIVE ); +} + +static inline void __pagevec_lru_add_active (struct pagevec *pvec) +{ +____pagevec_lru_add (pvec,LRU_ACTIVE ); +} +static inline void pagevec_lru_add (struct pagevec *pvec) {if (pagevec_count (pvec))
include/linux/swap.h +16 -2
@@ -171,8 +171,8 @@ extern unsigned int nr_free_pagecache_pa/* linux/mm/swap.c */ -extern void lru_cache_add (struct page *); -extern void lru_cache_add_active (struct page *); +extern void __lru_cache_add (struct page *,enum lru_list lru); +extern void lru_cache_add_lru (struct page *,enum lru_list lru);extern void activate_page (struct page *);extern void mark_page_accessed (struct page *);extern void lru_add_drain (void );
@@ -180,6 +180,20 @@ extern int lru_add_drain_all(void);extern void rotate_reclaimable_page (struct page *page);extern void swap_setup (void ); +/** +* lru_cache_add: add a page to the page lists +* @page: the page to add +*/ +static inline void lru_cache_add (struct page *page) +{ +__lru_cache_add (page,LRU_INACTIVE ); +} + +static inline void lru_cache_add_active (struct page *page) +{ +__lru_cache_add (page,LRU_ACTIVE ); +} +/* linux/mm/vmscan.c */ extern unsigned long try_to_free_pages (struct zonelist *zonelist,int order,gfp_t gfp_mask);
mm/migrate.c +1 -10
@@ -55,16 +55,7 @@ int migrate_prep(void)static inline void move_to_lru (struct page *page) { -if (PageActive (page)) { -/* -* lru_cache_add_active checks that -* the PG_active bit is off. -*/ -ClearPageActive (page); -lru_cache_add_active (page); - }else { -lru_cache_add (page); - } +lru_cache_add_lru (page,page_lru (page));put_page (page); }
Signature · 1 line
All Rights Reversed
PATCH 4/16 free swap space on swap-in/activation
In reply to Rik van RielFrom: Rik van Riel <[email protected]>
Free swap cache entries when swapping in pages if vm_swap_full() [swap space > 1/2 used]. Uses new pagevec to reduce pressure on locks.
--- include/linux/pagevec.h | 1 + include/linux/swap.h | 6 ++++++ mm/swap.c | 18 ++++++++++++++++++ mm/swapfile.c | 25 ++++++++++++++++++++++--- mm/vmscan.c | 7 +++++++ 5 files changed, 54 insertions(+), 3 deletions(-)
mm/vmscan.c +7 -0
@@ -619,6 +619,9 @@ free_it:continue ; activate_locked: +/* Not a candidate for swapping, so reclaim swap space. */ +if (PageSwapCache (page) &&vm_swap_full ()) +remove_exclusive_swap_page_ref (page);SetPageActive (page); pgactivate++; keep_locked:
@@ -1203,6 +1206,8 @@ static void shrink_active_list(unsigned__mod_zone_page_state (zone,NR_ACTIVE , pgmoved); pgmoved =0 ;spin_unlock_irq (&zone->lru_lock); +if (vm_swap_full ()) +pagevec_swap_free (&pvec);__pagevec_release (&pvec);spin_lock_irq (&zone->lru_lock); }
@@ -1212,6 +1217,8 @@ static void shrink_active_list(unsigned__count_zone_vm_events (PGREFILL , zone, pgscanned);__count_vm_events (PGDEACTIVATE , pgdeactivate);spin_unlock_irq (&zone->lru_lock); +if (vm_swap_full ()) +pagevec_swap_free (&pvec);pagevec_release (&pvec); }
mm/swap.c +18 -0
@@ -443,6 +443,24 @@ void pagevec_strip(struct pagevec *pvec) } } +/* +* Try to free swap space from the pages in a pagevec +*/ +void pagevec_swap_free (struct pagevec *pvec) +{ +int i; + +for (i =0 ; i <pagevec_count (pvec); i++) { +struct page *page = pvec->pages[i]; + +if (PageSwapCache (page) && !TestSetPageLocked (page)) { +if (PageSwapCache (page)) +remove_exclusive_swap_page_ref (page); +unlock_page (page); + } + } +} +/** * pagevec_lookup - gang pagecache lookup * @pvec: Where the resulting pages are placed
include/linux/pagevec.h +1 -0
@@ -25,6 +25,7 @@ void __pagevec_release_nonlru(struct pagvoid __pagevec_free (struct pagevec *pvec);void ____pagevec_lru_add (struct pagevec *pvec,enum lru_list lru);void pagevec_strip (struct pagevec *pvec); +void pagevec_swap_free (struct pagevec *pvec);unsigned pagevec_lookup (struct pagevec *pvec,struct address_space *mapping,pgoff_t start,unsigned nr_pages); unsigned pagevec_lookup_tag(struct pagevec *pvec,
include/linux/swap.h +6 -0
@@ -265,6 +265,7 @@ extern sector_t swapdev_block(int, pgoffextern struct swap_info_struct *get_swap_info_struct (unsigned );extern int can_share_swap_page (struct page *);extern int remove_exclusive_swap_page (struct page *); +extern int remove_exclusive_swap_page_ref (struct page *);struct backing_dev_info ;/* linux/mm/thrash.c */
@@ -353,6 +354,11 @@ static inline int remove_exclusive_swap_return 0 ; } +static inline int remove_exclusive_swap_page_ref (struct page *page) +{ +return 0 ; +} +static inline swp_entry_t get_swap_page (void ) {swp_entry_t entry;
mm/swapfile.c +22 -3
@@ -343,7 +343,7 @@ int can_share_swap_page(struct page *pag* Work out if there are any other processes sharing this * swap cache page. Free it if you can. Return success. */ -int remove_exclusive_swap_page (struct page *page) +static int remove_exclusive_swap_page_count (struct page *page,int count) {int retval;struct swap_info_struct * p;
@@ -356,7 +356,7 @@ int remove_exclusive_swap_page(struct pareturn 0 ;if (PageWriteback (page))return 0 ; -if (page_count (page) !=2 )/* 2: us + cache */ +if (page_count (page) != count)/* us + cache + ptes */ return 0 ; entry.val =page_private (page);
@@ -369,7 +369,7 @@ int remove_exclusive_swap_page(struct paif (p->swap_map[swp_offset (entry)] ==1 ) {/* Recheck the page count with the swapcache lock held.. */ write_lock_irq (&swapper_space.tree_lock); -if ((page_count (page) ==2 ) && !PageWriteback (page)) { +if ((page_count (page) == count) && !PageWriteback (page)) {__delete_from_swap_cache (page);SetPageDirty (page); retval =1 ;
@@ -387,6 +387,25 @@ int remove_exclusive_swap_page(struct pa }/* +* Most of the time the page should have two references: one for the +* process and one for the swap cache. +*/ +int remove_exclusive_swap_page (struct page *page) +{ +return remove_exclusive_swap_page_count (page,2 ); +} + +/* +* The pageout code holds an extra reference to the page. That raises +* the reference count to test for to 2 for a page that is only in the +* swap cache plus 1 for each process that maps the page. +*/ +int remove_exclusive_swap_page_ref (struct page *page) +{ +return remove_exclusive_swap_page_count (page,2 +page_mapcount (page)); +} + +/* * Free the swap entry like above, but also try to * free the page cache entry if it is the last user. */
Signature · 1 line
All Rights Reversed
Re: [PATCH -mm 04/16] free swap space on swap-in/activation
Quoted 10 lines
mm/vmscan.c +2
-0@@ -1203,6 +1206,8 @@ static void shrink_active_list(unsigned__mod_zone_page_state (zone,NR_ACTIVE , pgmoved); pgmoved =0 ;spin_unlock_irq (&zone->lru_lock); +if (vm_swap_full ()) +pagevec_swap_free (&pvec);__pagevec_release (&pvec);spin_lock_irq (&zone->lru_lock); }
mm/vmscan.c static void shrink_active_list(unsigned · line 1224
Quoted 8 lines
mm/vmscan.c +2
-0@@ -1212,6 +1217,8 @@ static void shrink_active_list(unsigned__count_zone_vm_events (PGREFILL , zone, pgscanned);__count_vm_events (PGDEACTIVATE , pgdeactivate);spin_unlock_irq (&zone->lru_lock); +if (vm_swap_full ()) +pagevec_swap_free (&pvec);pagevec_release (&pvec); }
I'm sorry if I misunderstand your patches, but is this part needed after
[PATCH -mm 07/16] second chance replacement for anonymous pages
is applied?
After the patch, if shrink_active_list() is called about
anon pages(that is, with file=0), those pages should go to inactive list, so they should not be removed from swap cache, should they?
Thanks,
Daisuke Nishimura.
PATCH 5/16 define page_file_cache() function
In reply to Rik van RielFrom: Rik van Riel <[email protected]>
Define page_file_cache() function to answer the question:
is page backed by a file?
Originally part of Rik van Riel's split-lru patch. Extracted to make available for other, independent reclaim patches.
Moved inline function to linux/mm_inline.h where it will be needed by subsequent "split LRU" and "noreclaim" patches.
Unfortunately this needs to use a page flag, since the PG_swapbacked state needs to be preserved all the way to the point where the page is last removed from the LRU. Trying to derive the status from other info in the page resulted in wrong VM statistics in earlier split VM patchsets.
--- include/linux/mm_inline.h | 24 ++++++++++++++++++++++++ include/linux/page-flags.h | 2 ++ mm/memory.c | 3 +++ mm/migrate.c | 2 ++ mm/page_alloc.c | 4 ++++ mm/shmem.c | 1 + mm/swap_state.c | 3 +++ 7 files changed, 39 insertions(+)
include/linux/mm_inline.h +24 -0
@@ -1,3 +1,26 @@ +#ifndef LINUX_MM_INLINE_H +#define LINUX_MM_INLINE_H + +/** +* page_file_cache - should the page be on a file LRU or anon LRU? +* @page: the page to test +* +* Returns !0 if @page is page cache page backed by a regular filesystem, +* or 0 if @page is anonymous, tmpfs or otherwise ram or swap backed. +* +* We would like to get this info without a page flag, but the state +* needs to survive until the page is last deleted from the LRU, which +* could be as far down as __page_cache_release. +*/ +static inline int page_file_cache (struct page *page) +{ +if (PageSwapBacked (page)) +return 0 ; + +/* The page is page cache backed by a normal filesystem. */ +return 2 ; +} +static inline void add_page_to_lru_list (struct zone *zone,struct page *page,enum lru_list l) {
@@ -49,3 +72,4 @@ del_page_from_lru(struct zone *zone, str __dec_zone_state(zone, NR_INACTIVE + l); } +#endif
mm/shmem.c +1 -0
@@ -1378,6 +1378,7 @@ repeat: goto failed; } +SetPageSwapBacked (filepage);spin_lock (&info->lock); entry =shmem_swp_alloc (info, idx, sgp);if (IS_ERR (entry))
include/linux/page-flags.h +2 -0
@@ -93,6 +93,7 @@ enum pageflags { PG_mappedtodisk,/* Has blocks allocated on-disk */ PG_reclaim,/* To be reclaimed asap */ PG_buddy,/* Page is free, on buddy lists */ + PG_swapbacked,/* Page is backed by RAM/swap */ #ifdef CONFIG_IA64_UNCACHED_ALLOCATOR PG_uncached, /* Page has been mapped as uncached */ #endif
@@ -160,6 +161,7 @@ PAGEFLAG(Pinned, owner_priv_1) TESTSCFLA PAGEFLAG(Reserved, reserved) __CLEARPAGEFLAG(Reserved, reserved) PAGEFLAG(Private, private) __CLEARPAGEFLAG(Private, private) __SETPAGEFLAG(Private, private) +PAGEFLAG(SwapBacked, swapbacked) __CLEARPAGEFLAG(SwapBacked, swapbacked)/* * Only test-and-set exist for PG_writeback. The unconditional operators are
mm/memory.c +3 -0
@@ -1765,6 +1765,7 @@ gotten:ptep_clear_flush (vma, address, page_table);set_pte_at (mm, address, page_table, entry);update_mmu_cache (vma, address, entry); +SetPageSwapBacked (new_page);lru_cache_add_active (new_page);page_add_new_anon_rmap (new_page, vma, address);
@@ -2233,6 +2234,7 @@ static int do_anonymous_page(struct mm_sif (!pte_none (*page_table)) goto release;inc_mm_counter (mm, anon_rss); +SetPageSwapBacked (page);lru_cache_add_active (page);page_add_new_anon_rmap (page, vma, address);set_pte_at (mm, address, page_table, entry);
@@ -2374,6 +2376,7 @@ static int __do_fault(struct mm_struct *set_pte_at (mm, address, page_table, entry);if (anon) {inc_mm_counter (mm, anon_rss); +SetPageSwapBacked (page);lru_cache_add_active (page);page_add_new_anon_rmap (page, vma, address); }else {
mm/swap_state.c +3 -0
@@ -74,6 +74,7 @@ int add_to_swap_cache(struct page *page,BUG_ON (!PageLocked (page));BUG_ON (PageSwapCache (page));BUG_ON (PagePrivate (page)); +BUG_ON (!PageSwapBacked (page)); error =radix_tree_preload (gfp_mask);if (!error) {write_lock_irq (&swapper_space.tree_lock);
@@ -295,6 +296,7 @@ struct page *read_swap_cache_async(swp_e* May fail (-ENOMEM) if radix-tree node allocation failed. */ SetPageLocked (new_page); +SetPageSwapBacked (new_page); err =add_to_swap_cache (new_page, entry, gfp_mask &GFP_KERNEL );if (!err) {/*
@@ -304,6 +306,7 @@ struct page *read_swap_cache_async(swp_eswap_readpage (NULL , new_page);return new_page; } +ClearPageSwapBacked (new_page);ClearPageLocked (new_page);swap_free (entry); }while (err != -ENOMEM );
mm/migrate.c +2 -0
@@ -558,6 +558,8 @@ static int move_to_new_page(struct page/* Prepare mapping for the new page.*/ newpage->index = page->index; newpage->mapping = page->mapping; +if (PageSwapBacked (page)) +SetPageSwapBacked (newpage); mapping =page_mapping (page);if (!mapping)
mm/page_alloc.c +4 -0
@@ -261,6 +261,7 @@ static void bad_page(struct page *page)1 << PG_slab |1 << PG_swapcache |1 << PG_writeback | +1 << PG_swapbacked | 1 << PG_buddy );set_page_count (page,0 );reset_page_mapcount (page);
@@ -494,6 +495,8 @@ static inline int free_pages_check(strucbad_page (page);if (PageDirty (page))__ClearPageDirty (page); +if (PageSwapBacked (page)) +__ClearPageSwapBacked (page);/* * For now, we report if PG_reserved was found set, but do not * clear it, and do not free the page. But we shall soon need
@@ -644,6 +647,7 @@ static int prep_new_page(struct page *pa 1 << PG_swapcache | 1 << PG_writeback | 1 << PG_reserved | + 1 << PG_swapbacked | 1 << PG_buddy ))))bad_page (page);
Signature · 1 line
All Rights Reversed
PATCH 6/16 split LRU lists into anon & file sets
In reply to Rik van RielFrom: Rik van Riel <[email protected]>
Split the LRU lists in two, one set for pages that are backed by real file systems ("file") and one for pages that are backed by memory and swap ("anon"). The latter includes tmpfs.
Eventually mlocked pages will be taken off the LRUs alltogether. A patch for that already exists and just needs to be integrated into this series.
This patch mostly has the infrastructure and a basic policy to balance how much we scan the anon lists and how much we scan the file lists. The big policy changes are in separate patches.
--- drivers/base/node.c | 56 +++-- fs/cifs/file.c | 4 fs/nfs/dir.c | 2 fs/ntfs/file.c | 4 fs/proc/proc_misc.c | 81 +++++--- fs/ramfs/file-nommu.c | 4 include/linux/memcontrol.h | 2 include/linux/mm_inline.h | 55 ++++- include/linux/mmzone.h | 33 ++- include/linux/pagevec.h | 29 ++- include/linux/swap.h | 20 +- include/linux/vmstat.h | 10 + mm/filemap.c | 9 mm/memcontrol.c | 73 +++---- mm/memory.c | 6 mm/page-writeback.c | 8 mm/page_alloc.c | 24 +- mm/readahead.c | 2 mm/swap.c | 12 - mm/swap_state.c | 2 mm/vmscan.c | 428 ++++++++++++++++++++------------------------- mm/vmstat.c | 14 - 22 files changed, 491 insertions(+), 387 deletions(-)
fs/proc/proc_misc.c +49 -32
@@ -132,6 +132,10 @@ static int meminfo_read_proc(char *page,unsigned long allowed;struct vmalloc_info vmi;long cached; +unsigned long inactive_anon; +unsigned long active_anon; +unsigned long inactive_file; +unsigned long active_file;/* * display in kilobytes.
@@ -150,48 +154,61 @@ static int meminfo_read_proc(char *page,get_vmalloc_info (&vmi); + inactive_anon =global_page_state (NR_INACTIVE_ANON ); + active_anon =global_page_state (NR_ACTIVE_ANON ); + inactive_file =global_page_state (NR_INACTIVE_FILE ); + active_file =global_page_state (NR_ACTIVE_FILE ); +/* * Tagged format, for easy grepping and expansion. */ len = sprintf(page, - "MemTotal: %8lu kB\n" - "MemFree: %8lu kB\n" - "Buffers: %8lu kB\n" - "Cached: %8lu kB\n" - "SwapCached: %8lu kB\n" - "Active: %8lu kB\n" - "Inactive: %8lu kB\n" + "MemTotal: %8lu kB\n" + "MemFree: %8lu kB\n" + "Buffers: %8lu kB\n" + "Cached: %8lu kB\n" + "SwapCached: %8lu kB\n" + "Active: %8lu kB\n" + "Inactive: %8lu kB\n" + "Active(anon): %8lu kB\n" + "Inactive(anon): %8lu kB\n" + "Active(file): %8lu kB\n" + "Inactive(file): %8lu kB\n" #ifdef CONFIG_HIGHMEM - "HighTotal: %8lu kB\n" - "HighFree: %8lu kB\n" - "LowTotal: %8lu kB\n" - "LowFree: %8lu kB\n" -#endif - "SwapTotal: %8lu kB\n" - "SwapFree: %8lu kB\n" - "Dirty: %8lu kB\n" - "Writeback: %8lu kB\n" - "AnonPages: %8lu kB\n" - "Mapped: %8lu kB\n" - "Slab: %8lu kB\n" - "SReclaimable: %8lu kB\n" - "SUnreclaim: %8lu kB\n" - "PageTables: %8lu kB\n" - "NFS_Unstable: %8lu kB\n" - "Bounce: %8lu kB\n" - "WritebackTmp: %8lu kB\n" - "CommitLimit: %8lu kB\n" - "Committed_AS: %8lu kB\n" - "VmallocTotal: %8lu kB\n" - "VmallocUsed: %8lu kB\n" - "VmallocChunk: %8lu kB\n", + "HighTotal: %8lu kB\n" + "HighFree: %8lu kB\n" + "LowTotal: %8lu kB\n" + "LowFree: %8lu kB\n" +#endif + "SwapTotal: %8lu kB\n" + "SwapFree: %8lu kB\n" + "Dirty: %8lu kB\n" + "Writeback: %8lu kB\n" + "AnonPages: %8lu kB\n" + "Mapped: %8lu kB\n" + "Slab: %8lu kB\n" + "SReclaimable: %8lu kB\n" + "SUnreclaim: %8lu kB\n" + "PageTables: %8lu kB\n" + "NFS_Unstable: %8lu kB\n" + "Bounce: %8lu kB\n" + "WritebackTmp: %8lu kB\n" + "CommitLimit: %8lu kB\n" + "Committed_AS: %8lu kB\n" + "VmallocTotal: %8lu kB\n" + "VmallocUsed: %8lu kB\n" + "VmallocChunk: %8lu kB\n", K(i.totalram), K(i.freeram), K(i.bufferram), K(cached), K(total_swapcache_pages), - K(global_page_state(NR_ACTIVE)), - K(global_page_state(NR_INACTIVE)), + K(active_anon + active_file), + K(inactive_anon + inactive_file), + K(active_anon), + K(inactive_anon), + K(active_file), + K(inactive_file), #ifdef CONFIG_HIGHMEM K(i.totalhigh), K(i.freehigh),
fs/cifs/file.c +2 -2
@@ -1778,7 +1778,7 @@ static void cifs_copy_cache_pages(structSetPageUptodate (page);unlock_page (page);if (!pagevec_add (plru_pvec, page)) -__pagevec_lru_add (plru_pvec); +__pagevec_lru_add_file (plru_pvec); data +=PAGE_CACHE_SIZE ; }return ;
@@ -1912,7 +1912,7 @@ static int cifs_readpages(struct file *f bytes_read =0 ; } -pagevec_lru_add (&lru_pvec); +pagevec_lru_add_file (&lru_pvec);/* need to free smb_read_data buf before exit */ if (smb_read_data) {
fs/ntfs/file.c +2 -2
@@ -439,7 +439,7 @@ static inline int __ntfs_grab_cache_page pages[nr] = *cached_page;page_cache_get (*cached_page);if (unlikely (!pagevec_add (lru_pvec, *cached_page))) -__pagevec_lru_add (lru_pvec); +__pagevec_lru_add_file (lru_pvec); *cached_page =NULL ; } index++;
@@ -2084,7 +2084,7 @@ err_out: OSYNC_METADATA|OSYNC_DATA); } } -pagevec_lru_add (&lru_pvec); +pagevec_lru_add_file (&lru_pvec);ntfs_debug ("Done. Returning %s (written 0x%lx, status %li)." , written ?"written" :"status" , (unsigned long )written, (long )status);
fs/nfs/dir.c +1 -1
@@ -1524,7 +1524,7 @@ static int nfs_symlink(struct inode *dirif (!add_to_page_cache (page, dentry->d_inode->i_mapping,0 ,GFP_KERNEL )) {pagevec_add (&lru_pvec, page); -pagevec_lru_add (&lru_pvec); +pagevec_lru_add_file (&lru_pvec);SetPageUptodate (page);unlock_page (page); }else
fs/ramfs/file-nommu.c +2 -2
@@ -111,12 +111,12 @@ static int ramfs_nommu_expand_for_mappin goto add_error;if (!pagevec_add (&lru_pvec, page)) -__pagevec_lru_add (&lru_pvec); +__pagevec_lru_add_file (&lru_pvec);unlock_page (page); } -pagevec_lru_add (&lru_pvec); +pagevec_lru_add_file (&lru_pvec);return 0 ; fsize_exceeded:
drivers/base/node.c +33 -23
67 lines not on this page · raw message
mm/memory.c +3 -3
@@ -1766,7 +1766,7 @@ gotten:set_pte_at (mm, address, page_table, entry);update_mmu_cache (vma, address, entry);SetPageSwapBacked (new_page); -lru_cache_add_active (new_page); +lru_cache_add_active_anon (new_page);page_add_new_anon_rmap (new_page, vma, address);/* Free the old page.. */
@@ -2235,7 +2235,7 @@ static int do_anonymous_page(struct mm_s goto release;inc_mm_counter (mm, anon_rss);SetPageSwapBacked (page); -lru_cache_add_active (page); +lru_cache_add_active_anon (page);page_add_new_anon_rmap (page, vma, address);set_pte_at (mm, address, page_table, entry);
@@ -2377,7 +2377,7 @@ static int __do_fault(struct mm_struct *if (anon) {inc_mm_counter (mm, anon_rss);SetPageSwapBacked (page); -lru_cache_add_active (page); +lru_cache_add_active_anon (page);page_add_new_anon_rmap (page, vma, address); }else {inc_mm_counter (mm, file_rss);
mm/page_alloc.c +17 -7
@@ -1908,10 +1908,13 @@ void show_free_areas(void) } } - printk("Active:%lu inactive:%lu dirty:%lu writeback:%lu unstable:%lu\n" + printk("Active_anon:%lu active_file:%lu inactive_anon%lu\n" + " inactive_file:%lu dirty:%lu writeback:%lu unstable:%lu\n" " free:%lu slab:%lu mapped:%lu pagetables:%lu bounce:%lu\n", - global_page_state(NR_ACTIVE), - global_page_state(NR_INACTIVE), + global_page_state(NR_ACTIVE_ANON), + global_page_state(NR_ACTIVE_FILE), + global_page_state(NR_INACTIVE_ANON), + global_page_state(NR_INACTIVE_FILE), global_page_state(NR_FILE_DIRTY), global_page_state(NR_WRITEBACK), global_page_state(NR_UNSTABLE_NFS),
@@ -1934,8 +1937,10 @@ void show_free_areas(void)" min:%lukB" " low:%lukB" " high:%lukB" -" active:%lukB" -" inactive:%lukB" +" active_anon:%lukB" +" inactive_anon:%lukB" +" active_file:%lukB" +" inactive_file:%lukB" " present:%lukB" " pages_scanned:%lu" " all_unreclaimable? %s"
@@ -1945,8 +1950,10 @@ void show_free_areas(void)K (zone->pages_min),K (zone->pages_low),K (zone->pages_high), -K (zone_page_state (zone,NR_ACTIVE )), -K (zone_page_state (zone,NR_INACTIVE )), +K (zone_page_state (zone,NR_ACTIVE_ANON )), +K (zone_page_state (zone,NR_INACTIVE_ANON )), +K (zone_page_state (zone,NR_ACTIVE_FILE )), +K (zone_page_state (zone,NR_INACTIVE_FILE )),K (zone->present_pages), zone->pages_scanned, (zone_is_all_unreclaimable (zone) ?"yes" :"no" )
@@ -3503,6 +3510,9 @@ static void __paginginit free_area_init_INIT_LIST_HEAD (&zone->list[l]); zone->nr_scan[l] =0 ; } + zone->recent_rotated_anon =0 ; + zone->recent_rotated_file =0 ; +//TODO recent_scanned_* ??? zap_zone_vm_stats (zone); zone->flags =0 ;if (!size)
mm/swap.c +9 -3
@@ -108,6 +108,7 @@ enum lru_list page_lru(struct page *pageif (PageActive (page)) lru +=LRU_ACTIVE ; + lru +=page_file_cache (page);return lru; }
@@ -133,7 +134,8 @@ static void pagevec_move_tail(struct pagspin_lock (&zone->lru_lock); }if (PageLRU (page) && !PageActive (page)) { -list_move_tail (&page->lru, &zone->list[LRU_INACTIVE ]); +int lru =page_file_cache (page); +list_move_tail (&page->lru, &zone->list[lru]); pgmoved++; } }
@@ -174,9 +176,13 @@ void activate_page(struct page *page)spin_lock_irq (&zone->lru_lock);if (PageLRU (page) && !PageActive (page)) { -del_page_from_inactive_list (zone, page); +int lru =LRU_BASE ; + lru +=page_file_cache (page); +del_page_from_lru_list (zone, page, lru); +SetPageActive (page); -add_page_to_active_list (zone, page); + lru +=LRU_ACTIVE ; +add_page_to_lru_list (zone, page, lru);__count_vm_event (PGACTIVATE );mem_cgroup_move_lists (page, true); }
mm/readahead.c +1 -1
@@ -229,7 +229,7 @@ int do_page_cache_readahead(struct addre*/ unsigned long max_sane_readahead (unsigned long nr) { -return min (nr, (node_page_state (numa_node_id (),NR_INACTIVE ) +return min (nr, (node_page_state (numa_node_id (),NR_INACTIVE_FILE ) +node_page_state (numa_node_id (),NR_FREE_PAGES )) /2 ); }
mm/filemap.c +7 -2
@@ -33,6 +33,7 @@#include <linux/cpuset.h> #include <linux/hardirq.h> /* for BUG_ON(!in_atomic()) only */ #include <linux/memcontrol.h> +#include <linux/mm_inline.h> /* for page_file_cache() */ #include "internal.h" /*
@@ -489,8 +490,12 @@ int add_to_page_cache_lru(struct page *p pgoff_t offset, gfp_t gfp_mask) { int ret = add_to_page_cache(page, mapping, offset, gfp_mask); -if (ret ==0 ) -lru_cache_add (page); +if (ret ==0 ) { +if (page_file_cache (page)) +lru_cache_add_file (page); +else +lru_cache_add_active_anon (page); + }return ret; }
mm/vmstat.c +9 -5
@@ -695,8 +695,10 @@ const struct seq_operations pagetypeinfostatic const char *const vmstat_text[] = {/* Zoned VM counters */ "nr_free_pages" , -"nr_inactive" , -"nr_active" , +"nr_inactive_anon" , +"nr_active_anon" , +"nr_inactive_file" , +"nr_active_file" ,"nr_anon_pages" ,"nr_mapped" ,"nr_file_pages" ,
@@ -764,7 +766,7 @@ static void zoneinfo_show_print(struct s"\n min %lu" "\n low %lu" "\n high %lu" -"\n scanned %lu (a: %lu i: %lu)" +"\n scanned %lu (aa: %lu ia: %lu af: %lu if: %lu)" "\n spanned %lu" "\n present %lu" , zone_page_state(zone, NR_FREE_PAGES),
@@ -772,8 +774,10 @@ static void zoneinfo_show_print(struct s zone->pages_low, zone->pages_high, zone->pages_scanned, - zone->nr_scan[LRU_ACTIVE ], - zone->nr_scan[LRU_INACTIVE ], + zone->nr_scan[LRU_ACTIVE_ANON ], + zone->nr_scan[LRU_INACTIVE_ANON ], + zone->nr_scan[LRU_ACTIVE_FILE ], + zone->nr_scan[LRU_INACTIVE_FILE ], zone->spanned_pages, zone->present_pages);
mm/vmscan.c +199 -229
730 lines not on this page · raw message
mm/swap_state.c +1 -1
@@ -302,7 +302,7 @@ struct page *read_swap_cache_async(swp_e/* * Initiate read into locked page and return. */ -lru_cache_add_active (new_page); +lru_cache_add_active_anon (new_page);swap_readpage (NULL , new_page);return new_page; }
include/linux/mmzone.h +27 -6
@@ -81,21 +81,23 @@ struct zone_padding {enum zone_stat_item {/* First 128 byte cacheline (assuming 64 bit words) */ NR_FREE_PAGES , -NR_INACTIVE ,/* must match order of LRU_[IN]ACTIVE */ -NR_ACTIVE ,/* " " " " " */ +NR_INACTIVE_ANON ,/* must match order of LRU_[IN]ACTIVE_* */ +NR_ACTIVE_ANON ,/* " " " " " */ +NR_INACTIVE_FILE ,/* " " " " " */ +NR_ACTIVE_FILE ,/* " " " " " */ NR_ANON_PAGES ,/* Mapped anonymous pages */ NR_FILE_MAPPED ,/* pagecache pages mapped into pagetables. only modified from process context */ NR_FILE_PAGES ,NR_FILE_DIRTY ,NR_WRITEBACK , -/* Second 128 byte cacheline */ NR_SLAB_RECLAIMABLE ,NR_SLAB_UNRECLAIMABLE ,NR_PAGETABLE ,/* used for pagetables */ NR_UNSTABLE_NFS ,/* NFS unstable pages */ NR_BOUNCE ,NR_VMSCAN_WRITE , +/* Second 128 byte cacheline */ NR_WRITEBACK_TEMP ,/* Writeback using temporary buffers */ #ifdef CONFIG_NUMA NUMA_HIT ,/* allocated in intended node */
@@ -107,18 +109,33 @@ enum zone_stat_item {#endif NR_VM_ZONE_STAT_ITEMS }; +/* +* We do arithmetic on the LRU lists in various places in the code, +* so it is important to keep the active lists LRU_ACTIVE higher in +* the array than the corresponding inactive lists, and to keep +* the *_FILE lists LRU_FILE higher than the corresponding _ANON lists. +*/ #define LRU_BASE 0 +#define LRU_ACTIVE 1 +#define LRU_FILE 2enum lru_list { -LRU_INACTIVE =LRU_BASE ,/* must match order of NR_[IN]ACTIVE */ -LRU_ACTIVE ,/* " " " " " */ +LRU_INACTIVE_ANON =LRU_BASE , +LRU_ACTIVE_ANON =LRU_BASE +LRU_ACTIVE , +LRU_INACTIVE_FILE =LRU_BASE +LRU_FILE , +LRU_ACTIVE_FILE =LRU_BASE +LRU_FILE +LRU_ACTIVE ,NR_LRU_LISTS };#define for_each_lru (l) for (l = 0; l < NR_LRU_LISTS; l++) +static inline int is_file_lru (enum lru_list l) +{ +return (l ==LRU_INACTIVE_FILE || l ==LRU_ACTIVE_FILE ); +} +static inline int is_active_lru (enum lru_list l) { -return (l ==LRU_ACTIVE ); +return (l ==LRU_ACTIVE_ANON || l ==LRU_ACTIVE_FILE ); }enum lru_list page_lru (struct page *page);
@@ -269,6 +286,10 @@ struct zone {spinlock_t lru_lock;struct list_head list[NR_LRU_LISTS ];unsigned long nr_scan[NR_LRU_LISTS ]; + +unsigned long recent_rotated_anon; +unsigned long recent_rotated_file; +unsigned long pages_scanned;/* since last reclaim */ unsigned long flags;/* zone flags, see below */
include/linux/mm_inline.h +40 -15
105 lines not on this page · raw message
include/linux/pagevec.h +23 -6
@@ -81,20 +81,37 @@ static inline void pagevec_free(struct p__pagevec_free (pvec); } -static inline void __pagevec_lru_add (struct pagevec *pvec) +static inline void __pagevec_lru_add_anon (struct pagevec *pvec) { -____pagevec_lru_add (pvec,LRU_INACTIVE ); +____pagevec_lru_add (pvec,LRU_INACTIVE_ANON ); } -static inline void __pagevec_lru_add_active (struct pagevec *pvec) +static inline void __pagevec_lru_add_active_anon (struct pagevec *pvec) { -____pagevec_lru_add (pvec,LRU_ACTIVE ); +____pagevec_lru_add (pvec,LRU_ACTIVE_ANON ); } -static inline void pagevec_lru_add (struct pagevec *pvec) +static inline void __pagevec_lru_add_file (struct pagevec *pvec) +{ +____pagevec_lru_add (pvec,LRU_INACTIVE_FILE ); +} + +static inline void __pagevec_lru_add_active_file (struct pagevec *pvec) +{ +____pagevec_lru_add (pvec,LRU_ACTIVE_FILE ); +} + + +static inline void pagevec_lru_add_file (struct pagevec *pvec) +{ +if (pagevec_count (pvec)) +__pagevec_lru_add_file (pvec); +} + +static inline void pagevec_lru_add_anon (struct pagevec *pvec) {if (pagevec_count (pvec)) -__pagevec_lru_add (pvec); +__pagevec_lru_add_anon (pvec); }#endif /* _LINUX_PAGEVEC_H */
include/linux/vmstat.h +10 -0
@@ -159,6 +159,16 @@ static inline unsigned long zone_page_streturn x; } +extern unsigned long global_lru_pages (void ); + +static inline unsigned long zone_lru_pages (struct zone *zone) +{ +return (zone_page_state (zone,NR_ACTIVE_ANON ) + +zone_page_state (zone,NR_ACTIVE_FILE ) + +zone_page_state (zone,NR_INACTIVE_ANON ) + +zone_page_state (zone,NR_INACTIVE_FILE )); +} +#ifdef CONFIG_NUMA /* * Determine the per node value of a stat item. This function
mm/page-writeback.c +2 -6
@@ -331,9 +331,7 @@ static unsigned long highmem_dirtyable_mstruct zone *z = &NODE_DATA (node)->node_zones[ZONE_HIGHMEM ]; - x +=zone_page_state (z,NR_FREE_PAGES ) - +zone_page_state (z,NR_INACTIVE ) - +zone_page_state (z,NR_ACTIVE ); + x +=zone_page_state (z,NR_FREE_PAGES ) +zone_lru_pages (z); }/* * Make sure that the number of highmem pages is never larger
@@ -351,9 +349,7 @@ static unsigned long determine_dirtyable {unsigned long x; - x =global_page_state (NR_FREE_PAGES ) - +global_page_state (NR_INACTIVE ) - +global_page_state (NR_ACTIVE ); + x =global_page_state (NR_FREE_PAGES ) +global_lru_pages ();if (!vm_highmem_is_dirtyable) x -=highmem_dirtyable_memory (x);
include/linux/swap.h +15 -5
@@ -184,14 +184,24 @@ extern void swap_setup(void);* lru_cache_add: add a page to the page lists * @page: the page to add */ -static inline void lru_cache_add (struct page *page) +static inline void lru_cache_add_anon (struct page *page) { -__lru_cache_add (page,LRU_INACTIVE ); +__lru_cache_add (page,LRU_INACTIVE_ANON ); } -static inline void lru_cache_add_active (struct page *page) +static inline void lru_cache_add_active_anon (struct page *page) { -__lru_cache_add (page,LRU_ACTIVE ); +__lru_cache_add (page,LRU_ACTIVE_ANON ); +} + +static inline void lru_cache_add_file (struct page *page) +{ +__lru_cache_add (page,LRU_INACTIVE_FILE ); +} + +static inline void lru_cache_add_active_file (struct page *page) +{ +__lru_cache_add (page,LRU_ACTIVE_FILE ); }/* linux/mm/vmscan.c */
@@ -199,7 +209,7 @@ extern unsigned long try_to_free_pages(s gfp_t gfp_mask);extern unsigned long try_to_free_mem_cgroup_pages (struct mem_cgroup *mem,gfp_t gfp_mask); -extern int __isolate_lru_page (struct page *page,int mode); +extern int __isolate_lru_page (struct page *page,int mode,int file);extern unsigned long shrink_all_memory (unsigned long nr_pages);extern int vm_swappiness;extern int remove_mapping (struct address_space *mapping,struct page *page);
include/linux/memcontrol.h +1 -1
@@ -41,7 +41,7 @@ extern unsigned long mem_cgroup_isolate_ unsigned long *scanned, int order, int mode, struct zone *z, struct mem_cgroup *mem_cont, - int active); + int active, int file);extern void mem_cgroup_out_of_memory (struct mem_cgroup *mem,gfp_t gfp_mask);int task_in_mem_cgroup (struct task_struct *task,const struct mem_cgroup *mem);
mm/memcontrol.c +38 -35
150 lines not on this page · raw message
Signature · 1 line
All Rights Reversed
PATCH 7/16 second chance replacement for anonymous pages
In reply to Rik van RielFrom: Rik van Riel <[email protected]>
We avoid evicting and scanning anonymous pages for the most part, but under some workloads we can end up with most of memory filled with anonymous pages. At that point, we suddenly need to clear the referenced bits on all of memory, which can take ages on very large memory systems.
We can reduce the maximum number of pages that need to be scanned by not taking the referenced state into account when deactivating an anonymous page. After all, every anonymous page starts out referenced, so why check?
If an anonymous page gets referenced again before it reaches the end of the inactive list, we move it back to the active list.
To keep the maximum amount of necessary work reasonable, we scale the active to inactive ratio with the size of memory, using the formula active:inactive ratio = sqrt(memory in GB * 10).
Kswapd CPU use now seems to scale by the amount of pageout bandwidth, instead of by the amount of memory present in the system.
--- include/linux/mm_inline.h | 12 ++++++++++++ include/linux/mmzone.h | 5 +++++ mm/page_alloc.c | 40 ++++++++++++++++++++++++++++++++++++++++ mm/vmscan.c | 38 +++++++++++++++++++++++++++++++------- mm/vmstat.c | 6 ++++-- 5 files changed, 92 insertions(+), 9 deletions(-)
include/linux/mm_inline.h +12 -0
@@ -97,4 +97,16 @@ del_page_from_lru(struct zone *zone, str __dec_zone_state(zone, NR_INACTIVE_ANON + l); } +static inline int inactive_anon_low (struct zone *zone) +{ +unsigned long active, inactive; + + active =zone_page_state (zone,NR_ACTIVE_ANON ); + inactive =zone_page_state (zone,NR_INACTIVE_ANON ); + +if (inactive * zone->inactive_ratio < active) +return 1 ; + +return 0 ; +}#endif
include/linux/mmzone.h +5 -0
@@ -311,6 +311,11 @@ struct zone {*/ int prev_priority; +/* +* The ratio of active to inactive pages. +*/ +unsigned int inactive_ratio; +ZONE_PADDING (_pad2_)/* Rarely used or read-mostly fields */
mm/page_alloc.c +40 -0
@@ -4269,6 +4269,45 @@ void setup_per_zone_pages_min(void)calculate_totalreserve_pages (); } +/** +* setup_per_zone_inactive_ratio - called when min_free_kbytes changes. +* +* The inactive anon list should be small enough that the VM never has to +* do too much work, but large enough that each inactive page has a chance +* to be referenced again before it is swapped out. +* +* The inactive_anon ratio is the ratio of active to inactive anonymous +* pages. Ie. a ratio of 3 means 3:1 or 25% of the anonymous pages are +* on the inactive list. +* +* total return max +* memory value inactive anon +* ------------------------------------- +* 10MB 1 5MB +* 100MB 1 50MB +* 1GB 3 250MB +* 10GB 10 0.9GB +* 100GB 31 3GB +* 1TB 101 10GB +* 10TB 320 32GB +*/ +void setup_per_zone_inactive_ratio (void ) +{ +struct zone *zone; + +for_each_zone (zone) { +unsigned int gb, ratio; + +/* Zone size in gigabytes */ + gb = zone->present_pages >> (30 -PAGE_SHIFT ); + ratio =int_sqrt (10 * gb); +if (!ratio) + ratio =1 ; + + zone->inactive_ratio = ratio; + } +} +/* * Initialise min_free_kbytes. *
@@ -4306,6 +4345,7 @@ static int __init init_per_zone_pages_mi min_free_kbytes =65536 ;setup_per_zone_pages_min ();setup_per_zone_lowmem_reserve (); +setup_per_zone_inactive_ratio ();return 0 ; }module_init (init_per_zone_pages_min)
mm/vmscan.c +31 -7
@@ -114,7 +114,7 @@ struct scan_control {/* * From 0 .. 100. Higher means more swappy. */ -int vm_swappiness =60 ; +int vm_swappiness =20 ;long vm_total_pages;/* The total number of pages which the VM controls */ static LIST_HEAD (shrinker_list);
@@ -1008,7 +1008,7 @@ static inline int zone_is_near_oom(strucstatic void shrink_active_list (unsigned long nr_pages,struct zone *zone,struct scan_control *sc,int priority,int file) { -unsigned long pgmoved; +unsigned long pgmoved =0 ;int pgdeactivate =0 ;unsigned long pgscanned;LIST_HEAD (l_hold);/* The pages which were snipped off */
@@ -1040,13 +1040,25 @@ static void shrink_active_list(unsignedcond_resched (); page =lru_to_page (&l_hold);list_del (&page->lru); -if (page_referenced (page,0 , sc->mem_cgroup)) +if (page_referenced (page,0 , sc->mem_cgroup) && file) { +/* Referenced file pages stay active. */ list_add (&page->lru, &l_active); -else + }else {list_add (&page->lru, &l_inactive); +if (!file) +/* Anonymous pages always get deactivated. */ + pgmoved++; + } }/* +* Count the referenced anon pages as rotated, to balance pageout +* scan pressure between file and anonymous pages in get_sacn_ratio. +*/ +if (!file) + zone->recent_rotated_anon += pgmoved; + +/* * Now put the pages back on the appropriate [file or anon] inactive * and active lists. */
@@ -1129,7 +1141,11 @@ static unsigned long shrink_list(enum lr {int file =is_file_lru (lru); -if (lru ==LRU_ACTIVE_ANON || lru ==LRU_ACTIVE_FILE ) { +if (lru ==LRU_ACTIVE_FILE ) { +shrink_active_list (nr_to_scan, zone, sc, priority, file); +return 0 ; + } +if (lru ==LRU_ACTIVE_ANON &&inactive_anon_low (zone)) {shrink_active_list (nr_to_scan, zone, sc, priority, file);return 0 ; }
@@ -1239,8 +1255,8 @@ static unsigned long shrink_zone(int pri } } -while (nr[LRU_ACTIVE_ANON ] || nr[LRU_INACTIVE_ANON ] || - nr[LRU_ACTIVE_FILE ] || nr[LRU_INACTIVE_FILE ]) { +while (nr[LRU_INACTIVE_ANON ] || nr[LRU_ACTIVE_FILE ] || + nr[LRU_INACTIVE_FILE ]) {for_each_lru (l) {if (nr[l]) { nr_to_scan = min(nr[l],
@@ -1542,6 +1558,14 @@ loop_again: priority != DEF_PRIORITY)continue ; +/* +* Do some background aging of the anon list, to give +* pages a chance to be referenced before reclaiming. +*/ +if (inactive_anon_low (zone)) +shrink_active_list (SWAP_CLUSTER_MAX , zone, + &sc, priority,0 ); +if (!zone_watermark_ok (zone, order, zone->pages_high,0 ,0 )) { end_zone = i;
mm/vmstat.c +4 -2
@@ -814,10 +814,12 @@ static void zoneinfo_show_print(struct sseq_printf (m,"\n all_unreclaimable: %u" "\n prev_priority: %i" -"\n start_pfn: %lu" , +"\n start_pfn: %lu" +"\n inactive_ratio: %u" ,zone_is_all_unreclaimable (zone), zone->prev_priority, - zone->zone_start_pfn); + zone->zone_start_pfn, + zone->inactive_ratio);seq_putc (m,'\n' ); }
Signature · 1 line
All Rights Reversed
Re: [PATCH -mm 07/16] second chance replacement for anonymous pages
Hi.
Quoted 23 lines
mm/vmscan.c +7
-3@@ -1008,7 +1008,7 @@ static inline int zone_is_near_oom(strucstatic void shrink_active_list (unsigned long nr_pages,struct zone *zone,struct scan_control *sc,int priority,int file) { -unsigned long pgmoved; +unsigned long pgmoved =0 ;int pgdeactivate =0 ;unsigned long pgscanned;LIST_HEAD (l_hold);/* The pages which were snipped off */ @@ -1040,13 +1040,25 @@ static void shrink_active_list(unsignedcond_resched (); page =lru_to_page (&l_hold);list_del (&page->lru); -if (page_referenced (page,0 , sc->mem_cgroup)) +if (page_referenced (page,0 , sc->mem_cgroup) && file) { +/* Referenced file pages stay active. */ list_add (&page->lru, &l_active); -else + }else {list_add (&page->lru, &l_inactive); +if (!file) +/* Anonymous pages always get deactivated. */ + pgmoved++;
mm/vmscan.c static void shrink_active_list(unsigned · line 1060
Quoted 10 lines
mm/vmscan.c +7
-0+ } }/* +* Count the referenced anon pages as rotated, to balance pageout +* scan pressure between file and anonymous pages in get_sacn_ratio. +*/ +if (!file) + zone->recent_rotated_anon += pgmoved; +
Shouldn't 'pgmoved' be cleared to 0 before scanning l_hold? It's used to store the result of sc->isolate_pages() before scanning l_hold.
Thanks,
Daisuke Nishimura.
Re: [PATCH -mm 07/16] second chance replacement for anonymous pages
Quoted 11 lines · On Wed, 28 May 2008 14:36:56 +0900 Daisuke Nishimura…
mm/vmscan.c static void shrink_active_list(unsigned · line 1060
Quoted 7 lines
mm/vmscan.c +6
-0/* +* Count the referenced anon pages as rotated, to balance pageout +* scan pressure between file and anonymous pages in get_sacn_ratio. +*/ +if (!file) + zone->recent_rotated_anon += pgmoved; +Shouldn't 'pgmoved' be cleared to 0 before scanning
l_hold? It's used to store the result ofsc->isolate_pages()before scanningl_hold.
It is zeroed 2 statements down.
if (!file)
zone->recent_rotated_anon += pgmoved;
/*
* Now put the pages back on the appropriate [file or anon] inactive
* and active lists.
*/
pagevec_init(&pvec, 1);
pgmoved = 0;Signature · 1 line
All rights reversed.
Re: [PATCH -mm 07/16] second chance replacement for anonymous pages
Quoted 16 lines · Rik van Riel
Quoted 10 lines · On Wed, 28 May 2008 14:36:56 +0900 Daisuke Nishimura…
mm/vmscan.c static void shrink_active_list(unsigned · line 1060
Quoted 7 lines
mm/vmscan.c +6
-0/* +* Count the referenced anon pages as rotated, to balance pageout +* scan pressure between file and anonymous pages in get_sacn_ratio. +*/ +if (!file) + zone->recent_rotated_anon += pgmoved; +Shouldn't 'pgmoved' be cleared to 0 before scanning
l_hold? It's used to store the result ofsc->isolate_pages()before scanningl_hold.It is zeroed 2 statements down.
Quoted 10 lines
if (!file)
zone->recent_rotated_anon += pgmoved;
/*
* Now put the pages back on the appropriate [file or anon] inactive
* and active lists.
*/
pagevec_init(&pvec, 1);
pgmoved = 0;When all the patches applied, shrink_active_list() looks like:
1135 pgmoved = sc->isolate_pages(nr_pages, &l_hold, &pgscanned, sc->order,
1136 ISOLATE_ACTIVE, zone,
1137 sc->mem_cgroup, 1, file);
:
1150 if (file)
1151 __mod_zone_page_state(zone, NR_ACTIVE_FILE, -pgmoved);
1152 else
1153 __mod_zone_page_state(zone, NR_ACTIVE_ANON, -pgmoved);
1154 spin_unlock_irq(&zone->lru_lock);
1155
1156 while (!list_empty(&l_hold)) {
:
1166 if (page_referenced(page, 0, sc->mem_cgroup) && file) {
1167 /* Referenced file pages stay active. */
1168 list_add(&page->lru, &l_active);
1169 } else {
1170 list_add(&page->lru, &l_inactive);
1171 if (!file)
1172 /* Anonymous pages always get deactivated. */
1173 pgmoved++;
:
1178 }
1179 }
1180
1181 /*
1182 * Count the referenced anon pages as rotated, to balance pageout
1183 * scan pressure between file and anonymous pages in get_sacn_ratio.
1184 */
1185 if (!file)
1186 zone->recent_rotated_anon += pgmoved;So, I wondered that there is no need to clear pgmoved before 1156 line.
Thanks,
Daisuke Nishimura.
Re: [PATCH -mm 07/16] second chance replacement for anonymous pages
Quoted 3 lines · On Thu, 29 May 2008 00:42:52 +0900 Daisuke Nishimura…
When all the patches applied,
shrink_active_list()looks like:So, I wondered that there is no need to clear pgmoved before 1156 line.
You are right, of course.
Looks like that code got mangled a little during one of the forward merges :)
Signature · 1 line
All rights reversed.
Re: [PATCH -mm 07/16] second chance replacement for anonymous pages
In reply to Rik van RielReview static unsigned long shrink_list(enum lr · line 1151
Quoted 13 lines
(quoted diff) +5
-1@@ -1129,7 +1141,11 @@ static unsigned long shrink_list(enum lr { int file = is_file_lru(lru); - if (lru == LRU_ACTIVE_ANON || lru == LRU_ACTIVE_FILE) { + if (lru == LRU_ACTIVE_FILE) { + shrink_active_list(nr_to_scan, zone, sc, priority, file); + return 0; + } + if (lru == LRU_ACTIVE_ANON && inactive_anon_low(zone)) { shrink_active_list(nr_to_scan, zone, sc, priority, file); return 0; }
I made memcgroup noreclaim infrastructure today.
and, I found slightly odd behaviour.
this condition increase OOM, because prevent active -> inactive moving even though non global reclaim.
When memcgroup is used, OOM killer is happend very freqently. because inactive_anon_low() prevent move active lru to inactive lru.
--- mm/vmscan.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
mm/vmscan.c +3 -1
@@ -1145,7 +1145,9 @@ static unsigned long shrink_list(enum lrshrink_active_list (nr_to_scan, zone, sc, priority, file);return 0 ; } -if (lru ==LRU_ACTIVE_ANON &&inactive_anon_low (zone)) { + +if (lru ==LRU_ACTIVE_ANON && + (!scan_global_lru (sc) ||inactive_anon_low (zone))) {shrink_active_list (nr_to_scan, zone, sc, priority, file);return 0 ; }
Re: [PATCH -mm 07/16] second chance replacement for anonymous pages
Quoted 17 lines · On Wed, 28 May 2008 20:03:11 +0900 KOSAKI Motohiro…
Review static unsigned long shrink_list(enum lr · line 1151
Quoted 13 lines
(quoted diff) +5
-1@@ -1129,7 +1141,11 @@ static unsigned long shrink_list(enum lr { int file = is_file_lru(lru); - if (lru == LRU_ACTIVE_ANON || lru == LRU_ACTIVE_FILE) { + if (lru == LRU_ACTIVE_FILE) { + shrink_active_list(nr_to_scan, zone, sc, priority, file); + return 0; + } + if (lru == LRU_ACTIVE_ANON && inactive_anon_low(zone)) { shrink_active_list(nr_to_scan, zone, sc, priority, file); return 0; }I made memcgroup noreclaim infrastructure today.
and, I found slightly odd behaviour.
Quoted 2 lines
this condition increase OOM, because prevent active -> inactive moving even though non global reclaim.
Good catch, I have applied your patch to my tree.
Signature · 1 line
All rights reversed.
PATCH 8/16 add some sanity checks to get_scan_ratio
In reply to Rik van RielFrom: Rik van Riel <[email protected]>
The access ratio based scan rate determination in get_scan_ratio works ok in most situations, but needs to be corrected in some corner cases:
- if we run out of swap space, do not bother scanning the anon LRUs
- if we have already freed all of the page cache, we need to scan the anon LRUs
- restore the *actual* access ratio based scan rate algorithm, the previous versions of this patch series had the wrong version
- scale the number of pages added to zone->nr_scan[l]
--- include/linux/mmzone.h | 2 mm/page_alloc.c | 3 - mm/swap.c | 13 +++++- mm/vmscan.c | 104 ++++++++++++++++++++++++++++++++----------------- 4 files changed, 85 insertions(+), 37 deletions(-)
mm/vmscan.c +70 -34
@@ -893,8 +893,13 @@ static unsigned long shrink_inactive_lis__mod_zone_page_state (zone,NR_INACTIVE_ANON , -count[LRU_INACTIVE_ANON ]); -if (scan_global_lru (sc)) +if (scan_global_lru (sc)) { zone->pages_scanned += nr_scan; + zone->recent_scanned_anon += count[LRU_ACTIVE_ANON ] + + count[LRU_INACTIVE_ANON ]; + zone->recent_scanned_file += count[LRU_ACTIVE_FILE ] + + count[LRU_INACTIVE_FILE ]; + }spin_unlock_irq (&zone->lru_lock); nr_scanned += nr_scan;
@@ -944,11 +949,13 @@ static unsigned long shrink_inactive_lisVM_BUG_ON (PageLRU (page));SetPageLRU (page);list_del (&page->lru); -if (page_file_cache (page)) { +if (page_file_cache (page)) lru +=LRU_FILE ; - zone->recent_rotated_file++; - }else { - zone->recent_rotated_anon++; +if (scan_global_lru (sc)) { +if (page_file_cache (page)) + zone->recent_rotated_file++; +else + zone->recent_rotated_anon++; }if (PageActive (page)) lru +=LRU_ACTIVE ;
@@ -1027,8 +1034,13 @@ static void shrink_active_list(unsigned* zone->pages_scanned is used for detect zone's oom * mem_cgroup remembers nr_scan by itself. */ -if (scan_global_lru (sc)) +if (scan_global_lru (sc)) { zone->pages_scanned += pgscanned; +if (file) + zone->recent_scanned_file += pgscanned; +else + zone->recent_scanned_anon += pgscanned; + }if (file)__mod_zone_page_state (zone,NR_ACTIVE_FILE , -pgmoved);
@@ -1165,9 +1177,8 @@ static unsigned long shrink_list(enum lrstatic void get_scan_ratio (struct zone *zone,struct scan_control * sc,unsigned long *percent) { -unsigned long anon, file; +unsigned long anon, file, free;unsigned long anon_prio, file_prio; -unsigned long rotate_sum;unsigned long ap, fp; anon = zone_page_state(zone, NR_ACTIVE_ANON) +
@@ -1175,15 +1186,19 @@ static void get_scan_ratio(struct zone * file =zone_page_state (zone,NR_ACTIVE_FILE ) +zone_page_state (zone,NR_INACTIVE_FILE ); - rotate_sum = zone->recent_rotated_file + zone->recent_rotated_anon; -/* Keep a floating average of RECENT references. */ -if (unlikely (rotate_sum >min (anon, file))) { +if (unlikely (zone->recent_scanned_anon > anon / zone->inactive_ratio)) {spin_lock_irq (&zone->lru_lock); - zone->recent_rotated_file /=2 ; + zone->recent_scanned_anon /=2 ; zone->recent_rotated_anon /=2 ;spin_unlock_irq (&zone->lru_lock); - rotate_sum /=2 ; + } + +if (unlikely (zone->recent_scanned_file > file /4 )) { +spin_lock_irq (&zone->lru_lock); + zone->recent_scanned_file /=2 ; + zone->recent_rotated_file /=2 ; +spin_unlock_irq (&zone->lru_lock); }/*
@@ -1196,23 +1211,33 @@ static void get_scan_ratio(struct zone */* * anon recent_rotated_anon * %anon = 100 * ----------- / ------------------- * IO cost -* anon + file rotate_sum +* anon + file recent_scanned_anon */ - ap = (anon_prio * anon) / (anon + file +1 ); - ap *= rotate_sum / (zone->recent_rotated_anon +1 ); -if (ap ==0 ) - ap =1 ; -else if (ap >100 ) - ap =100 ; - percent[0 ] = ap; - - fp = (file_prio * file) / (anon + file +1 ); - fp *= rotate_sum / (zone->recent_rotated_file +1 ); -if (fp ==0 ) - fp =1 ; -else if (fp >100 ) - fp =100 ; - percent[1 ] = fp; + ap = (anon_prio +1 ) * (zone->recent_scanned_anon +1 ); + ap /= zone->recent_rotated_anon +1 ; + + fp = (file_prio +1 ) * (zone->recent_scanned_file +1 ); + fp /= zone->recent_rotated_file +1 ; + +/* Normalize to percentages */ + percent[0 ] =100 * ap / (ap + fp +1 ); + percent[1 ] =100 - percent[0 ]; + + free =zone_page_state (zone,NR_FREE_PAGES ); + +/* +* If we have no swap space, do not bother scanning anon pages. +*/ +if (nr_swap_pages <=0 ) { + percent[0 ] =0 ; + percent[1 ] =100 ; + } +/* +* If we already freed most file pages, scan the anon pages +* regardless of the page access ratios or swappiness setting. +*/ +else if (file + free <= zone->pages_high) + percent[0 ] =100 ; }
@@ -1233,13 +1258,17 @@ static unsigned long shrink_zone(int prifor_each_lru (l) {if (scan_global_lru (sc)) {int file =is_file_lru (l); +int scan;/* * Add one to nr_to_scan just to make sure that the -* kernel will slowly sift through the active list. +* kernel will slowly sift through each list. */ - zone->nr_scan[l] += (zone_page_state (zone, -NR_INACTIVE_ANON + l) >> priority) +1 ; - nr[l] = zone->nr_scan[l] * percent[file] /100 ; + scan =zone_page_state (zone,NR_INACTIVE_ANON + l); + scan >>= priority; + scan = (scan * percent[file]) /100 ; + + zone->nr_scan[l] += scan +1 ; + nr[l] = zone->nr_scan[l];if (nr[l] >= sc->swap_cluster_max) zone->nr_scan[l] =0 ;else
@@ -1256,7 +1285,7 @@ static unsigned long shrink_zone(int pri }while (nr[LRU_INACTIVE_ANON ] || nr[LRU_ACTIVE_FILE ] || - nr[LRU_INACTIVE_FILE ]) { + nr[LRU_INACTIVE_FILE ]) {for_each_lru (l) {if (nr[l]) { nr_to_scan = min(nr[l],
@@ -1269,6 +1298,13 @@ static unsigned long shrink_zone(int pri } } +/* +* Even if we did not try to evict anon pages at all, we want to +* rebalance the anon lru active/inactive ratio. +*/ +if (scan_global_lru (sc) &&inactive_anon_low (zone)) +shrink_active_list (SWAP_CLUSTER_MAX , zone, sc, priority,0 ); +throttle_vm_writeout (sc->gfp_mask);return nr_reclaimed; }
include/linux/mmzone.h +2 -0
@@ -289,6 +289,8 @@ struct zone {unsigned long recent_rotated_anon;unsigned long recent_rotated_file; +unsigned long recent_scanned_anon; +unsigned long recent_scanned_file;unsigned long pages_scanned;/* since last reclaim */ unsigned long flags;/* zone flags, see below */
mm/page_alloc.c +2 -1
@@ -3512,7 +3512,8 @@ static void __paginginit free_area_init_ } zone->recent_rotated_anon =0 ; zone->recent_rotated_file =0 ; -//TODO recent_scanned_* ??? + zone->recent_scanned_anon =0 ; + zone->recent_scanned_file =0 ;zap_zone_vm_stats (zone); zone->flags =0 ;if (!size)
mm/swap.c +11 -2
@@ -176,8 +176,8 @@ void activate_page(struct page *page)spin_lock_irq (&zone->lru_lock);if (PageLRU (page) && !PageActive (page)) { -int lru =LRU_BASE ; - lru +=page_file_cache (page); +int file =page_file_cache (page); +int lru =LRU_BASE + file;del_page_from_lru_list (zone, page, lru);SetPageActive (page);
@@ -185,6 +185,15 @@ void activate_page(struct page *page)add_page_to_lru_list (zone, page, lru);__count_vm_event (PGACTIVATE );mem_cgroup_move_lists (page, true); + +if (file) { + zone->recent_scanned_file++; + zone->recent_rotated_file++; + }else { +/* Can this happen? Maybe through tmpfs... */ + zone->recent_scanned_anon++; + zone->recent_rotated_anon++; + } }spin_unlock_irq (&zone->lru_lock); }
Signature · 1 line
All Rights Reversed
PATCH 9/16 fix pagecache reclaim referenced bit check
In reply to Rik van RielFrom: Rik van Riel <[email protected]>
The -mm tree contains the patch vmscan-give-referenced-active-and-unmapped-pages-a-second-trip-around-the-lru.patch which gives referenced pagecache pages another trip around
the active list. This seems to help keep frequently accessed pagecache pages in memory.
However, it means that pagecache pages that get moved to the inactive list do not have their referenced bit set, and a reference to the page will not get it moved back to the active list. This patch sets the referenced bit on pagecache pages that get deactivated, so the next access to the page will promote it back to the active list.
--- mm/vmscan.c | 4 ++++ 1 file changed, 4 insertions(+)
mm/vmscan.c +4 -0
@@ -1060,6 +1060,10 @@ static void shrink_active_list(unsignedif (!file)/* Anonymous pages always get deactivated. */ pgmoved++; +else if (!page_mapped (page)) +/* Bypass use-once, make the next access count. +* See mark_page_accessed. */ +SetPageReferenced (page); } }
Signature · 1 line
All Rights Reversed
PATCH 10/16 add newly swapped in pages to the inactive list
In reply to Rik van RielFrom: Rik van Riel <[email protected]>
Swapin_readahead can read in a lot of data that the processes in memory never need. Adding swap cache pages to the inactive list prevents them from putting too much pressure on the working set.
This has the potential to help the programs that are already in memory, but it could also be a disadvantage to processes that are trying to get swapped in.
In short, this patch needs testing.
--- mm/swap_state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
mm/swap_state.c +1 -1
@@ -302,7 +302,7 @@ struct page *read_swap_cache_async(swp_e/* * Initiate read into locked page and return. */ -lru_cache_add_active_anon (new_page); +lru_cache_add_anon (new_page);swap_readpage (NULL , new_page);return new_page; }
Signature · 1 line
All Rights Reversed
PATCH 11/16 more aggressively use lumpy reclaim
In reply to Rik van RielFrom: Rik van Riel <[email protected]>
During an AIM7 run on a 16GB system, fork started failing around 32000 threads, despite the system having plenty of free swap and 15GB of pageable memory.
If normal pageout does not result in contiguous free pages for kernel stacks, fall back to lumpy reclaim instead of failing fork or doing excessive pageout IO.
I do not know whether this change is needed due to the extreme stress test or because the inactive list is a smaller fraction of system memory on huge systems.
--- mm/vmscan.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-)
mm/vmscan.c +16 -4
@@ -857,7 +857,8 @@ int isolate_lru_page(struct page *page)* of reclaimed pages */ static unsigned long shrink_inactive_list (unsigned long max_scan, -struct zone *zone,struct scan_control *sc,int file) +struct zone *zone,struct scan_control *sc, +int priority,int file) {LIST_HEAD (page_list);struct pagevec pvec;
@@ -875,8 +876,19 @@ static unsigned long shrink_inactive_lisunsigned long nr_freed;unsigned long nr_active;unsigned int count[NR_LRU_LISTS ] = {0 , }; -int mode = (sc->order >PAGE_ALLOC_COSTLY_ORDER ) ? -ISOLATE_BOTH :ISOLATE_INACTIVE ; +int mode =ISOLATE_INACTIVE ; + +/* +* If we need a large contiguous chunk of memory, or have +* trouble getting a small set of contiguous pages, we +* will reclaim both active and inactive pages. +* +* We use the same threshold as pageout congestion_wait below. +*/ +if (sc->order >PAGE_ALLOC_COSTLY_ORDER ) + mode =ISOLATE_BOTH ; +else if (sc->order && priority <DEF_PRIORITY -2 ) + mode =ISOLATE_BOTH ; nr_taken = sc->isolate_pages(sc->swap_cluster_max, &page_list, &nr_scan, sc->order, mode,
@@ -1165,7 +1177,7 @@ static unsigned long shrink_list(enum lrshrink_active_list (nr_to_scan, zone, sc, priority, file);return 0 ; } -return shrink_inactive_list (nr_to_scan, zone, sc, file); +return shrink_inactive_list (nr_to_scan, zone, sc, priority, file); }/*
Signature · 1 line
All Rights Reversed
PATCH 12/16 pageflag helpers for configed-out flags
In reply to Rik van RielFrom: Lee Schermerhorn <[email protected]>
Define proper false/noop inline functions for noreclaim page flags when !defined(CONFIG_NORECLAIM_LRU)
--- include/linux/page-flags.h | 12 ++++++++++++ 1 file changed, 12 insertions(+)
include/linux/page-flags.h +12 -0
@@ -147,6 +147,18 @@ static inline int Page##uname(struct pag#define TESTSCFLAG (uname, lname) \ TESTSETFLAG(uname, lname) TESTCLEARFLAG(uname, lname) +#define SETPAGEFLAG_NOOP (uname) \ +static inline void SetPage##uname(struct page *page) { } + +#define CLEARPAGEFLAG_NOOP (uname) \ +static inline void ClearPage##uname(struct page *page) { } + +#define __CLEARPAGEFLAG_NOOP (uname) \ +static inline void __ClearPage##uname(struct page *page) { } + +#define TESTCLEARFLAG_FALSE (uname) \ +static inline int TestClearPage##uname(struct page *page) { return 0; } +struct page ;/* forward declaration */ PAGEFLAG(Locked, locked) TESTSCFLAG(Locked, locked)
Signature · 1 line
All Rights Reversed
PATCH 13/16 No Reclaim LRU Infrastructure
In reply to Rik van RielFrom: Lee Schermerhorn <[email protected]>
Infrastructure to manage pages excluded from reclaim--i.e., hidden from vmscan. Based on a patch by Larry Woodman of Red Hat. Reworked to maintain "nonreclaimable" pages on a separate per-zone LRU list, to "hide" them from vmscan. A separate noreclaim pagevec is provided for shrink_active_list() to move nonreclaimable pages to the noreclaim list without over burdening the zone lru_lock.
Pages on the noreclaim list have both PG_noreclaim and PG_lru set. Thus, PG_noreclaim is analogous to and mutually exclusive with PG_active--it specifies which LRU list the page is on.
The noreclaim infrastructure is enabled by a new mm Kconfig option [CONFIG_]NORECLAIM_LRU.
A new function 'page_reclaimable(page, vma)' in vmscan.c tests whether or not a page is reclaimable. Subsequent patches will add the various !reclaimable tests. We'll want to keep these tests light-weight for use in shrink_active_list() and, possibly, the fault path.
---
V3 -> V6:
+ remove lru_cache_add_active_or_noreclaim(). Only used by optional patch to cull nonreclaimable pages in fault path. Will add back to that patch.
+ misc cleanup pointed out by review of V5
V1 -> V3:
+ rebase to 23-mm1 atop RvR's split LRU series
+ define NR_NORECLAIM and LRU_NORECLAIM to avoid errors when not configured.
V1 -> V2:
+ handle review comments -- various typos and errors.
+ extract "putback_all_noreclaim_pages()" into a separate patch and rework as "scan_all_zones_noreclaim_pages().
include/linux/mm_inline.h | 13 ++- include/linux/mmzone.h | 24 ++++++ include/linux/page-flags.h | 13 +++ include/linux/pagevec.h | 1 include/linux/swap.h | 12 +++ mm/Kconfig | 10 ++ mm/internal.h | 26 +++++++ mm/mempolicy.c | 2 mm/migrate.c | 68 ++++++++++++------- mm/page_alloc.c | 9 ++ mm/swap.c | 50 ++++++++++++-- mm/vmscan.c | 156 ++++++++++++++++++++++++++++++++++++++++----- 12 files changed, 329 insertions(+), 55 deletions(-)
mm/Kconfig +10 -0
@@ -205,3 +205,13 @@ config NR_QUICK config VIRT_TO_BUS def_bool y depends on !ARCH_NO_VIRT_TO_BUS + +config NORECLAIM_LRU + bool "Add LRU list to track non-reclaimable pages (EXPERIMENTAL, 64BIT only)" + depends on EXPERIMENTAL && 64BIT + help + Supports tracking of non-reclaimable pages off the [in]active lists + to avoid excessive reclaim overhead on large memory systems. Pages + may be non-reclaimable because: they are locked into memory, they + are anonymous pages for which no swap space exists, or they are anon + pages that are expensive to unmap [long anon_vma "related vma" list.]
include/linux/page-flags.h +13 -0
@@ -94,6 +94,9 @@ enum pageflags { PG_reclaim,/* To be reclaimed asap */ PG_buddy,/* Page is free, on buddy lists */ PG_swapbacked,/* Page is backed by RAM/swap */ +#ifdef CONFIG_NORECLAIM_LRU + PG_noreclaim,/* Page is "non-reclaimable" */ +#endif #ifdef CONFIG_IA64_UNCACHED_ALLOCATOR PG_uncached, /* Page has been mapped as uncached */ #endif
@@ -167,6 +170,7 @@ PAGEFLAG(Referenced, referenced) TESTCLE PAGEFLAG(Dirty, dirty) TESTSCFLAG(Dirty, dirty) __CLEARPAGEFLAG(Dirty, dirty) PAGEFLAG(LRU, lru) __CLEARPAGEFLAG(LRU, lru) PAGEFLAG(Active, active) __CLEARPAGEFLAG(Active, active) + TESTCLEARFLAG(Active, active) __PAGEFLAG(Slab, slab) PAGEFLAG(Checked, owner_priv_1) /* Used by some filesystems */ PAGEFLAG(Pinned, owner_priv_1) TESTSCFLAG(Pinned, owner_priv_1) /* Xen */
@@ -203,6 +207,15 @@ PAGEFLAG(SwapCache, swapcache) PAGEFLAG_FALSE(SwapCache)#endif +#ifdef CONFIG_NORECLAIM_LRU +PAGEFLAG(Noreclaim, noreclaim) __CLEARPAGEFLAG(Noreclaim, noreclaim) + TESTCLEARFLAG(Noreclaim, noreclaim) +#else +PAGEFLAG_FALSE(Noreclaim) TESTCLEARFLAG_FALSE(Noreclaim) + SETPAGEFLAG_NOOP(Noreclaim) CLEARPAGEFLAG_NOOP(Noreclaim) + __CLEARPAGEFLAG_NOOP(Noreclaim) +#endif + #ifdef CONFIG_IA64_UNCACHED_ALLOCATOR PAGEFLAG(Uncached, uncached)#else
include/linux/mmzone.h +23 -1
@@ -85,6 +85,11 @@ enum zone_stat_item {NR_ACTIVE_ANON ,/* " " " " " */ NR_INACTIVE_FILE ,/* " " " " " */ NR_ACTIVE_FILE ,/* " " " " " */ +#ifdef CONFIG_NORECLAIM_LRU +NR_NORECLAIM ,/* " " " " " */ +#else +NR_NORECLAIM =NR_ACTIVE_FILE ,/* avoid compiler errors in dead code */ +#endifNR_ANON_PAGES ,/* Mapped anonymous pages */ NR_FILE_MAPPED ,/* pagecache pages mapped into pagetables. only modified from process context */
@@ -124,10 +129,18 @@ enum lru_list {LRU_ACTIVE_ANON =LRU_BASE +LRU_ACTIVE ,LRU_INACTIVE_FILE =LRU_BASE +LRU_FILE ,LRU_ACTIVE_FILE =LRU_BASE +LRU_FILE +LRU_ACTIVE , - NR_LRU_LISTS }; +#ifdef CONFIG_NORECLAIM_LRU +LRU_NORECLAIM , +#else +LRU_NORECLAIM =LRU_ACTIVE_FILE ,/* avoid compiler errors in dead code */ +#endif + NR_LRU_LISTS +};#define for_each_lru (l) for (l = 0; l < NR_LRU_LISTS; l++) +#define for_each_reclaimable_lru (l) for (l = 0; l <= LRU_ACTIVE_FILE; l++) +static inline int is_file_lru (enum lru_list l) {return (l ==LRU_INACTIVE_FILE || l ==LRU_ACTIVE_FILE );
@@ -138,6 +151,15 @@ static inline int is_active_lru(enum lrureturn (l ==LRU_ACTIVE_ANON || l ==LRU_ACTIVE_FILE ); } +static inline int is_noreclaim_lru (enum lru_list l) +{ +#ifdef CONFIG_NORECLAIM_LRU +return (l ==LRU_NORECLAIM ); +#else +return 0 ; +#endif +} +enum lru_list page_lru (struct page *page);struct per_cpu_pages {
mm/page_alloc.c +9 -0
@@ -256,6 +256,9 @@ static void bad_page(struct page *page) 1 << PG_private | 1 << PG_locked | 1 << PG_active | +#ifdef CONFIG_NORECLAIM_LRU + 1 << PG_noreclaim | +#endif 1 << PG_dirty | 1 << PG_reclaim | 1 << PG_slab |
@@ -491,6 +494,9 @@ static inline int free_pages_check(struc 1 << PG_swapcache | 1 << PG_writeback | 1 << PG_reserved | +#ifdef CONFIG_NORECLAIM_LRU + 1 << PG_noreclaim | +#endif 1 << PG_buddy ))))bad_page (page);if (PageDirty (page))
@@ -642,6 +648,9 @@ static int prep_new_page(struct page *pa 1 << PG_private | 1 << PG_locked | 1 << PG_active | +#ifdef CONFIG_NORECLAIM_LRU + 1 << PG_noreclaim | +#endif 1 << PG_dirty | 1 << PG_slab | 1 << PG_swapcache |
include/linux/mm_inline.h +9 -4
@@ -89,11 +89,16 @@ del_page_from_lru(struct zone *zone, strenum lru_list l =LRU_INACTIVE_ANON ;list_del (&page->lru); -if (PageActive (page)) { -__ClearPageActive (page); - l +=LRU_ACTIVE ; +if (PageNoreclaim (page)) { +__ClearPageNoreclaim (page); + l =LRU_NORECLAIM ; + }else { +if (PageActive (page)) { +__ClearPageActive (page); + l +=LRU_ACTIVE ; + } + l +=page_file_cache (page); } - l +=page_file_cache (page);__dec_zone_state (zone,NR_INACTIVE_ANON + l); }
include/linux/swap.h +12 -0
@@ -180,6 +180,8 @@ extern int lru_add_drain_all(void);extern void rotate_reclaimable_page (struct page *page);extern void swap_setup (void ); +extern void add_page_to_noreclaim_list (struct page *page); +/** * lru_cache_add: add a page to the page lists * @page: the page to add
@@ -228,6 +230,16 @@ static inline int zone_reclaim(struct zo }#endif +#ifdef CONFIG_NORECLAIM_LRU +extern int page_reclaimable (struct page *page,struct vm_area_struct *vma); +#else +static inline int page_reclaimable (struct page *page, +struct vm_area_struct *vma) +{ +return 1 ; +} +#endif +extern int kswapd_run (int nid);#ifdef CONFIG_MMU
include/linux/pagevec.h +0 -1
@@ -101,7 +101,6 @@ static inline void __pagevec_lru_add_act____pagevec_lru_add (pvec,LRU_ACTIVE_FILE ); } -static inline void pagevec_lru_add_file (struct pagevec *pvec) {if (pagevec_count (pvec))
mm/swap.c +42 -8
110 lines not on this page · raw message
mm/migrate.c +45 -23
151 lines not on this page · raw message
mm/vmscan.c +139 -17
241 lines not on this page · raw message
mm/mempolicy.c +1 -1
@@ -2199,7 +2199,7 @@ static void gather_stats(struct page *paif (PageSwapCache (page)) md->swapcache++; -if (PageActive (page)) +if (PageActive (page) ||PageNoreclaim (page)) md->active++;if (PageWriteback (page))
mm/internal.h +26 -0
@@ -34,8 +34,15 @@ static inline void __put_page(struct pagatomic_dec (&page->_count); } +/* +* in mm/vmscan.c: +*/ extern int isolate_lru_page (struct page *page); +extern int putback_lru_page (struct page *page); +/* +* in mm/page_alloc.c +*/ extern void __free_pages_bootmem (struct page *page,unsigned int order);/*
@@ -49,6 +56,25 @@ static inline unsigned long page_order(sreturn page_private (page); } +#ifdef CONFIG_NORECLAIM_LRU +/* +* noreclaim_migrate_page() called only from migrate_page_copy() to +* migrate noreclaim flag to new page. +* Note that the old page has been isolated from the LRU lists at this +* point so we don't need to worry about LRU statistics. +*/ +static inline void noreclaim_migrate_page (struct page *new,struct page *old) +{ +if (TestClearPageNoreclaim (old)) +SetPageNoreclaim (new); +} +#else +static inline void noreclaim_migrate_page (struct page *new,struct page *old) +{ +} +#endif + +/* * FLATMEM and DISCONTIGMEM configurations use alloc_bootmem_node, * so all functions starting at paging_init should be marked __init
Signature · 1 line
All Rights Reversed
PATCH 14/16 Non-reclaimable page statistics
In reply to Rik van RielFrom: Lee Schermerhorn <[email protected]>
Report non-reclaimable pages per zone and system wide.
Note: may want to track/report some specific reasons for nonreclaimability for deciding when to splice the noreclaim lists back to the normal lru. That will be tricky, especially in shrink_active_list(), where we'd need someplace to save the per page reason for non-reclaimability until the pages are dumped back onto the noreclaim list from the pagevec.
Note: my tests indicate that NR_NORECLAIM and probably the other LRU stats aren't being maintained properly--especially with large amounts of mlocked memory and the mlock patch in this series installed. Can't be sure of this, as I don't know why the pages are on the noreclaim list. Needs further investigation.
---
V2 -> V3:
+ rebase to 23-mm1 atop RvR's split LRU series
V1 -> V2:
no changes
drivers/base/node.c | 6 ++++++ fs/proc/proc_misc.c | 6 ++++++ mm/page_alloc.c | 16 +++++++++++++++- mm/vmstat.c | 3 +++ 4 files changed, 30 insertions(+), 1 deletion(-)
mm/page_alloc.c +15 -1
@@ -1918,12 +1918,20 @@ void show_free_areas(void) } printk("Active_anon:%lu active_file:%lu inactive_anon%lu\n" - " inactive_file:%lu dirty:%lu writeback:%lu unstable:%lu\n" + " inactive_file:%lu" +//TODO: check/adjust line lengths +#ifdef CONFIG_NORECLAIM_LRU +" noreclaim:%lu" +#endif +" dirty:%lu writeback:%lu unstable:%lu\n" " free:%lu slab:%lu mapped:%lu pagetables:%lu bounce:%lu\n" ,global_page_state (NR_ACTIVE_ANON ),global_page_state (NR_ACTIVE_FILE ),global_page_state (NR_INACTIVE_ANON ),global_page_state (NR_INACTIVE_FILE ), +#ifdef CONFIG_NORECLAIM_LRU +global_page_state (NR_NORECLAIM ), +#endifglobal_page_state (NR_FILE_DIRTY ),global_page_state (NR_WRITEBACK ), global_page_state(NR_UNSTABLE_NFS),
@@ -1950,6 +1958,9 @@ void show_free_areas(void) " inactive_anon:%lukB" " active_file:%lukB" " inactive_file:%lukB" +#ifdef CONFIG_NORECLAIM_LRU + " noreclaim:%lukB" +#endif" present:%lukB" " pages_scanned:%lu" " all_unreclaimable? %s"
@@ -1963,6 +1974,9 @@ void show_free_areas(void)K (zone_page_state (zone,NR_INACTIVE_ANON )),K (zone_page_state (zone,NR_ACTIVE_FILE )),K (zone_page_state (zone,NR_INACTIVE_FILE )), +#ifdef CONFIG_NORECLAIM_LRU +K (zone_page_state (zone,NR_NORECLAIM )), +#endifK (zone->present_pages), zone->pages_scanned, (zone_is_all_unreclaimable (zone) ?"yes" :"no" )
mm/vmstat.c +3 -0
@@ -699,6 +699,9 @@ static const char * const vmstat_text[]"nr_active_anon" ,"nr_inactive_file" ,"nr_active_file" , +#ifdef CONFIG_NORECLAIM_LRU +"nr_noreclaim" , +#endif"nr_anon_pages" ,"nr_mapped" , "nr_file_pages",
drivers/base/node.c +6 -0
@@ -67,6 +67,9 @@ static ssize_t node_read_meminfo(struct "Node %d Inactive(anon): %8lu kB\n" "Node %d Active(file): %8lu kB\n" "Node %d Inactive(file): %8lu kB\n" +#ifdef CONFIG_NORECLAIM_LRU + "Node %d Noreclaim: %8lu kB\n" +#endif#ifdef CONFIG_HIGHMEM "Node %d HighTotal: %8lu kB\n" "Node %d HighFree: %8lu kB\n"
@@ -96,6 +99,9 @@ static ssize_t node_read_meminfo(struct nid,node_page_state (nid,NR_INACTIVE_ANON ), nid,node_page_state (nid,NR_ACTIVE_FILE ), nid,node_page_state (nid,NR_INACTIVE_FILE ), +#ifdef CONFIG_NORECLAIM_LRU + nid,node_page_state (nid,NR_NORECLAIM ), +#endif #ifdef CONFIG_HIGHMEM nid,K (i.totalhigh), nid, K(i.freehigh),
fs/proc/proc_misc.c +6 -0
@@ -174,6 +174,9 @@ static int meminfo_read_proc(char *page, "Inactive(anon): %8lu kB\n" "Active(file): %8lu kB\n" "Inactive(file): %8lu kB\n" +#ifdef CONFIG_NORECLAIM_LRU + "Noreclaim: %8lu kB\n" +#endif#ifdef CONFIG_HIGHMEM "HighTotal: %8lu kB\n" "HighFree: %8lu kB\n"
@@ -209,6 +212,9 @@ static int meminfo_read_proc(char *page,K (inactive_anon),K (active_file),K (inactive_file), +#ifdef CONFIG_NORECLAIM_LRU +K (global_page_state (NR_NORECLAIM )), +#endif #ifdef CONFIG_HIGHMEMK (i.totalhigh), K(i.freehigh),
Signature · 1 line
All Rights Reversed
PATCH 15/16 ramfs pages are non-reclaimable
In reply to Rik van RielFrom: Lee Schermerhorn <[email protected]>
Christoph Lameter pointed out that ram disk pages also clutter the LRU lists. When vmscan finds them dirty and tries to clean them, the ram disk writeback function just redirties the page so that it goes back onto the active list. Round and round she goes...
Define new address_space flag [shares address_space flags member with mapping's gfp mask] to indicate that the address space contains all non-reclaimable pages. This will provide for efficient testing of ramdisk pages in page_reclaimable().
Also provide wrapper functions to set/test the noreclaim state to minimize #ifdefs in ramdisk driver and any other users of this facility.
Set the noreclaim state on address_space structures for new ramdisk inodes. Test the noreclaim state in page_reclaimable() to cull non-reclaimable pages.
Similarly, ramfs pages are non-reclaimable. Set the 'noreclaim' address_space flag for new ramfs inodes.
These changes depend on [CONFIG_]NORECLAIM_LRU.
---
V3 -> V4:
+ drivers/block/rd.c was replaced by brd.c in 24-rc4-mm1. Update patch to add brd_open() to mark mapping as nonreclaimable
V2 -> V3:
+ rebase to 23-mm1 atop RvR's split LRU series [no changes]
V1 -> V2:
+ add ramfs pages to this class of non-reclaimable pages by marking ramfs address_space [mapping] as non-reclaimble.
drivers/block/brd.c | 13 +++++++++++++ fs/ramfs/inode.c | 1 + include/linux/pagemap.h | 22 ++++++++++++++++++++++ mm/vmscan.c | 5 +++++ 4 files changed, 41 insertions(+)
include/linux/pagemap.h +22 -0
@@ -30,6 +30,28 @@ static inline void mapping_set_error(str } } +#ifdef CONFIG_NORECLAIM_LRU +#define AS_NORECLAIM (__GFP_BITS_SHIFT + 2)/* e.g., ramdisk, SHM_LOCK */ + +static inline void mapping_set_noreclaim (struct address_space *mapping) +{ +set_bit (AS_NORECLAIM , &mapping->flags); +} + +static inline int mapping_non_reclaimable (struct address_space *mapping) +{ +if (mapping && (mapping->flags &AS_NORECLAIM )) +return 1 ; +return 0 ; +} +#else +static inline void mapping_set_noreclaim (struct address_space *mapping) { } +static inline int mapping_non_reclaimable (struct address_space *mapping) +{ +return 0 ; +} +#endif +static inline gfp_t mapping_gfp_mask (struct address_space * mapping) {return (__force gfp_t )mapping->flags & __GFP_BITS_MASK;
mm/vmscan.c +5 -0
@@ -2301,6 +2301,8 @@ int zone_reclaim(struct zone *zone, gfp_* lists vs noreclaim list. * * Reasons page might not be reclaimable: +* (1) page's mapping marked non-reclaimable +* * TODO - later patches */ int page_reclaimable (struct page *page,struct vm_area_struct *vma)
@@ -2308,6 +2310,9 @@ int page_reclaimable(struct page *page,VM_BUG_ON (PageNoreclaim (page)); +if (mapping_non_reclaimable (page_mapping (page))) +return 0 ; +/* TODO: test page [!]reclaimable conditions */ return 1 ;
fs/ramfs/inode.c +1 -0
@@ -61,6 +61,7 @@ struct inode *ramfs_get_inode(struct sup inode->i_mapping->a_ops = &ramfs_aops; inode->i_mapping->backing_dev_info = &ramfs_backing_dev_info;mapping_set_gfp_mask (inode->i_mapping,GFP_HIGHUSER ); +mapping_set_noreclaim (inode->i_mapping); inode->i_atime = inode->i_mtime = inode->i_ctime =CURRENT_TIME ;switch (mode &S_IFMT ) {default :
drivers/block/brd.c +13 -0
@@ -374,8 +374,21 @@ static int brd_ioctl(struct inode *inodereturn error; } +/* +* brd_open(): +* Just mark the mapping as containing non-reclaimable pages +*/ +static int brd_open (struct inode *inode,struct file *filp) +{ +struct address_space *mapping = inode->i_mapping; + +mapping_set_noreclaim (mapping); +return 0 ; +} +static struct block_device_operations brd_fops = { .owner =THIS_MODULE , + .open = brd_open, .ioctl = brd_ioctl, #ifdef CONFIG_BLK_DEV_XIP .direct_access = brd_direct_access,
Signature · 1 line
All Rights Reversed
PATCH 16/16 SHM_LOCKED pages are nonreclaimable
In reply to Rik van RielFrom: Lee Schermerhorn <[email protected]>
While working with Nick Piggin's mlock patches, I noticed that shmem segments locked via shmctl(SHM_LOCKED) were not being handled. SHM_LOCKed pages work like ramdisk pages--the writeback function just redirties the page so that it can't be reclaimed. Deal with these using the same approach as for ram disk pages.
Use the AS_NORECLAIM flag to mark address_space of SHM_LOCKed shared memory regions as non-reclaimable. Then these pages will be culled off the normal LRU lists during vmscan.
Add new wrapper function to clear the mapping's noreclaim state when/if shared memory segment is munlocked.
Add 'scan_mapping_noreclaim_page()' to mm/vmscan.c to scan all pages in the shmem segment's mapping [struct address_space] for reclaimability now that they're no longer locked. If so, move them to the appropriate zone lru list.
Changes depend on [CONFIG_]NORECLAIM_LRU.
---
V2 -> V3:
+ rebase to 23-mm1 atop RvR's split LRU series.
+ Use scan_mapping_noreclaim_page() on unlock. See below.
V1 -> V2:
+ modify to use reworked 'scan_all_zones_noreclaim_pages()' See 'TODO' below - still pending.
include/linux/mm.h | 7 ++- include/linux/pagemap.h | 10 ++++- include/linux/swap.h | 4 ++ ipc/shm.c | 11 ++++- mm/shmem.c | 10 +++-- mm/vmscan.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 124 insertions(+), 10 deletions(-)
mm/shmem.c +7 -3
@@ -1458,23 +1458,27 @@ static struct mempolicy *shmem_get_polic }#endif -int shmem_lock (struct file *file,int lock,struct user_struct *user) +struct address_space *shmem_lock (struct file *file,int lock, +struct user_struct *user) {struct inode *inode = file->f_path.dentry->d_inode;struct shmem_inode_info *info =SHMEM_I (inode); -int retval = -ENOMEM ; +struct address_space *retval =ERR_PTR (-ENOMEM );spin_lock (&info->lock);if (lock && !(info->flags &VM_LOCKED )) {if (!user_shm_lock (inode->i_size, user)) goto out_nomem; info->flags |=VM_LOCKED ; +mapping_set_noreclaim (file->f_mapping); + retval =NULL ; }if (!lock && (info->flags &VM_LOCKED ) && user) {user_shm_unlock (inode->i_size, user); info->flags &= ~VM_LOCKED ; +mapping_clear_noreclaim (file->f_mapping); + retval = file->f_mapping; } - retval =0 ; out_nomem:spin_unlock (&info->lock);return retval;
include/linux/pagemap.h +8 -2
@@ -38,14 +38,20 @@ static inline void mapping_set_noreclaim set_bit(AS_NORECLAIM, &mapping->flags); } +static inline void mapping_clear_noreclaim (struct address_space *mapping) +{ +clear_bit (AS_NORECLAIM , &mapping->flags); +} +static inline int mapping_non_reclaimable (struct address_space *mapping) { -if (mapping && (mapping->flags &AS_NORECLAIM )) -return 1 ; +if (mapping) +return test_bit (AS_NORECLAIM , &mapping->flags);return 0 ; }#else static inline void mapping_set_noreclaim (struct address_space *mapping) { } +static inline void mapping_clear_noreclaim (struct address_space *mapping) { }static inline int mapping_non_reclaimable (struct address_space *mapping) {return 0 ;
mm/vmscan.c +92 -0
96 lines not on this page · raw message
include/linux/swap.h +4 -0
@@ -232,12 +232,16 @@ static inline int zone_reclaim(struct zo#ifdef CONFIG_NORECLAIM_LRU extern int page_reclaimable (struct page *page,struct vm_area_struct *vma); +extern void scan_mapping_noreclaim_pages (struct address_space *);#else static inline int page_reclaimable (struct page *page,struct vm_area_struct *vma) {return 1 ; } +static inline void scan_mapping_noreclaim_pages (struct address_space *mapping) +{ +}#endif extern int kswapd_run (int nid);
include/linux/mm.h +4 -3
@@ -694,10 +694,11 @@ static inline int page_mapped(struct pagextern void show_free_areas (void );#ifdef CONFIG_SHMEM -int shmem_lock (struct file *file,int lock,struct user_struct *user); +extern struct address_space *shmem_lock (struct file *file,int lock, +struct user_struct *user);#else -static inline int shmem_lock (struct file *file,int lock, -struct user_struct *user) +static inline struct address_space *shmem_lock (struct file *file,int lock, +struct user_struct *user) {return 0 ; }
ipc/shm.c +9 -2
@@ -736,6 +736,8 @@ asmlinkage long sys_shmctl(int shmid, incase SHM_LOCK :case SHM_UNLOCK : { +struct address_space *mapping =NULL ; + shp =shm_lock_check (ns, shmid);if (IS_ERR (shp)) { err =PTR_ERR (shp);
@@ -763,18 +765,23 @@ asmlinkage long sys_shmctl(int shmid, inif (cmd==SHM_LOCK ) {struct user_struct * user = current->user;if (!is_file_hugepages (shp->shm_file)) { - err =shmem_lock (shp->shm_file,1 , user); + mapping =shmem_lock (shp->shm_file,1 , user); +if (IS_ERR (mapping)) + err =PTR_ERR (mapping); + mapping =NULL ;if (!err && !(shp->shm_perm.mode &SHM_LOCKED )){ shp->shm_perm.mode |=SHM_LOCKED ; shp->mlock_user = user; } } }else if (!is_file_hugepages (shp->shm_file)) { -shmem_lock (shp->shm_file,0 , shp->mlock_user); + mapping =shmem_lock (shp->shm_file,0 , shp->mlock_user); shp->shm_perm.mode &= ~SHM_LOCKED ; shp->mlock_user =NULL ; }shm_unlock (shp); +if (mapping) +scan_mapping_noreclaim_pages (mapping); goto out; }case IPC_RMID :
Signature · 1 line
All Rights Reversed
Quoted 4 lines · Rik van Riel
On large memory systems, the VM can spend way too much time scanning through pages that it cannot (or should not) evict from memory. Not only does it use up CPU time, but it also provokes lock contention and can leave large systems under memory presure in a catatonic state.
Hi, Rik,
This patchset looks good (I did a brief scan). I'll go ahead and play with it? What is a good memory size to test the patches on (to see improvements).
Signature · 4 lines
Warm Regards, Balbir Singh Linux Technology Center IBM, ISTL
Quoted 10 lines · On Mon, 26 May 2008 23:54:55 +0530 Balbir Singh <[email protected]>…
Quoted 4 lines · Rik van Riel
On large memory systems, the VM can spend way too much time scanning through pages that it cannot (or should not) evict from memory. Not only does it use up CPU time, but it also provokes lock contention and can leave large systems under memory presure in a catatonic state.
Hi, Rik,
This patchset looks good (I did a brief scan). I'll go ahead and play with it? What is a good memory size to test the patches on (to see improvements).
The larger, the better. One known problem with the current upstream VM is large numbers of anonymous pages, or a mix of mlocked and anon pages.
Once the system needs to swap something out, every single anon page will have the referenced bit set and the system needs to do lots of scanning before it can evict the first page. This scanning causes multiple CPUs to pile up and things slow down exponentially and/or catastrophically :)
Unfortunately the largest system I have access to on a regular basis has "only" 16GB of RAM :(
I am also making 2.6.25 based kernel RPMs available with the split LRU patch set, at http://people.redhat.com/riel/splitvm/
The most recently posted patches are newer, though...
Signature · 1 line
All rights reversed.
Quoted 24 lines · Rik van Riel
Quoted 10 lines · On Mon, 26 May 2008 23:54:55 +0530 Balbir Singh <[email protected]>…
Quoted 4 lines · Rik van Riel
On large memory systems, the VM can spend way too much time scanning through pages that it cannot (or should not) evict from memory. Not only does it use up CPU time, but it also provokes lock contention and can leave large systems under memory presure in a catatonic state.
Hi, Rik,
This patchset looks good (I did a brief scan). I'll go ahead and play with it? What is a good memory size to test the patches on (to see improvements).
The larger, the better. One known problem with the current upstream VM is large numbers of anonymous pages, or a mix of mlocked and anon pages.
Once the system needs to swap something out, every single anon page will have the referenced bit set and the system needs to do lots of scanning before it can evict the first page. This scanning causes multiple CPUs to pile up and things slow down exponentially and/or catastrophically :)
Quoted 8 lines
Unfortunately the largest system I have access to on a regular basis has "only" 16GB of RAM :(
I am also making 2.6.25 based kernel RPMs available with the split LRU patch set, at http://people.redhat.com/riel/splitvm/
The most recently posted patches are newer, though...
I tested Rik's previous patch set with my noreclaim/mlock patches over the long weekend on 32GB systems--one ia64 [16cpu x 4 nodes] and one x86_64 [8 core x 4 nodes] on 26-rc2-mm1. A fairly heavy stress load ran for 92-93 hours on each system w/o error. Stats tracked throughout, no leaked pages, ...
Since Balbir is starting to look at this, I need to ask about interaction with the memory controller. It is currently unaware of the noreclaim list. I'm not sure what will happen if/when the memory controller tries to reclaim a page that system has moved to the noreclaim list. Something we'll need to address. It's on my list, but I won't get to it for a couple of weeks.
Lee
Quoted 38 lines · Lee Schermerhorn
Quoted 22 lines · Rik van Riel
Quoted 9 lines · On Mon, 26 May 2008 23:54:55 +0530 Balbir Singh <[email protected]>…
Quoted 4 lines · Rik van Riel
On large memory systems, the VM can spend way too much time scanning through pages that it cannot (or should not) evict from memory. Not only does it use up CPU time, but it also provokes lock contention and can leave large systems under memory presure in a catatonic state.
Hi, Rik,
This patchset looks good (I did a brief scan). I'll go ahead and play with it? What is a good memory size to test the patches on (to see improvements).
The larger, the better. One known problem with the current upstream VM is large numbers of anonymous pages, or a mix of mlocked and anon pages.
Once the system needs to swap something out, every single anon page will have the referenced bit set and the system needs to do lots of scanning before it can evict the first page. This scanning causes multiple CPUs to pile up and things slow down exponentially and/or catastrophically :)
Quoted 8 lines
Unfortunately the largest system I have access to on a regular basis has "only" 16GB of RAM :(
I am also making 2.6.25 based kernel RPMs available with the split LRU patch set, at http://people.redhat.com/riel/splitvm/
The most recently posted patches are newer, though...
I tested Rik's previous patch set with my noreclaim/mlock patches over the long weekend on 32GB systems--one ia64 [16cpu x 4 nodes] and one
x86_64[8 core x 4 nodes] on 26-rc2-mm1. A fairly heavy stress load ran for 92-93 hours on each system w/o error. Stats tracked throughout, no leaked pages, ...
Quoted 6 lines
Since Balbir is starting to look at this, I need to ask about interaction with the memory controller. It is currently unaware of the noreclaim list. I'm not sure what will happen if/when the memory controller tries to reclaim a page that system has moved to the noreclaim list. Something we'll need to address. It's on my list, but I won't get to it for a couple of weeks.
I have not looked at the patches, but thanks for the heads up. I intend to start looking at it in the spare bandwidth I have.
Signature · 4 lines
Warm Regards, Balbir Singh Linux Technology Center IBM, ISTL
Quoted 7 lines · On Tue, 27 May 2008 11:54:02 -0400 Lee Schermerhorn <[email protected]>…
Since Balbir is starting to look at this, I need to ask about interaction with the memory controller. It is currently unaware of the noreclaim list. I'm not sure what will happen if/when the memory controller tries to reclaim a page that system has moved to the noreclaim list. Something we'll need to address. It's on my list, but I won't get to it for a couple of weeks.
In my understanding, 2 checks we have to do.
1. When memcg finds PG_noreclaim page in its LRU, move it to noreclaim list of memcg.
2. When PG_noreclaim page is moved back to generic LRU, memcg should move it on its list. (we have to add a hook somewhere.)
But this may break current 'loose' synchronization between global LRU and memcg's LRU. When PG_noreclaim page is put back into active/inactive LRU ?
concerns are
A. how to implement '2'
B. race condtions.
But maybe we don't have to write a big patch.
Thanks,
-Kame
RFC PATCH 1/1 No Reclaim LRU Infrastructure enhancement for memcgroup
Hi
Quoted 7 lines
In my understanding, 2 checks we have to do.
1. When memcg finds
PG_noreclaimpage in its LRU, move it to noreclaim list of memcg.
2. WhenPG_noreclaimpage is moved back to generic LRU, memcg should move it on its list. (we have to add a hook somewhere.)
Quoted 5 lines
But this may break current 'loose' synchronization between global LRU and memcg's LRU. When
PG_noreclaimpage is put back into active/inactive LRU ?concerns are
A. how to implement '2'
I tried to implement it today.
this patch is made against "[PATCH -mm 13/16] No Reclaim LRU Infrastructure"
Quoted 1 line
B. race condtions.
don't worry :)
it isn't big problem.
global lru is reclaimbale and memcg lru is noreclaimable
-> we can repair at move lru of shrink_[in]active_page().
global lru is noreclaimbale and memcg lru is reclaimable
-> we can repair mem_cgroup_isolate_pages()
When memco lru moved, mem_cgroup_move_lists() is called. thus, I add noreclaim lru logic at that point.
--- include/linux/memcontrol.h | 2 - mm/memcontrol.c | 73 +++++++++++++++++++++++++++------------------ mm/swap.c | 2 - mm/vmscan.c | 10 ++++-- 4 files changed, 54 insertions(+), 33 deletions(-)
mm/memcontrol.c +45 -28
@@ -161,9 +161,10 @@ struct page_cgroup {int ref_cnt;/* cached, mapped, migrating */ int flags; }; -#define PAGE_CGROUP_FLAG_CACHE (0x1)/* charged as cache */ -#define PAGE_CGROUP_FLAG_ACTIVE (0x2)/* page is active in this cgroup */ -#define PAGE_CGROUP_FLAG_FILE (0x4)/* page is file system backed */ +#define PAGE_CGROUP_FLAG_CACHE (0x1)/* charged as cache */ +#define PAGE_CGROUP_FLAG_ACTIVE (0x2)/* page is active in this cgroup */ +#define PAGE_CGROUP_FLAG_FILE (0x4)/* page is file system backed */ +#define PAGE_CGROUP_FLAG_NORECLAIM (0x8)/* page is noreclaimable page */ static int page_cgroup_nid (struct page_cgroup *pc) {
@@ -283,10 +284,14 @@ static void __mem_cgroup_remove_list(str {int lru =LRU_BASE ; -if (pc->flags &PAGE_CGROUP_FLAG_ACTIVE ) - lru +=LRU_ACTIVE ; -if (pc->flags &PAGE_CGROUP_FLAG_FILE ) - lru +=LRU_FILE ; +if (pc->flags &PAGE_CGROUP_FLAG_NORECLAIM ) + lru =LRU_NORECLAIM ; +else { +if (pc->flags &PAGE_CGROUP_FLAG_ACTIVE ) + lru +=LRU_ACTIVE ; +if (pc->flags &PAGE_CGROUP_FLAG_FILE ) + lru +=LRU_FILE ; + }MEM_CGROUP_ZSTAT (mz, lru) -=1 ;
@@ -299,10 +304,14 @@ static void __mem_cgroup_add_list(struct {int lru =LRU_BASE ; -if (pc->flags &PAGE_CGROUP_FLAG_ACTIVE ) - lru +=LRU_ACTIVE ; -if (pc->flags &PAGE_CGROUP_FLAG_FILE ) - lru +=LRU_FILE ; +if (pc->flags &PAGE_CGROUP_FLAG_NORECLAIM ) + lru =LRU_NORECLAIM ; +else { +if (pc->flags &PAGE_CGROUP_FLAG_ACTIVE ) + lru +=LRU_ACTIVE ; +if (pc->flags &PAGE_CGROUP_FLAG_FILE ) + lru +=LRU_FILE ; + }MEM_CGROUP_ZSTAT (mz, lru) +=1 ;list_add (&pc->lru, &mz->lists[lru]);
@@ -310,21 +319,31 @@ static void __mem_cgroup_add_list(structmem_cgroup_charge_statistics (pc->mem_cgroup, pc->flags, true); } -static void __mem_cgroup_move_lists (struct page_cgroup *pc,bool active) +static void __mem_cgroup_move_lists (struct page_cgroup *pc,enum lru_list lru) {struct mem_cgroup_per_zone *mz =page_cgroup_zoneinfo (pc); -int from = pc->flags &PAGE_CGROUP_FLAG_ACTIVE ; -int file = pc->flags &PAGE_CGROUP_FLAG_FILE ; -int lru =LRU_FILE * !!file + !!from; +int active = pc->flags &PAGE_CGROUP_FLAG_ACTIVE ; +int file = pc->flags &PAGE_CGROUP_FLAG_FILE ; +int noreclaim = pc->flags &PAGE_CGROUP_FLAG_NORECLAIM ; +enum lru_list from = noreclaim ?LRU_NORECLAIM : + (LRU_FILE * !!file + !!active); -MEM_CGROUP_ZSTAT (mz, lru) -=1 ; +if (lru == from) +return ; -if (active) - pc->flags |=PAGE_CGROUP_FLAG_ACTIVE ; -else +MEM_CGROUP_ZSTAT (mz, from) -=1 ; + +if (is_noreclaim_lru (lru)) { pc->flags &= ~PAGE_CGROUP_FLAG_ACTIVE ; + pc->flags |=PAGE_CGROUP_FLAG_NORECLAIM ; + }else { +if (is_active_lru (lru)) + pc->flags |=PAGE_CGROUP_FLAG_ACTIVE ; +else + pc->flags &= ~PAGE_CGROUP_FLAG_ACTIVE ; + pc->flags &= ~PAGE_CGROUP_FLAG_NORECLAIM ; + } - lru =LRU_FILE * !!file + !!active;MEM_CGROUP_ZSTAT (mz, lru) +=1 ;list_move (&pc->lru, &mz->lists[lru]); }
@@ -342,7 +361,7 @@ int task_in_mem_cgroup(struct task_struc/* * This routine assumes that the appropriate zone's lru lock is already held */ -void mem_cgroup_move_lists (struct page *page,bool active) +void mem_cgroup_move_lists (struct page *page,enum lru_list lru) {struct page_cgroup *pc;struct mem_cgroup_per_zone *mz;
@@ -362,7 +381,7 @@ void mem_cgroup_move_lists(struct page *if (pc) { mz =page_cgroup_zoneinfo (pc);spin_lock_irqsave (&mz->lru_lock, flags); -__mem_cgroup_move_lists (pc, active); +__mem_cgroup_move_lists (pc, lru);spin_unlock_irqrestore (&mz->lru_lock, flags); }unlock_page_cgroup (page);
@@ -460,12 +479,10 @@ unsigned long mem_cgroup_isolate_pages(u/* * TODO: play better with lumpy reclaim, grabbing anything. */ -if (PageActive (page) && !active) { -__mem_cgroup_move_lists (pc, true); -continue ; - } -if (!PageActive (page) && active) { -__mem_cgroup_move_lists (pc, false); +if (PageNoreclaim (page) || + (PageActive (page) && !active) || + (!PageActive (page) && active)) { +__mem_cgroup_move_lists (pc,page_lru (page));continue ; }
mm/swap.c +1 -1
@@ -189,7 +189,7 @@ void activate_page(struct page *page) lru +=LRU_ACTIVE ;add_page_to_lru_list (zone, page, lru);__count_vm_event (PGACTIVATE ); -mem_cgroup_move_lists (page, true); +mem_cgroup_move_lists (page, lru);if (file) { zone->recent_scanned_file++;
mm/vmscan.c +7 -3
@@ -475,13 +475,16 @@ int putback_lru_page(struct page *page)* non-reclaimable page on [in]active list. * We know how to handle that. */ -lru_cache_add_lru (page, lru +page_file_cache (page)); + lru +=page_file_cache (page); +lru_cache_add_lru (page, lru); +mem_cgroup_move_lists (page, lru); }else {/* * Put non-reclaimable pages directly on zone's noreclaim * list. */ add_page_to_noreclaim_list (page); +mem_cgroup_move_lists (page,LRU_NORECLAIM ); }put_page (page);/* drop ref from isolate */
@@ -1065,6 +1068,7 @@ static unsigned long shrink_inactive_lis }SetPageLRU (page);add_page_to_lru_list (zone, page, lru); +mem_cgroup_move_lists (page, lru);if (!pagevec_add (&pvec, page)) {spin_unlock_irq (&zone->lru_lock);__pagevec_release (&pvec);
@@ -1202,7 +1206,7 @@ static void shrink_active_list(unsignedClearPageActive (page);list_move (&page->lru, &zone->list[lru]); -mem_cgroup_move_lists (page, false); +mem_cgroup_move_lists (page, lru); pgmoved++;if (!pagevec_add (&pvec, page)) { __mod_zone_page_state(zone, NR_INACTIVE_ANON + lru,
@@ -1234,7 +1238,7 @@ static void shrink_active_list(unsignedVM_BUG_ON (!PageActive (page));list_move (&page->lru, &zone->list[lru]); -mem_cgroup_move_lists (page, true); +mem_cgroup_move_lists (page, lru); pgmoved++;if (!pagevec_add (&pvec, page)) { __mod_zone_page_state(zone, NR_INACTIVE_ANON + lru,
include/linux/memcontrol.h +1 -1
@@ -35,7 +35,7 @@ extern int mem_cgroup_charge(struct pageextern int mem_cgroup_cache_charge (struct page *page,struct mm_struct *mm,gfp_t gfp_mask);extern void mem_cgroup_uncharge_page (struct page *page); -extern void mem_cgroup_move_lists (struct page *page,bool active); +extern void mem_cgroup_move_lists (struct page *page,enum lru_list lru); extern unsigned long mem_cgroup_isolate_pages(unsigned long nr_to_scan, struct list_head *dst, unsigned long *scanned, int order,
add noreclaim field to /dev/cgroup/[group]/memory.stat.
--- mm/memcontrol.c | 6 ++++++ 1 file changed, 6 insertions(+)
mm/memcontrol.c +6 -0
@@ -903,6 +903,7 @@ static int mem_control_stat_show(struct {unsigned long active_anon, inactive_anon;unsigned long active_file, inactive_file; +unsigned long noreclaim; inactive_anon =mem_cgroup_get_all_zonestat (mem_cont,LRU_INACTIVE_ANON );
@@ -912,10 +913,15 @@ static int mem_control_stat_show(struct LRU_INACTIVE_FILE); active_file =mem_cgroup_get_all_zonestat (mem_cont,LRU_ACTIVE_FILE ); + noreclaim =mem_cgroup_get_all_zonestat (mem_cont, +LRU_NORECLAIM ); + cb->fill (cb,"active_anon" , (active_anon) *PAGE_SIZE ); cb->fill (cb,"inactive_anon" , (inactive_anon) *PAGE_SIZE ); cb->fill (cb,"active_file" , (active_file) *PAGE_SIZE ); cb->fill (cb,"inactive_file" , (inactive_file) *PAGE_SIZE ); + cb->fill (cb,"noreclaim" , noreclaim *PAGE_SIZE ); + }return 0 ; }
Re: [RFC PATCH] No Reclaim LRU Infrastructure enhancement for memcgroup
Quoted 21 lines · KOSAKI Motohiro
Hi
Quoted 7 lines
In my understanding, 2 checks we have to do.
1. When memcg finds
PG_noreclaimpage in its LRU, move it to noreclaim list of memcg.
2. WhenPG_noreclaimpage is moved back to generic LRU, memcg should move it on its list. (we have to add a hook somewhere.)Quoted 5 lines
But this may break current 'loose' synchronization between global LRU and memcg's LRU. When
PG_noreclaimpage is put back into active/inactive LRU ?concerns are
A. how to implement '2'I tried to implement it today.
this patch is made against "[PATCH -mm 13/16] No Reclaim LRU Infrastructure"Quoted 1 line
B. race condtions.
Quoted 10 lines
don't worry :)
it isn't big problem.global lru is reclaimbale and memcg lru is noreclaimable
-> we can repair at move lru of shrink_[in]active_page().global lru is noreclaimbale and memcg lru is reclaimable
-> we can repair mem_cgroup_isolate_pages()
I've tried these patches and I still get OOM killed. I'll investigate a bit more.
Signature · 4 lines
Warm Regards, Balbir Singh Linux Technology Center IBM, ISTL
Re: [RFC PATCH] No Reclaim LRU Infrastructure enhancement for memcgroup
Quoted 26 lines · Balbir Singh
Quoted 25 lines · KOSAKI Motohiro
Hi
Quoted 7 lines
In my understanding, 2 checks we have to do.
1. When memcg finds
PG_noreclaimpage in its LRU, move it to noreclaim list of memcg.
2. WhenPG_noreclaimpage is moved back to generic LRU, memcg should move it on its list. (we have to add a hook somewhere.)Quoted 5 lines
But this may break current 'loose' synchronization between global LRU and memcg's LRU. When
PG_noreclaimpage is put back into active/inactive LRU ?concerns are
A. how to implement '2'I tried to implement it today.
this patch is made against "[PATCH -mm 13/16] No Reclaim LRU Infrastructure"Quoted 1 line
B. race condtions.
don't worry :)
it isn't big problem.global lru is reclaimbale and memcg lru is noreclaimable
-> we can repair at move lru of shrink_[in]active_page().
Quoted 8 lines
Quoted 4 lines
global lru is noreclaimbale and memcg lru is reclaimable
-> we can repair mem_cgroup_isolate_pages()I've tried these patches and I still get OOM killed. I'll investigate a bit more.
Hmm... I cannot reporoduce this problem.
When a process exceeds the limit of the group, swap is used as expected.
I tested 2.6.26-rc2-mm1 + splitlru-v8 + Kosaki-san's 3 patches
+ fix for shrink_active_list(attached).
I'm afraid, have you applied rvr-07.1-kosaki-memcg-shrink_list.patch which Kosaki-san posted to [07/16] of this thread?
Thanks,
Daisuke Nishimura.
---
mm/vmscan.c +1 -0
@@ -1153,6 +1153,7 @@ static void shrink_active_list(unsigned long nr_pages, struct zone *__mod_zone_page_state (zone,NR_ACTIVE_ANON , -pgmoved);spin_unlock_irq (&zone->lru_lock); + pgmoved =0 ;while (!list_empty (&l_hold)) {cond_resched (); page =lru_to_page (&l_hold);
Quoted 23 lines · Rik van Riel
Quoted 9 lines · On Mon, 26 May 2008 23:54:55 +0530 Balbir Singh <[email protected]>…
Quoted 4 lines · Rik van Riel
On large memory systems, the VM can spend way too much time scanning through pages that it cannot (or should not) evict from memory. Not only does it use up CPU time, but it also provokes lock contention and can leave large systems under memory presure in a catatonic state.
Hi, Rik,
This patchset looks good (I did a brief scan). I'll go ahead and play with it? What is a good memory size to test the patches on (to see improvements).
The larger, the better. One known problem with the current upstream VM is large numbers of anonymous pages, or a mix of mlocked and anon pages.
Once the system needs to swap something out, every single anon page will have the referenced bit set and the system needs to do lots of scanning before it can evict the first page. This scanning causes multiple CPUs to pile up and things slow down exponentially and/or catastrophically :)
Quoted 8 lines
Unfortunately the largest system I have access to on a regular basis has "only" 16GB of RAM :(
I am also making 2.6.25 based kernel RPMs available with the split LRU patch set, at http://people.redhat.com/riel/splitvm/
The most recently posted patches are newer, though...
Rik,
I've run into a case where with the splitvm patches the memory controller immediately OOMs a task in the cgroup on exceeding it's limit. I am going to try and find the root cause for it.
Signature · 4 lines
Warm Regards, Balbir Singh Linux Technology Center IBM, ISTL
Hi balir-san,
Quoted 3 lines
I've run into a case where with the splitvm patches the memory controller immediately OOMs a task in the cgroup on exceeding it's limit. I am going to try and find the root cause for it.
I sent the patch of fix this problem.
please see it.
Quoted 8 lines · KOSAKI Motohiro
Hi balir-san,
Quoted 3 lines
I've run into a case where with the splitvm patches the memory controller immediately OOMs a task in the cgroup on exceeding it's limit. I am going to try and find the root cause for it.
I sent the patch of fix this problem.
please see it.
Yes, I just saw it after I sent out my email. Let me try those patches. I'll revert back
Signature · 4 lines
Warm Regards, Balbir Singh Linux Technology Center IBM, ISTL
Quoted 13 lines · Rik van Riel
On large memory systems, the VM can spend way too much time scanning through pages that it cannot (or should not) evict from memory. Not only does it use up CPU time, but it also provokes lock contention and can leave large systems under memory presure in a catatonic state.
Against 2.6.26-rc2-mm1
This patch series improves VM scalability by:
1) putting filesystem backed, swap backed and non-reclaimable pages onto their own LRUs, so the system only scans the pages that it can/should evict from memory
Quoted 8 lines
2) switching to SEQ replacement for the anonymous LRUs, so the number of pages that need to be scanned when the system starts swapping is bound to a reasonable number
3) keeping non-reclaimable pages off the LRU completely, so the VM does not waste CPU time scanning them. Currently only ramfs and
SHM_LOCKEDpages are kept on the noreclaim list, mlock()ed VMAs will be added later
I think I've run into #2 with kvm on s390 lately. I've tried a large setup with 200 guests running WebSphere. The guest memory is stored in anonymous pages, all guests are started up from a script so everything is dirty initially. I use 200gig swap with 45 gig main memory for the scenario. Everything runs perfect except when vmscan is triggered for the first time: it starts to writeback, and the whole system freezes until it has paged out the 15gig in the inactive list. From there on, everything runs smooth again with a constant swap rate. I'd like to try your patchset to see how that behave in this scenario. Do you have a version that applies against current git, 2.6.26-rc3 or similar?
Quoted 3 lines · On Thu, 29 May 2008 14:47:43 +0200 Carsten Otte <[email protected]> wrote:
I'd like to try your patchset to see how that behave in this scenario. Do you have a version that applies against current git, 2.6.26-rc3 or similar?
I have a slightly older version of this patch set against 2.6.25 - it may apply to -rc3. I mostly stick to the -mm kernels though, since that is my merge target.
Signature · 1 line
All rights reversed.