mm: optimisations
Subsystems (4)
cover mm: optimisations
The following patchset (against 2.6.15-rc5ish) uses the new atomic ops to do away with the offset page refcounting, and simplify the race that it was designed to cover.
This allows some nice optimisations, and we end up saving 2 atomic ops including a spin_lock_irqsave in the !PageLRU case, and 2 or 3 atomic ops in the PageLRU case in the page-release path.
It also happens to be a requirement for my lockless pagecache work, but they stand on their own as good patches.
Nick
Signature · 4 lines
SUSE Labs, Novell Inc. Send instant messages to your online friends http://au.messenger.yahoo.com
PATCH 1/4 mm: page refcount use atomic primitives
The VM has an interesting race where a page refcount can drop to zero, but it is still on the LRU lists for a short time. This was solved by testing a 0->1 refcount transition when picking up pages from the LRU, and dropping the refcount in that case.
Instead, use atomic_inc_not_zero to ensure we never pick up a 0 refcount page from the LRU (ie. we guarantee the page will not be touched).
This ensures we can test PageLRU without taking the lru_lock, and allows further optimisations (in later patches).
include/linux/mm.h +22 -19
@@ -281,49 +281,52 @@ struct page {* * Also, many kernel routines increase the page count before a critical * routine so they can be sure the page doesn't go away from under them. -* -* Since 2.6.6 (approx), a free page has ->_count = -1. This is so that we -* can use atomic_add_negative(-1, page->_count) to detect when the page -* becomes free and so that we can also use atomic_inc_and_test to atomically -* detect when we just tried to grab a ref on a page which some other CPU has -* already deemed to be freeable. -* -* NO code should make assumptions about this internal detail! Use the provided -* macros which retain the old rules: page_count(page) == 0 is a free page. */ /* * Drop a ref, return true if the logical refcount fell to zero (the page has * no users) */ -#define put_page_testzero (p) \ - ({ \ - BUG_ON(page_count(p) == 0); \ - atomic_add_negative(-1, &(p)->_count); \ - }) +static inline int put_page_testzero (struct page *page) +{ +BUG_ON (atomic_read (&page->_count) ==0 ); +return atomic_dec_and_test (&page->_count); +}/* * Grab a ref, return true if the page previously had a logical refcount of * zero. ie: returns true if we just grabbed an already-deemed-to-be-free page */ -#define get_page_testone (p) atomic_inc_and_test(&(p)->_count) +static inline int get_page_unless_zero (struct page *page) +{ +return atomic_inc_not_zero (&page->_count); +} -#define set_page_count (p,v) atomic_set(&(p)->_count, v - 1) -#define __put_page (p) atomic_dec(&(p)->_count) +static inline void set_page_count (struct page *page,int v) +{ +atomic_set (&page->_count, v); +} + +static inline void __put_page (struct page *page) +{ +BUG_ON (atomic_read (&page->_count) ==0 ); +atomic_dec (&page->_count); +}extern void FASTCALL (__page_cache_release (struct page *));static inline int page_count (struct page *page) { -if (PageCompound (page)) +if (unlikely (PageCompound (page))) page = (struct page *)page_private (page); -return atomic_read (&page->_count) +1 ; +return atomic_read (&page->_count); }static inline void get_page (struct page *page) {if (unlikely (PageCompound (page))) page = (struct page *)page_private (page); +BUG_ON (atomic_read (&page->_count) ==0 );atomic_inc (&page->_count); }
mm/vmscan.c +9 -13
@@ -599,24 +599,20 @@ static int isolate_lru_pages(int nr_to_sint scan =0 ;while (scan++ < nr_to_scan && !list_empty (src)) { +struct list_head *target; page =lru_to_page (src);prefetchw_prev_lru_page (page, src, flags); -if (!TestClearPageLRU (page)) -BUG (); +BUG_ON (!PageLRU (page));list_del (&page->lru); -if (get_page_testone (page)) { -/* -* It is being freed elsewhere -*/ -__put_page (page); -SetPageLRU (page); -list_add (&page->lru, src); -continue ; - }else { -list_add (&page->lru, dst); + target = src; +if (get_page_unless_zero (page)) { +ClearPageLRU (page); + target = dst; nr_taken++; - } + }/* else it is being freed elsewhere */ + +list_add (&page->lru, target); } *scanned = scan;
mm/swap.c +29 -23
73 lines not on this page · raw message
Send instant messages to your online friends http://au.messenger.yahoo.com
PATCH 2/4 mm: PageLRU no testset
In reply to Nick PigginPG_lru is protected by zone->lru_lock. It does not need TestSet/TestClear operations.
mm/vmscan.c +6 -6
@@ -666,8 +666,8 @@ static void shrink_cache(struct zone *zo*/ while (!list_empty (&page_list)) { page =lru_to_page (&page_list); -if (TestSetPageLRU (page)) -BUG (); +BUG_ON (PageLRU (page)); +SetPageLRU (page);list_del (&page->lru);if (PageActive (page))add_page_to_active_list (zone, page);
@@ -779,8 +779,8 @@ refill_inactive_zone(struct zone *zone,while (!list_empty (&l_inactive)) { page =lru_to_page (&l_inactive);prefetchw_prev_lru_page (page, &l_inactive, flags); -if (TestSetPageLRU (page)) -BUG (); +BUG_ON (PageLRU (page)); +SetPageLRU (page);if (!TestClearPageActive (page))BUG ();list_move (&page->lru, &zone->inactive_list);
@@ -808,8 +808,8 @@ refill_inactive_zone(struct zone *zone,while (!list_empty (&l_active)) { page =lru_to_page (&l_active);prefetchw_prev_lru_page (page, &l_active, flags); -if (TestSetPageLRU (page)) -BUG (); +BUG_ON (PageLRU (page)); +SetPageLRU (page);BUG_ON (!PageActive (page));list_move (&page->lru, &zone->active_list); pgmoved++;
mm/swap.c +8 -8
@@ -179,8 +179,8 @@ void fastcall __page_cache_release(strucstruct zone *zone =page_zone (page);spin_lock_irqsave (&zone->lru_lock, flags); -if (!TestClearPageLRU (page)) -BUG (); +BUG_ON (!PageLRU (page)); +ClearPageLRU (page);del_page_from_lru (zone, page);spin_unlock_irqrestore (&zone->lru_lock, flags); }
@@ -224,8 +224,8 @@ void release_pages(struct page **pages, zone = pagezone;spin_lock_irq (&zone->lru_lock); } -if (!TestClearPageLRU (page)) -BUG (); +BUG_ON (!PageLRU (page)); +ClearPageLRU (page);del_page_from_lru (zone, page); }BUG_ON (page_count (page));
@@ -305,8 +305,8 @@ void __pagevec_lru_add(struct pagevec *p zone = pagezone;spin_lock_irq (&zone->lru_lock); } -if (TestSetPageLRU (page)) -BUG (); +BUG_ON (PageLRU (page)); +SetPageLRU (page);add_page_to_inactive_list (zone, page); }if (zone)
@@ -332,8 +332,8 @@ void __pagevec_lru_add_active(struct pag zone = pagezone;spin_lock_irq (&zone->lru_lock); } -if (TestSetPageLRU (page)) -BUG (); +BUG_ON (PageLRU (page)); +SetPageLRU (page);if (TestSetPageActive (page))BUG ();add_page_to_active_list (zone, page);
include/linux/page-flags.h +2 -3
@@ -233,10 +233,9 @@ extern void __mod_page_state_offset(unsi#define __ClearPageDirty (page) __clear_bit(PG_dirty, &(page)->flags)#define TestClearPageDirty (page) test_and_clear_bit(PG_dirty, &(page)->flags) -#define SetPageLRU (page) set_bit(PG_lru, &(page)->flags)#define PageLRU (page) test_bit(PG_lru, &(page)->flags) -#define TestSetPageLRU (page) test_and_set_bit(PG_lru, &(page)->flags) -#define TestClearPageLRU (page) test_and_clear_bit(PG_lru, &(page)->flags) +#define SetPageLRU (page) set_bit(PG_lru, &(page)->flags) +#define ClearPageLRU (page) clear_bit(PG_lru, &(page)->flags)#define PageActive (page) test_bit(PG_active, &(page)->flags)#define SetPageActive (page) set_bit(PG_active, &(page)->flags)
Send instant messages to your online friends http://au.messenger.yahoo.com
PATCH 3/4 mm: PageActive no testset
In reply to Nick PigginPG_active is protected by zone->lru_lock, it does not need TestSet/TestClear operations.
mm/vmscan.c +3 -2
@@ -781,8 +781,9 @@ refill_inactive_zone(struct zone *zone,prefetchw_prev_lru_page (page, &l_inactive, flags);BUG_ON (PageLRU (page));SetPageLRU (page); -if (!TestClearPageActive (page)) -BUG (); +BUG_ON (!PageActive (page)); +ClearPageActive (page); +list_move (&page->lru, &zone->inactive_list); pgmoved++;if (!pagevec_add (&pvec, page)) {
mm/swap.c +2 -2
@@ -334,8 +334,8 @@ void __pagevec_lru_add_active(struct pag }BUG_ON (PageLRU (page));SetPageLRU (page); -if (TestSetPageActive (page)) -BUG (); +BUG_ON (PageActive (page)); +SetPageActive (page);add_page_to_active_list (zone, page); }if (zone)
include/linux/page-flags.h +0 -2
@@ -240,8 +240,6 @@ extern void __mod_page_state_offset(unsi#define PageActive (page) test_bit(PG_active, &(page)->flags)#define SetPageActive (page) set_bit(PG_active, &(page)->flags)#define ClearPageActive (page) clear_bit(PG_active, &(page)->flags) -#define TestClearPageActive (page) test_and_clear_bit(PG_active, &(page)->flags) -#define TestSetPageActive (page) test_and_set_bit(PG_active, &(page)->flags)#define PageSlab (page) test_bit(PG_slab, &(page)->flags)#define SetPageSlab (page) set_bit(PG_slab, &(page)->flags)
Send instant messages to your online friends http://au.messenger.yahoo.com
PATCH 4/4 mm: less atomic ops
In reply to Nick PigginIn the page release paths, we can be sure that nobody will mess with our page->flags because the refcount has dropped to 0. So no need for atomic operations here.
include/linux/page-flags.h +2 -0
@@ -236,10 +236,12 @@ extern void __mod_page_state_offset(unsi#define PageLRU (page) test_bit(PG_lru, &(page)->flags)#define SetPageLRU (page) set_bit(PG_lru, &(page)->flags)#define ClearPageLRU (page) clear_bit(PG_lru, &(page)->flags) +#define __ClearPageLRU (page) __clear_bit(PG_lru, &(page)->flags)#define PageActive (page) test_bit(PG_active, &(page)->flags)#define SetPageActive (page) set_bit(PG_active, &(page)->flags)#define ClearPageActive (page) clear_bit(PG_active, &(page)->flags) +#define __ClearPageActive (page) __clear_bit(PG_active, &(page)->flags)#define PageSlab (page) test_bit(PG_slab, &(page)->flags)#define SetPageSlab (page) set_bit(PG_slab, &(page)->flags)
mm/swap.c +2 -2
@@ -180,7 +180,7 @@ void fastcall __page_cache_release(strucstruct zone *zone =page_zone (page);spin_lock_irqsave (&zone->lru_lock, flags);BUG_ON (!PageLRU (page)); -ClearPageLRU (page); +__ClearPageLRU (page);del_page_from_lru (zone, page);spin_unlock_irqrestore (&zone->lru_lock, flags); }
@@ -225,7 +225,7 @@ void release_pages(struct page **pages,spin_lock_irq (&zone->lru_lock); }BUG_ON (!PageLRU (page)); -ClearPageLRU (page); +__ClearPageLRU (page);del_page_from_lru (zone, page); }BUG_ON (page_count (page));
include/linux/mm_inline.h +1 -1
@@ -32,7 +32,7 @@ del_page_from_lru(struct zone *zone, str {list_del (&page->lru);if (PageActive (page)) { -ClearPageActive (page); +__ClearPageActive (page); zone->nr_active--; }else { zone->nr_inactive--;
Send instant messages to your online friends http://au.messenger.yahoo.com