On Fri, 11 Feb 2022, Vlastimil Babka wrote: > On 2/6/22 22:40, Hugh Dickins wrote: > > @@ -72,19 +91,40 @@ void mlock_page(struct page *page) > > */ > > void munlock_page(struct page *page) > > { > > + struct lruvec *lruvec; > > + int nr_pages = thp_nr_pages(page); > > + > > VM_BUG_ON_PAGE(PageTail(page), page); > > > > + lock_page_memcg(page); > > Hm this (and unlock_page_memcg() below) didn't catch my attention until I > see patch 10/13 removes it again. It also AFAICS wasn't present in the code > removed by patch 1. Am I missing something or it wasn't necessary to add it > in the first place? Something is needed to stabilize page->memcg, whoops I'm way out of date, folio->memcg_data, before trying to get the lock on the relevant lruvec. In commit_charge(), Johannes gives us a choice between four tools: * - the page lock * - LRU isolation * - lock_page_memcg() * - exclusive reference The original code was using TestClearPageLRU inside isolate_lru_page() to do it (also happened to have the page lock, but one tool is enough). But I chose to use lock_page_memcg() at this stage, because we want to do the TestClearPageMlocked part of the business even when !PageLRU; and I don't entirely love the TestClearPageLRU method, since one will fail if two try it concurrently. Later, when doing the pagevec implementation, it seemed to become more natural to use the TestClearPageLRU method - because that's how pagevec_lru_move_fn() does it, or did I have a stronger reason for making a different choice at that stage? Maybe: I might have been trying to keep the different functions as similar as possible. But really we have too many ways to do that same thing, and my choices may have been arbitrary, according to mood. (When Alex Shi popularized the TestClearPageLRU method, I did devise a patch which used the lock_page_memcg() method throughout instead; but it was not obviously better, so I didn't waste anyone else's time with it.) I'm afraid that looking here again has led me to wonder whether __munlock_page() in the final (10/13 pagevec) implementaton is correct to be using __operations in its !isolated case. But I'll have to come back and think about that another time, must push forward tonight. Hugh > > > + lruvec = folio_lruvec_lock_irq(page_folio(page)); > > + if (PageLRU(page) && PageUnevictable(page)) { > > + /* Then mlock_count is maintained, but might undercount */ > > + if (page->mlock_count) > > + page->mlock_count--; > > + if (page->mlock_count) > > + goto out; > > + } > > + /* else assume that was the last mlock: reclaim will fix it if not */ > > + > > if (TestClearPageMlocked(page)) { > > - int nr_pages = thp_nr_pages(page); > > - > > - mod_zone_page_state(page_zone(page), NR_MLOCK, -nr_pages); > > - if (!isolate_lru_page(page)) { > > - putback_lru_page(page); > > - count_vm_events(UNEVICTABLE_PGMUNLOCKED, nr_pages); > > - } else if (PageUnevictable(page)) { > > - count_vm_events(UNEVICTABLE_PGSTRANDED, nr_pages); > > - } > > + __mod_zone_page_state(page_zone(page), NR_MLOCK, -nr_pages); > > + if (PageLRU(page) || !PageUnevictable(page)) > > + __count_vm_events(UNEVICTABLE_PGMUNLOCKED, nr_pages); > > + else > > + __count_vm_events(UNEVICTABLE_PGSTRANDED, nr_pages); > > + } > > + > > + /* page_evictable() has to be checked *after* clearing Mlocked */ > > + if (PageLRU(page) && PageUnevictable(page) && page_evictable(page)) { > > + del_page_from_lru_list(page, lruvec); > > + ClearPageUnevictable(page); > > + add_page_to_lru_list(page, lruvec); > > + __count_vm_events(UNEVICTABLE_PGRESCUED, nr_pages); > > } > > +out: > > + unlock_page_lruvec_irq(lruvec); > > + unlock_page_memcg(page); > > } > > > > /*