Skip to content

Commit dcf5aed

Browse files
vwooltorvalds
authored andcommitted
z3fold: stricter locking and more careful reclaim
Use temporary slots in reclaim function to avoid possible race when freeing those. While at it, make sure we check CLAIMED flag under page lock in the reclaim function to make sure we are not racing with z3fold_alloc(). Link: https://lkml.kernel.org/r/20201209145151.18994-4-vitaly.wool@konsulko.com Signed-off-by: Vitaly Wool <vitaly.wool@konsulko.com> Cc: <stable@vger.kernel.org> Cc: Mike Galbraith <efault@gmx.de> Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent fc54886 commit dcf5aed

File tree

1 file changed

+85
-58
lines changed

1 file changed

+85
-58
lines changed

mm/z3fold.c

Lines changed: 85 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,13 @@ enum z3fold_page_flags {
181181
PAGE_CLAIMED, /* by either reclaim or free */
182182
};
183183

184+
/*
185+
* handle flags, go under HANDLE_FLAG_MASK
186+
*/
187+
enum z3fold_handle_flags {
188+
HANDLES_NOFREE = 0,
189+
};
190+
184191
/*
185192
* Forward declarations
186193
*/
@@ -311,6 +318,12 @@ static inline void free_handle(unsigned long handle, struct z3fold_header *zhdr)
311318
slots = handle_to_slots(handle);
312319
write_lock(&slots->lock);
313320
*(unsigned long *)handle = 0;
321+
322+
if (test_bit(HANDLES_NOFREE, &slots->pool)) {
323+
write_unlock(&slots->lock);
324+
return; /* simple case, nothing else to do */
325+
}
326+
314327
if (zhdr->slots != slots)
315328
zhdr->foreign_handles--;
316329

@@ -621,6 +634,28 @@ static inline void add_to_unbuddied(struct z3fold_pool *pool,
621634
}
622635
}
623636

637+
static inline enum buddy get_free_buddy(struct z3fold_header *zhdr, int chunks)
638+
{
639+
enum buddy bud = HEADLESS;
640+
641+
if (zhdr->middle_chunks) {
642+
if (!zhdr->first_chunks &&
643+
chunks <= zhdr->start_middle - ZHDR_CHUNKS)
644+
bud = FIRST;
645+
else if (!zhdr->last_chunks)
646+
bud = LAST;
647+
} else {
648+
if (!zhdr->first_chunks)
649+
bud = FIRST;
650+
else if (!zhdr->last_chunks)
651+
bud = LAST;
652+
else
653+
bud = MIDDLE;
654+
}
655+
656+
return bud;
657+
}
658+
624659
static inline void *mchunk_memmove(struct z3fold_header *zhdr,
625660
unsigned short dst_chunk)
626661
{
@@ -682,18 +717,7 @@ static struct z3fold_header *compact_single_buddy(struct z3fold_header *zhdr)
682717
if (WARN_ON(new_zhdr == zhdr))
683718
goto out_fail;
684719

685-
if (new_zhdr->first_chunks == 0) {
686-
if (new_zhdr->middle_chunks != 0 &&
687-
chunks >= new_zhdr->start_middle) {
688-
new_bud = LAST;
689-
} else {
690-
new_bud = FIRST;
691-
}
692-
} else if (new_zhdr->last_chunks == 0) {
693-
new_bud = LAST;
694-
} else if (new_zhdr->middle_chunks == 0) {
695-
new_bud = MIDDLE;
696-
}
720+
new_bud = get_free_buddy(new_zhdr, chunks);
697721
q = new_zhdr;
698722
switch (new_bud) {
699723
case FIRST:
@@ -815,9 +839,8 @@ static void do_compact_page(struct z3fold_header *zhdr, bool locked)
815839
return;
816840
}
817841

818-
if (unlikely(PageIsolated(page) ||
819-
test_bit(PAGE_CLAIMED, &page->private) ||
820-
test_bit(PAGE_STALE, &page->private))) {
842+
if (test_bit(PAGE_STALE, &page->private) ||
843+
test_and_set_bit(PAGE_CLAIMED, &page->private)) {
821844
z3fold_page_unlock(zhdr);
822845
return;
823846
}
@@ -826,13 +849,16 @@ static void do_compact_page(struct z3fold_header *zhdr, bool locked)
826849
zhdr->mapped_count == 0 && compact_single_buddy(zhdr)) {
827850
if (kref_put(&zhdr->refcount, release_z3fold_page_locked))
828851
atomic64_dec(&pool->pages_nr);
829-
else
852+
else {
853+
clear_bit(PAGE_CLAIMED, &page->private);
830854
z3fold_page_unlock(zhdr);
855+
}
831856
return;
832857
}
833858

834859
z3fold_compact_page(zhdr);
835860
add_to_unbuddied(pool, zhdr);
861+
clear_bit(PAGE_CLAIMED, &page->private);
836862
z3fold_page_unlock(zhdr);
837863
}
838864

