Re: [PATCH v7 02/10] mm/memcg: fold lru_lock in lock_page_lru
From: Alex Shi <hidden>
Date: 2020-01-13 12:49:00
Also in:
linux-mm, lkml
在 2020/1/13 下午5:55, Konstantin Khlebnikov 写道:
quoted
quoted
quoted
index c5b5f74cfd4d..0ad10caabc3d 100644--- a/mm/memcontrol.c +++ b/mm/memcontrol.c@@ -2572,12 +2572,11 @@ static void cancel_charge(struct mem_cgroup *memcg, unsigned int nr_pages)static void lock_page_lru(struct page *page, int *isolated) { - pg_data_t *pgdat = page_pgdat(page); - - spin_lock_irq(&pgdat->lru_lock); if (PageLRU(page)) { + pg_data_t *pgdat = page_pgdat(page); struct lruvec *lruvec; + spin_lock_irq(&pgdat->lru_lock);That's wrong. Here PageLRU must be checked again under lru_lock.Hi, Konstantin, For logical remain, we can get the lock and then release for !PageLRU. but I still can figure out the problem scenario. Would like to give more hints?That's trivial race: page could be isolated from lru between if (PageLRU(page)) and spin_lock_irq(&pgdat->lru_lock);
yes, it could be a problem. guess the following change could helpful:
I will update it in new version.
Thanks a lot!
Alex
-static void lock_page_lru(struct page *page, int *isolated)
-{
- pg_data_t *pgdat = page_pgdat(page);
-
- spin_lock_irq(&pgdat->lru_lock);
- if (PageLRU(page)) {
- struct lruvec *lruvec;
-
- lruvec = mem_cgroup_page_lruvec(page, pgdat);
- ClearPageLRU(page);
- del_page_from_lru_list(page, lruvec, page_lru(page));
- *isolated = 1;
- } else
- *isolated = 0;
-}
-
-static void unlock_page_lru(struct page *page, int isolated)
-{
- pg_data_t *pgdat = page_pgdat(page);
-
- if (isolated) {
- struct lruvec *lruvec;
-
- lruvec = mem_cgroup_page_lruvec(page, pgdat);
- VM_BUG_ON_PAGE(PageLRU(page), page);
- SetPageLRU(page);
- add_page_to_lru_list(page, lruvec, page_lru(page));
- }
- spin_unlock_irq(&pgdat->lru_lock);
-}
-
static void commit_charge(struct page *page, struct mem_cgroup *memcg,
bool lrucare)
{
- int isolated;
+ struct lruvec *lruvec = NULL;
VM_BUG_ON_PAGE(page->mem_cgroup, page);
@@ -2612,8 +2617,16 @@ static void commit_charge(struct page *page, struct mem_cgroup *memcg, * In some cases, SwapCache and FUSE(splice_buf->radixtree), the page * may already be on some other mem_cgroup's LRU. Take care of it. */ - if (lrucare) - lock_page_lru(page, &isolated); + if (lrucare) { + lruvec = lock_page_lruvec_irq(page); + if (likely(PageLRU(page))) { + ClearPageLRU(page); + del_page_from_lru_list(page, lruvec, page_lru(page)); + } else { + unlock_page_lruvec_irq(lruvec); + lruvec = NULL; + } + } /* * Nobody should be changing or seriously looking at
@@ -2631,8 +2644,15 @@ static void commit_charge(struct page *page, struct mem_cgroup *memcg, */ page->mem_cgroup = memcg; - if (lrucare) - unlock_page_lru(page, isolated); + if (lrucare && lruvec) { + unlock_page_lruvec_irq(lruvec); + lruvec = lock_page_lruvec_irq(page); + + VM_BUG_ON_PAGE(PageLRU(page), page); + SetPageLRU(page); + add_page_to_lru_list(page, lruvec, page_lru(page)); + unlock_page_lruvec_irq(lruvec); + } }