forked from TencentOS/TencentOS-kernel
mm/lru: introduce TestClearPageLRU
Currently lru_lock still guards both lru list and page's lru bit, that's ok. but if we want to use specific lruvec lock on the page, we need to pin down the page's lruvec/memcg during locking. Just taking lruvec lock first may be undermined by the page's memcg charge/migration. To fix this problem, we could clear the lru bit out of locking and use it as pin down action to block the page isolation in memcg changing. So now a standard steps of page isolation is following: 1, get_page(); #pin the page avoid to be free 2, TestClearPageLRU(); #block other isolation like memcg change 3, spin_lock on lru_lock; #serialize lru list access 4, delete page from lru list; The step 2 could be optimzed/replaced in scenarios which page is unlikely be accessed or be moved between memcgs. This patch start with the first part: TestClearPageLRU, which combines PageLRU check and ClearPageLRU into a macro func TestClearPageLRU. This function will be used as page isolation precondition to prevent other isolations some where else. Then there are may !PageLRU page on lru list, need to remove BUG() checking accordingly. There 2 rules for lru bit now: 1, the lru bit still indicate if a page on lru list, just in some temporary moment(isolating), the page may have no lru bit when it's on lru list. but the page still must be on lru list when the lru bit set. 2, have to remove lru bit before delete it from lru list. As Andrew Morton mentioned this change would dirty cacheline for page isn't on LRU. But the lost would be acceptable in Rong Chen <rong.a.chen@intel.com> report: https://lore.kernel.org/lkml/20200304090301.GB5972@shao2-debian/ [flyingpeng: compatibility code porting] Suggested-by: Johannes Weiner <hannes@cmpxchg.org> Signed-off-by: Alex Shi <alex.shi@linux.alibaba.com> Signed-off-by: Peng Hao <flyingpeng@tencent.com>
This commit is contained in:
parent
f646d14e7d
commit
4702fd8039
|
|
@ -318,6 +318,7 @@ PAGEFLAG(Referenced, referenced, PF_HEAD)
|
|||
PAGEFLAG(Dirty, dirty, PF_HEAD) TESTSCFLAG(Dirty, dirty, PF_HEAD)
|
||||
__CLEARPAGEFLAG(Dirty, dirty, PF_HEAD)
|
||||
PAGEFLAG(LRU, lru, PF_HEAD) __CLEARPAGEFLAG(LRU, lru, PF_HEAD)
|
||||
TESTCLEARFLAG(LRU, lru, PF_HEAD)
|
||||
PAGEFLAG(Active, active, PF_HEAD) __CLEARPAGEFLAG(Active, active, PF_HEAD)
|
||||
TESTCLEARFLAG(Active, active, PF_HEAD)
|
||||
PAGEFLAG(Workingset, workingset, PF_HEAD)
|
||||
|
|
|
|||
|
|
@ -272,10 +272,9 @@ static void __munlock_pagevec(struct pagevec *pvec, struct zone *zone)
|
|||
* We already have pin from follow_page_mask()
|
||||
* so we can spare the get_page() here.
|
||||
*/
|
||||
if (PageLRU(page)) {
|
||||
if (TestClearPageLRU(page)) {
|
||||
struct lruvec *lruvec;
|
||||
|
||||
ClearPageLRU(page);
|
||||
lruvec = mem_cgroup_page_lruvec(page,
|
||||
page_pgdat(page));
|
||||
del_page_from_lru_list(page, lruvec,
|
||||
|
|
|
|||
39
mm/vmscan.c
39
mm/vmscan.c
|
|
@ -1586,7 +1586,7 @@ unsigned long reclaim_clean_pages_from_list(struct zone *zone,
|
|||
*/
|
||||
int __isolate_lru_page(struct page *page, isolate_mode_t mode)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
int ret = -EBUSY;
|
||||
|
||||
/* Only take pages on the LRU. */
|
||||
if (!PageLRU(page))
|
||||
|
|
@ -1596,7 +1596,6 @@ int __isolate_lru_page(struct page *page, isolate_mode_t mode)
|
|||
if (PageUnevictable(page) && !(mode & ISOLATE_UNEVICTABLE))
|
||||
return ret;
|
||||
|
||||
ret = -EBUSY;
|
||||
|
||||
/*
|
||||
* To minimise LRU disruption, the caller can indicate that it only
|
||||
|
|
@ -1644,8 +1643,10 @@ int __isolate_lru_page(struct page *page, isolate_mode_t mode)
|
|||
* sure the page is not being freed elsewhere -- the
|
||||
* page release code relies on it.
|
||||
*/
|
||||
ClearPageLRU(page);
|
||||
ret = 0;
|
||||
if (TestClearPageLRU(page))
|
||||
ret = 0;
|
||||
else
|
||||
put_page(page);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -1715,8 +1716,6 @@ static unsigned long isolate_lru_pages(unsigned long nr_to_scan,
|
|||
page = lru_to_page(src);
|
||||
prefetchw_prev_lru_page(page, src, flags);
|
||||
|
||||
VM_BUG_ON_PAGE(!PageLRU(page), page);
|
||||
|
||||
nr_pages = compound_nr(page);
|
||||
total_scan += nr_pages;
|
||||
|
||||
|
|
@ -1813,20 +1812,17 @@ int isolate_lru_page(struct page *page)
|
|||
VM_BUG_ON_PAGE(!page_count(page), page);
|
||||
WARN_RATELIMIT(PageTail(page), "trying to isolate tail page");
|
||||
|
||||
if (PageLRU(page)) {
|
||||
if (TestClearPageLRU(page)) {
|
||||
pg_data_t *pgdat = page_pgdat(page);
|
||||
struct lruvec *lruvec;
|
||||
|
||||
spin_lock_irq(&pgdat->lru_lock);
|
||||
get_page(page);
|
||||
lruvec = mem_cgroup_page_lruvec(page, pgdat);
|
||||
if (PageLRU(page)) {
|
||||
int lru = page_lru(page);
|
||||
get_page(page);
|
||||
ClearPageLRU(page);
|
||||
del_page_from_lru_list(page, lruvec, lru);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
spin_lock_irq(&pgdat->lru_lock);
|
||||
del_page_from_lru_list(page, lruvec, page_lru(page));
|
||||
spin_unlock_irq(&pgdat->lru_lock);
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -4759,6 +4755,11 @@ void check_move_unevictable_pages(struct pagevec *pvec)
|
|||
struct pglist_data *pagepgdat = page_pgdat(page);
|
||||
|
||||
pgscanned++;
|
||||
|
||||
/* block memcg migration during page moving between lru */
|
||||
if (!TestClearPageLRU(page))
|
||||
continue;
|
||||
|
||||
if (pagepgdat != pgdat) {
|
||||
if (pgdat)
|
||||
spin_unlock_irq(&pgdat->lru_lock);
|
||||
|
|
@ -4767,10 +4768,7 @@ void check_move_unevictable_pages(struct pagevec *pvec)
|
|||
}
|
||||
lruvec = mem_cgroup_page_lruvec(page, pgdat);
|
||||
|
||||
if (!PageLRU(page) || !PageUnevictable(page))
|
||||
continue;
|
||||
|
||||
if (page_evictable(page)) {
|
||||
if (page_evictable(page) && PageUnevictable(page)) {
|
||||
enum lru_list lru = page_lru_base_type(page);
|
||||
|
||||
VM_BUG_ON_PAGE(PageActive(page), page);
|
||||
|
|
@ -4778,6 +4776,7 @@ void check_move_unevictable_pages(struct pagevec *pvec)
|
|||
del_page_from_lru_list(page, lruvec, LRU_UNEVICTABLE);
|
||||
add_page_to_lru_list(page, lruvec, lru);
|
||||
pgrescued++;
|
||||
SetPageLRU(page);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4785,6 +4784,8 @@ void check_move_unevictable_pages(struct pagevec *pvec)
|
|||
__count_vm_events(UNEVICTABLE_PGRESCUED, pgrescued);
|
||||
__count_vm_events(UNEVICTABLE_PGSCANNED, pgscanned);
|
||||
spin_unlock_irq(&pgdat->lru_lock);
|
||||
} else if (pgscanned) {
|
||||
count_vm_events(UNEVICTABLE_PGSCANNED, pgscanned);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(check_move_unevictable_pages);
|
||||
|
|
|
|||
Loading…
Reference in New Issue