@@ -1080,17 +1106,8 @@ static int z3fold_alloc(struct z3fold_pool *pool, size_t size, gfp_t gfp,
10801106
retry:
10811107
zhdr = __z3fold_alloc(pool, size, can_sleep);
10821108
if (zhdr) {
1083-
if (zhdr->first_chunks == 0) {
1084-
if (zhdr->middle_chunks != 0 &&
1085-
chunks >= zhdr->start_middle)
1086-
bud = LAST;
1087-
else
1088-
bud = FIRST;
1089-
} else if (zhdr->last_chunks == 0)
1090-
bud = LAST;
1091-
else if (zhdr->middle_chunks == 0)
1092-
bud = MIDDLE;
1093-
else {
1109+
bud = get_free_buddy(zhdr, chunks);
1110+
if (bud == HEADLESS) {
10941111
if (kref_put(&zhdr->refcount,
10951112
release_z3fold_page_locked))
10961113
atomic64_dec(&pool->pages_nr);
@@ -1236,7 +1253,6 @@ static void z3fold_free(struct z3fold_pool *pool, unsigned long handle)
12361253
pr_err("%s: unknown bud %d\n", __func__, bud);
12371254
WARN_ON(1);
12381255
put_z3fold_header(zhdr);
1239-
clear_bit(PAGE_CLAIMED, &page->private);
12401256
return;
12411257
}
12421258

@@ -1251,8 +1267,7 @@ static void z3fold_free(struct z3fold_pool *pool, unsigned long handle)
12511267
z3fold_page_unlock(zhdr);
12521268
return;
12531269
}
1254-
if (unlikely(PageIsolated(page)) ||
1255-
test_and_set_bit(NEEDS_COMPACTING, &page->private)) {
1270+
if (test_and_set_bit(NEEDS_COMPACTING, &page->private)) {
12561271
put_z3fold_header(zhdr);
12571272
clear_bit(PAGE_CLAIMED, &page->private);
12581273
return;
@@ -1316,6 +1331,10 @@ static int z3fold_reclaim_page(struct z3fold_pool *pool, unsigned int retries)
13161331
struct page *page = NULL;
13171332
struct list_head *pos;
13181333
unsigned long first_handle = 0, middle_handle = 0, last_handle = 0;
1334+
struct z3fold_buddy_slots slots __attribute__((aligned(SLOTS_ALIGN)));
1335+
1336+
rwlock_init(&slots.lock);
1337+
slots.pool = (unsigned long)pool | (1 << HANDLES_NOFREE);
13191338

13201339
spin_lock(&pool->lock);
13211340
if (!pool->ops || !pool->ops->evict || retries == 0) {
@@ -1330,35 +1349,36 @@ static int z3fold_reclaim_page(struct z3fold_pool *pool, unsigned int retries)
13301349
list_for_each_prev(pos, &pool->lru) {
13311350
page = list_entry(pos, struct page, lru);
13321351

1333-
/* this bit could have been set by free, in which case
1334-
* we pass over to the next page in the pool.
1335-
*/
1336-
if (test_and_set_bit(PAGE_CLAIMED, &page->private)) {
1337-
page = NULL;
1338-
continue;
1339-
}
1340-
1341-
if (unlikely(PageIsolated(page))) {
1342-
clear_bit(PAGE_CLAIMED, &page->private);
1343-
page = NULL;
1344-
continue;
1345-
}
13461352
zhdr = page_address(page);
13471353
if (test_bit(PAGE_HEADLESS, &page->private))
13481354
break;
13491355

1356+
if (kref_get_unless_zero(&zhdr->refcount) == 0) {
1357+
zhdr = NULL;
1358+
break;
1359+
}
13501360
if (!z3fold_page_trylock(zhdr)) {
1351-
clear_bit(PAGE_CLAIMED, &page->private);
1361+
if (kref_put(&zhdr->refcount,
1362+
release_z3fold_page))
1363+
atomic64_dec(&pool->pages_nr);
13521364
zhdr = NULL;
13531365
continue; /* can't evict at this point */
13541366
}
1355-
if (zhdr->foreign_handles) {
1356-
clear_bit(PAGE_CLAIMED, &page->private);
1357-
z3fold_page_unlock(zhdr);
1367+
1368+
/* test_and_set_bit is of course atomic, but we still
1369+
* need to do it under page lock, otherwise checking
1370+
* that bit in __z3fold_alloc wouldn't make sense
1371+
*/
1372+
if (zhdr->foreign_handles ||
1373+
test_and_set_bit(PAGE_CLAIMED, &page->private)) {
1374+
if (kref_put(&zhdr->refcount,
1375+
release_z3fold_page))
1376+
atomic64_dec(&pool->pages_nr);
1377+
else
1378+
z3fold_page_unlock(zhdr);
13581379
zhdr = NULL;
13591380
continue; /* can't evict such page */
13601381
}
1361-
kref_get(&zhdr->refcount);
13621382
list_del_init(&zhdr->buddy);
13631383
zhdr->cpu = -1;
13641384
break;
@@ -1380,12 +1400,16 @@ static int z3fold_reclaim_page(struct z3fold_pool *pool, unsigned int retries)
13801400
first_handle = 0;
13811401
last_handle = 0;
13821402
middle_handle = 0;
1403+
memset(slots.slot, 0, sizeof(slots.slot));
13831404
if (zhdr->first_chunks)
1384-
first_handle = encode_handle(zhdr, FIRST);
1405+
first_handle = __encode_handle(zhdr, &slots,
1406+
FIRST);
13851407
if (zhdr->middle_chunks)
1386-
middle_handle = encode_handle(zhdr, MIDDLE);
1408+
middle_handle = __encode_handle(zhdr, &slots,
1409+
MIDDLE);
13871410
if (zhdr->last_chunks)
1388-
last_handle = encode_handle(zhdr, LAST);
1411+
last_handle = __encode_handle(zhdr, &slots,
1412+
LAST);
13891413
/*
13901414
* it's safe to unlock here because we hold a
13911415
* reference to this page
@@ -1400,19 +1424,16 @@ static int z3fold_reclaim_page(struct z3fold_pool *pool, unsigned int retries)
14001424
ret = pool->ops->evict(pool, middle_handle);
14011425
if (ret)
14021426
goto next;
1403-
free_handle(middle_handle, zhdr);
14041427
}
14051428
if (first_handle) {
14061429
ret = pool->ops->evict(pool, first_handle);
14071430
if (ret)
14081431
goto next;
1409-
free_handle(first_handle, zhdr);
14101432
}
14111433
if (last_handle) {
14121434
ret = pool->ops->evict(pool, last_handle);
14131435
if (ret)
14141436
goto next;
1415-
free_handle(last_handle, zhdr);
14161437
}
14171438
next:
14181439
if (test_bit(PAGE_HEADLESS, &page->private)) {
@@ -1426,9 +1447,11 @@ static int z3fold_reclaim_page(struct z3fold_pool *pool, unsigned int retries)
14261447
spin_unlock(&pool->lock);
14271448
clear_bit(PAGE_CLAIMED, &page->private);
14281449
} else {
1450+
struct z3fold_buddy_slots *slots = zhdr->slots;
14291451
z3fold_page_lock(zhdr);
14301452
if (kref_put(&zhdr->refcount,
14311453
release_z3fold_page_locked)) {
1454+
kmem_cache_free(pool->c_handle, slots);
14321455
atomic64_dec(&pool->pages_nr);
14331456
return 0;
14341457
}
@@ -1544,8 +1567,7 @@ static bool z3fold_page_isolate(struct page *page, isolate_mode_t mode)
15441567
VM_BUG_ON_PAGE(!PageMovable(page), page);
15451568
VM_BUG_ON_PAGE(PageIsolated(page), page);
15461569

1547-
if (test_bit(PAGE_HEADLESS, &page->private) ||
1548-
test_bit(PAGE_CLAIMED, &page->private))
1570+
if (test_bit(PAGE_HEADLESS, &page->private))
15491571
return false;
15501572

15511573
zhdr = page_address(page);
@@ -1557,6 +1579,8 @@ static bool z3fold_page_isolate(struct page *page, isolate_mode_t mode)
15571579
if (zhdr->mapped_count != 0 || zhdr->foreign_handles != 0)
15581580
goto out;
15591581

1582+
if (test_and_set_bit(PAGE_CLAIMED, &page->private))
1583+
goto out;
15601584
pool = zhdr_to_pool(zhdr);
15611585
spin_lock(&pool->lock);
15621586
if (!list_empty(&zhdr->buddy))
@@ -1583,16 +1607,17 @@ static int z3fold_page_migrate(struct address_space *mapping, struct page *newpa
15831607

15841608
VM_BUG_ON_PAGE(!PageMovable(page), page);
15851609
VM_BUG_ON_PAGE(!PageIsolated(page), page);
1610+
VM_BUG_ON_PAGE(!test_bit(PAGE_CLAIMED, &page->private), page);
15861611
VM_BUG_ON_PAGE(!PageLocked(newpage), newpage);
15871612

15881613
zhdr = page_address(page);
15891614
pool = zhdr_to_pool(zhdr);
15901615

1591-
if (!z3fold_page_trylock(zhdr)) {
1616+
if (!z3fold_page_trylock(zhdr))
15921617
return -EAGAIN;
1593-
}
15941618
if (zhdr->mapped_count != 0 || zhdr->foreign_handles != 0) {
15951619
z3fold_page_unlock(zhdr);
1620+
clear_bit(PAGE_CLAIMED, &page->private);
15961621
return -EBUSY;
15971622
}
15981623
if (work_pending(&zhdr->work)) {
@@ -1634,6 +1659,7 @@ static int z3fold_page_migrate(struct address_space *mapping, struct page *newpa
16341659
queue_work_on(new_zhdr->cpu, pool->compact_wq, &new_zhdr->work);
16351660

16361661
page_mapcount_reset(page);
1662+
clear_bit(PAGE_CLAIMED, &page->private);
16371663
put_page(page);
16381664
return 0;
16391665
}
@@ -1657,6 +1683,7 @@ static void z3fold_page_putback(struct page *page)
16571683
spin_lock(&pool->lock);
16581684
list_add(&page->lru, &pool->lru);
16591685
spin_unlock(&pool->lock);
1686+
clear_bit(PAGE_CLAIMED, &page->private);
16601687
z3fold_page_unlock(zhdr);
16611688
}
16621689

0 commit comments

Comments
 (0)