Skip to content

Commit 90d0721

Browse files
lorenzo-stoakesakpm00
authored andcommitted
mm: mlock: use folios and a folio batch internally
This brings mlock in line with the folio batches declared in mm/swap.c and makes the code more consistent across the two. The existing mechanism for identifying which operation each folio in the batch is undergoing is maintained, i.e. using the lower 2 bits of the struct folio address (previously struct page address). This should continue to function correctly as folios remain at least system word-aligned. All invocations of mlock() pass either a non-compound page or the head of a THP-compound page and no tail pages need updating so this functionality works with struct folios being used internally rather than struct pages. In this patch the external interface is kept identical to before in order to maintain separation between patches in the series, using a rather awkward conversion from struct page to struct folio in relevant functions. However, this maintenance of the existing interface is intended to be temporary - the next patch in the series will update the interfaces to accept folios directly. Link: https://lkml.kernel.org/r/9f894d54d568773f4ed3cb0eef5f8932f62c95f4.1673526881.git.lstoakes@gmail.com Signed-off-by: Lorenzo Stoakes <[email protected]> Acked-by: Vlastimil Babka <[email protected]> Cc: Christian Brauner <[email protected]> Cc: Geert Uytterhoeven <[email protected]> Cc: Hugh Dickins <[email protected]> Cc: Joel Fernandes (Google) <[email protected]> Cc: Jonathan Corbet <[email protected]> Cc: Liam R. Howlett <[email protected]> Cc: Matthew Wilcox <[email protected]> Cc: Mike Rapoport (IBM) <[email protected]> Cc: William Kucharski <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 8115612 commit 90d0721

File tree

1 file changed

+124
-122
lines changed

1 file changed

+124
-122
lines changed

mm/mlock.c

Lines changed: 124 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@
2828

2929
#include "internal.h"
3030

