mm: simplify vmscan vs release refcounting
Subsystems (4)
PATCH RESEND 5/6 mm: simplify vmscan vs release refcounting
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_add_unless to ensure we never pick up a 0 refcount page from the LRU, thus a 0 refcount page will never have its refcount elevated until it is allocated again.
include/linux/mm.h +11 -8
@@ -301,17 +301,20 @@ struct 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) ==-1 ); +return atomic_add_negative (-1 , &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 +* Try to grab a ref unless the page has a refcount of zero, return false if +* that is the case. */ -#define get_page_testone (p) atomic_inc_and_test(&(p)->_count) +static inline int get_page_unless_zero (struct page *page) +{ +return atomic_add_unless (&page->_count,1 ,-1 ); +}#define set_page_count (p,v) atomic_set(&(p)->_count, (v) - 1)#define __put_page (p) atomic_dec(&(p)->_count)
mm/vmscan.c +11 -14
@@ -821,29 +821,26 @@ 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);BUG_ON (!PageLRU (page));list_del (&page->lru); -if (unlikely (get_page_testone (page))) { + target = src; +if (likely (get_page_unless_zero (page))) {/* -* It is being freed elsewhere +* Be careful not to clear PageLRU until after we're +* sure the page is not being freed elsewhere -- the +* page release code relies on it. */ -__put_page (page); -list_add (&page->lru, src); -continue ; - } +ClearPageLRU (page); + target = dst; + nr_taken++; + }/* else it is being freed elsewhere */ -/* -* Be careful not to clear PageLRU until after we're sure -* the page is not being freed elsewhere -- the page release -* code relies on it. -*/ -ClearPageLRU (page); -list_add (&page->lru, dst); - nr_taken++; +list_add (&page->lru, target); } *scanned = scan;
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH RESEND 6/6 mm: de-skew page refcounting
atomic_add_unless (atomic_inc_not_zero) no longer requires an offset refcount to function correctly.
include/linux/mm.h +5 -14
@@ -286,15 +286,6 @@ 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. */ /*
@@ -303,8 +294,8 @@ struct page {*/ static inline int put_page_testzero (struct page *page) { -BUG_ON (atomic_read (&page->_count) ==-1 ); -return atomic_add_negative (-1 , &page->_count); +BUG_ON (atomic_read (&page->_count) ==0 ); +return atomic_dec_and_test (&page->_count); }/*
@@ -313,10 +304,10 @@ static inline int put_page_testzero(stru*/ static inline int get_page_unless_zero (struct page *page) { -return atomic_add_unless (&page->_count,1 ,-1 ); +return atomic_inc_not_zero (&page->_count); } -#define set_page_count (p,v) atomic_set(&(p)->_count, (v) - 1) +#define set_page_count (p,v) atomic_set(&(p)->_count, (v))#define __put_page (p) atomic_dec(&(p)->_count)extern void FASTCALL (__page_cache_release (struct page *));
@@ -325,7 +316,7 @@ static inline int page_count(struct page {if (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)
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 2 lines · Nick Piggin
Signed-off-by Nick Piggin <[email protected]>
ACK, looks ok by me now.
I assume this is still "-mm" material for now.
Linus
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>