31-
struct mlock_pvec {
31+
struct mlock_fbatch {
3232
local_lock_t lock;
33-
struct pagevec vec;
33+
struct folio_batch fbatch;
3434
};
3535

36-
static DEFINE_PER_CPU(struct mlock_pvec, mlock_pvec) = {
36+
static DEFINE_PER_CPU(struct mlock_fbatch, mlock_fbatch) = {
3737
.lock = INIT_LOCAL_LOCK(lock),
3838
};
3939

@@ -48,192 +48,192 @@ bool can_do_mlock(void)
4848
EXPORT_SYMBOL(can_do_mlock);
4949

5050
/*
51-
* Mlocked pages are marked with PageMlocked() flag for efficient testing
51+
* Mlocked folios are marked with the PG_mlocked flag for efficient testing
5252
* in vmscan and, possibly, the fault path; and to support semi-accurate
5353
* statistics.
5454
*
55-
* An mlocked page [PageMlocked(page)] is unevictable. As such, it will
56-
* be placed on the LRU "unevictable" list, rather than the [in]active lists.
57-
* The unevictable list is an LRU sibling list to the [in]active lists.
58-
* PageUnevictable is set to indicate the unevictable state.
55+
* An mlocked folio [folio_test_mlocked(folio)] is unevictable. As such, it
56+
* will be ostensibly placed on the LRU "unevictable" list (actually no such
57+
* list exists), rather than the [in]active lists. PG_unevictable is set to
58+
* indicate the unevictable state.
5959
*/
6060

61-
static struct lruvec *__mlock_page(struct page *page, struct lruvec *lruvec)
61+
static struct lruvec *__mlock_folio(struct folio *folio, struct lruvec *lruvec)
6262
{
6363
/* There is nothing more we can do while it's off LRU */
64-
if (!TestClearPageLRU(page))
64+
if (!folio_test_clear_lru(folio))
6565
return lruvec;
6666

67-
lruvec = folio_lruvec_relock_irq(page_folio(page), lruvec);
67+
lruvec = folio_lruvec_relock_irq(folio, lruvec);
6868

69-
if (unlikely(page_evictable(page))) {
69+
if (unlikely(folio_evictable(folio))) {
7070
/*
71-
* This is a little surprising, but quite possible:
72-
* PageMlocked must have got cleared already by another CPU.
73-
* Could this page be on the Unevictable LRU? I'm not sure,
74-
* but move it now if so.
71+
* This is a little surprising, but quite possible: PG_mlocked
72+
* must have got cleared already by another CPU. Could this
73+
* folio be unevictable? I'm not sure, but move it now if so.
7574
*/
76-
if (PageUnevictable(page)) {
77-
del_page_from_lru_list(page, lruvec);
78-
ClearPageUnevictable(page);
79-
add_page_to_lru_list(page, lruvec);
75+
if (folio_test_unevictable(folio)) {
76+
lruvec_del_folio(lruvec, folio);
77+
folio_clear_unevictable(folio);
78+
lruvec_add_folio(lruvec, folio);
79+
8080
__count_vm_events(UNEVICTABLE_PGRESCUED,
81-
thp_nr_pages(page));
81+
folio_nr_pages(folio));
8282
}
8383
goto out;
8484
}
8585

86-
if (PageUnevictable(page)) {
87-
if (PageMlocked(page))
88-
page->mlock_count++;
86+
if (folio_test_unevictable(folio)) {
87+
if (folio_test_mlocked(folio))
88+
folio->mlock_count++;
8989
goto out;
9090
}
9191

92-
del_page_from_lru_list(page, lruvec);
93-
ClearPageActive(page);
94-
SetPageUnevictable(page);
95-
page->mlock_count = !!PageMlocked(page);
96-
add_page_to_lru_list(page, lruvec);
97-
__count_vm_events(UNEVICTABLE_PGCULLED, thp_nr_pages(page));
92+
lruvec_del_folio(lruvec, folio);
93+
folio_clear_active(folio);
94+
folio_set_unevictable(folio);
95+
folio->mlock_count = !!folio_test_mlocked(folio);
96+
lruvec_add_folio(lruvec, folio);
97+
__count_vm_events(UNEVICTABLE_PGCULLED, folio_nr_pages(folio));
9898
out:
99-
SetPageLRU(page);
99+
folio_set_lru(folio);
100100
return lruvec;
101101
}
102102

103-
static struct lruvec *__mlock_new_page(struct page *page, struct lruvec *lruvec)
103+
static struct lruvec *__mlock_new_folio(struct folio *folio, struct lruvec *lruvec)
104104
{
105-
VM_BUG_ON_PAGE(PageLRU(page), page);
105+
VM_BUG_ON_FOLIO(folio_test_lru(folio), folio);
106106

107-
lruvec = folio_lruvec_relock_irq(page_folio(page), lruvec);
107+
lruvec = folio_lruvec_relock_irq(folio, lruvec);
108108

109109
/* As above, this is a little surprising, but possible */
110-
if (unlikely(page_evictable(page)))
110+
if (unlikely(folio_evictable(folio)))
111111
goto out;
112112

113-
SetPageUnevictable(page);
114-
page->mlock_count = !!PageMlocked(page);
115-
__count_vm_events(UNEVICTABLE_PGCULLED, thp_nr_pages(page));
113+
folio_set_unevictable(folio);
114+
folio->mlock_count = !!folio_test_mlocked(folio);
115+
__count_vm_events(UNEVICTABLE_PGCULLED, folio_nr_pages(folio));
116116
out:
117-
add_page_to_lru_list(page, lruvec);
118-
SetPageLRU(page);
117+
lruvec_add_folio(lruvec, folio);
118+
folio_set_lru(folio);
119119
return lruvec;
120120
}
121121

122-
static struct lruvec *__munlock_page(struct page *page, struct lruvec *lruvec)
122+
static struct lruvec *__munlock_folio(struct folio *folio, struct lruvec *lruvec)
123123
{
124-
int nr_pages = thp_nr_pages(page);
124+
int nr_pages = folio_nr_pages(folio);
125125
bool isolated = false;
126126

127-
if (!TestClearPageLRU(page))
127+
if (!folio_test_clear_lru(folio))
128128
goto munlock;
129129

130130
isolated = true;
131-
lruvec = folio_lruvec_relock_irq(page_folio(page), lruvec);
131+
lruvec = folio_lruvec_relock_irq(folio, lruvec);
132132

133-
if (PageUnevictable(page)) {
133+
if (folio_test_unevictable(folio)) {
134134
/* Then mlock_count is maintained, but might undercount */
135-
if (page->mlock_count)
136-
page->mlock_count--;
137-
if (page->mlock_count)
135+
if (folio->mlock_count)
136+
folio->mlock_count--;
137+
if (folio->mlock_count)
138138
goto out;
139139
}
140140
/* else assume that was the last mlock: reclaim will fix it if not */
141141

142142
munlock:
143-
if (TestClearPageMlocked(page)) {
144-
__mod_zone_page_state(page_zone(page), NR_MLOCK, -nr_pages);
145-
if (isolated || !PageUnevictable(page))
143+
if (folio_test_clear_mlocked(folio)) {
144+
__zone_stat_mod_folio(folio, NR_MLOCK, -nr_pages);
145+
if (isolated || !folio_test_unevictable(folio))
146146
__count_vm_events(UNEVICTABLE_PGMUNLOCKED, nr_pages);
147147
else
148148
__count_vm_events(UNEVICTABLE_PGSTRANDED, nr_pages);
149149
}
150150

151-
/* page_evictable() has to be checked *after* clearing Mlocked */
152-
if (isolated && PageUnevictable(page) && page_evictable(page)) {
153-
del_page_from_lru_list(page, lruvec);
154-
ClearPageUnevictable(page);
155-
add_page_to_lru_list(page, lruvec);
151+
/* folio_evictable() has to be checked *after* clearing Mlocked */
152+
if (isolated && folio_test_unevictable(folio) && folio_evictable(folio)) {
153+
lruvec_del_folio(lruvec, folio);
154+
folio_clear_unevictable(folio);
155+
lruvec_add_folio(lruvec, folio);
156156
__count_vm_events(UNEVICTABLE_PGRESCUED, nr_pages);
157157
}
158158
out:
159159
if (isolated)
160-
SetPageLRU(page);
160+
folio_set_lru(folio);
161161
return lruvec;
162162
}
163163

164164
/*
165-
* Flags held in the low bits of a struct page pointer on the mlock_pvec.
165+
* Flags held in the low bits of a struct folio pointer on the mlock_fbatch.
166166
*/
167-
#define LRU_PAGE 0x1
168-
#define NEW_PAGE 0x2
169-
static inline struct page *mlock_lru(struct page *page)
167+
#define LRU_FOLIO 0x1
168+
#define NEW_FOLIO 0x2
169+
static inline struct folio *mlock_lru(struct folio *folio)
170170
{
171-
return (struct page *)((unsigned long)page + LRU_PAGE);
171+
return (struct folio *)((unsigned long)folio + LRU_FOLIO);
172172
}
173173

174-
static inline struct page *mlock_new(struct page *page)
174+
static inline struct folio *mlock_new(struct folio *folio)
175175
{
176-
return (struct page *)((unsigned long)page + NEW_PAGE);
176+
return (struct folio *)((unsigned long)folio + NEW_FOLIO);
177177
}
178178

179179
/*
180-
* mlock_pagevec() is derived from pagevec_lru_move_fn():
181-
* perhaps that can make use of such page pointer flags in future,
182-
* but for now just keep it for mlock. We could use three separate
183-
* pagevecs instead, but one feels better (munlocking a full pagevec
184-
* does not need to drain mlocking pagevecs first).
180+
* mlock_folio_batch() is derived from folio_batch_move_lru(): perhaps that can
181+
* make use of such folio pointer flags in future, but for now just keep it for
182+
* mlock. We could use three separate folio batches instead, but one feels
183+
* better (munlocking a full folio batch does not need to drain mlocking folio
184+
* batches first).
185185
*/
186-
static void mlock_pagevec(struct pagevec *pvec)
186+
static void mlock_folio_batch(struct folio_batch *fbatch)
187187
{
188188
struct lruvec *lruvec = NULL;
189189
unsigned long mlock;
190-
struct page *page;
190+
struct folio *folio;
191191
int i;
192192

193-
for (i = 0; i < pagevec_count(pvec); i++) {
194-
page = pvec->pages[i];
195-
mlock = (unsigned long)page & (LRU_PAGE | NEW_PAGE);
196-
page = (struct page *)((unsigned long)page - mlock);
197-
pvec->pages[i] = page;
193+
for (i = 0; i < folio_batch_count(fbatch); i++) {
194+
folio = fbatch->folios[i];
195+
mlock = (unsigned long)folio & (LRU_FOLIO | NEW_FOLIO);
196+
folio = (struct folio *)((unsigned long)folio - mlock);
197+
fbatch->folios[i] = folio;
198198

199-
if (mlock & LRU_PAGE)
200-
lruvec = __mlock_page(page, lruvec);
201-
else if (mlock & NEW_PAGE)
202-
lruvec = __mlock_new_page(page, lruvec);
199+
if (mlock & LRU_FOLIO)
200+
lruvec = __mlock_folio(folio, lruvec);
201+
else if (mlock & NEW_FOLIO)
202+
lruvec = __mlock_new_folio(folio, lruvec);
203203
else
204-
lruvec = __munlock_page(page, lruvec);
204+
lruvec = __munlock_folio(folio, lruvec);
205205
}
206206

207207
if (lruvec)
208208
unlock_page_lruvec_irq(lruvec);
209-
release_pages(pvec->pages, pvec->nr);
210-
pagevec_reinit(pvec);
209+
release_pages(fbatch->folios, fbatch->nr);
210+
folio_batch_reinit(fbatch);
211211
}
212212

213213
void mlock_page_drain_local(void)
214214
{
215-
struct pagevec *pvec;
215+
struct folio_batch *fbatch;
216216

217-
local_lock(&mlock_pvec.lock);
218-
pvec = this_cpu_ptr(&mlock_pvec.vec);
219-
if (pagevec_count(pvec))
220-
mlock_pagevec(pvec);
221-
local_unlock(&mlock_pvec.lock);
217+
local_lock(&mlock_fbatch.lock);
218+
fbatch = this_cpu_ptr(&mlock_fbatch.fbatch);
219+
if (folio_batch_count(fbatch))
220+
mlock_folio_batch(fbatch);
221+
local_unlock(&mlock_fbatch.lock);
222222
}
223223

224224
void mlock_page_drain_remote(int cpu)
225225
{
226-
struct pagevec *pvec;
226+
struct folio_batch *fbatch;
227227

228228
WARN_ON_ONCE(cpu_online(cpu));
229-
pvec = &per_cpu(mlock_pvec.vec, cpu);
230-
if (pagevec_count(pvec))
231-
mlock_pagevec(pvec);
229+
fbatch = &per_cpu(mlock_fbatch.fbatch, cpu);
230+
if (folio_batch_count(fbatch))
231+
mlock_folio_batch(fbatch);
232232
}
233233

234234
bool need_mlock_page_drain(int cpu)
235235
{
236-
return pagevec_count(&per_cpu(mlock_pvec.vec, cpu));
236+
return folio_batch_count(&per_cpu(mlock_fbatch.fbatch, cpu));
237237
}
238238

239239
/**
@@ -242,10 +242,10 @@ bool need_mlock_page_drain(int cpu)
242242
*/
243243
void mlock_folio(struct folio *folio)
244244
{
245-
struct pagevec *pvec;
245+
struct folio_batch *fbatch;
246246

247-
local_lock(&mlock_pvec.lock);
248-
pvec = this_cpu_ptr(&mlock_pvec.vec);
247+
local_lock(&mlock_fbatch.lock);
248+
fbatch = this_cpu_ptr(&mlock_fbatch.fbatch);
249249

250250
if (!folio_test_set_mlocked(folio)) {
251251
int nr_pages = folio_nr_pages(folio);
@@ -255,10 +255,10 @@ void mlock_folio(struct folio *folio)
255255
}
256256

257257
folio_get(folio);
258-
if (!pagevec_add(pvec, mlock_lru(&folio->page)) ||
258+
if (!folio_batch_add(fbatch, mlock_lru(folio)) ||
259259
folio_test_large(folio) || lru_cache_disabled())
260-
mlock_pagevec(pvec);
261-
local_unlock(&mlock_pvec.lock);
260+
mlock_folio_batch(fbatch);
261+
local_unlock(&mlock_fbatch.lock);
262262
}
263263

264264
/**
@@ -267,20 +267,22 @@ void mlock_folio(struct folio *folio)
267267
*/
268268
void mlock_new_page(struct page *page)
269269
{
270-
struct pagevec *pvec;
271-
int nr_pages = thp_nr_pages(page);
270+
struct folio_batch *fbatch;
271+
struct folio *folio = page_folio(page);
272+
int nr_pages = folio_nr_pages(folio);
272273

273-
local_lock(&mlock_pvec.lock);
274-
pvec = this_cpu_ptr(&mlock_pvec.vec);
275-
SetPageMlocked(page);
276-
mod_zone_page_state(page_zone(page), NR_MLOCK, nr_pages);
274+
local_lock(&mlock_fbatch.lock);
275+
fbatch = this_cpu_ptr(&mlock_fbatch.fbatch);
276+
folio_set_mlocked(folio);
277+
278+
zone_stat_mod_folio(folio, NR_MLOCK, nr_pages);
277279
__count_vm_events(UNEVICTABLE_PGMLOCKED, nr_pages);
278280

279-
get_page(page);
280-
if (!pagevec_add(pvec, mlock_new(page)) ||
281-
PageHead(page) || lru_cache_disabled())
282-
mlock_pagevec(pvec);
283-
local_unlock(&mlock_pvec.lock);
281+
folio_get(folio);
282+
if (!folio_batch_add(fbatch, mlock_new(folio)) ||
283+
folio_test_large(folio) || lru_cache_disabled())
284+
mlock_folio_batch(fbatch);
285+
local_unlock(&mlock_fbatch.lock);
284286
}
285287

286288
/**
@@ -289,20 +291,20 @@ void mlock_new_page(struct page *page)
289291
*/
290292
void munlock_page(struct page *page)
291293
{
292-
struct pagevec *pvec;
294+
struct folio_batch *fbatch;
295+
struct folio *folio = page_folio(page);
293296

294-
local_lock(&mlock_pvec.lock);
295-
pvec = this_cpu_ptr(&mlock_pvec.vec);
297+
local_lock(&mlock_fbatch.lock);
298+
fbatch = this_cpu_ptr(&mlock_fbatch.fbatch);
296299
/*
297-
* TestClearPageMlocked(page) must be left to __munlock_page(),
298-
* which will check whether the page is multiply mlocked.
300+
* folio_test_clear_mlocked(folio) must be left to __munlock_folio(),
301+
* which will check whether the folio is multiply mlocked.
299302
*/
300-
301-
get_page(page);
302-
if (!pagevec_add(pvec, page) ||
303-
PageHead(page) || lru_cache_disabled())
304-
mlock_pagevec(pvec);
305-
local_unlock(&mlock_pvec.lock);
303+
folio_get(folio);
304+
if (!folio_batch_add(fbatch, folio) ||
305+
folio_test_large(folio) || lru_cache_disabled())
306+
mlock_folio_batch(fbatch);
307+
local_unlock(&mlock_fbatch.lock);
306308
}
307309

308310
static int mlock_pte_range(pmd_t *pmd, unsigned long addr,

0 commit comments

Comments
 (0)