Skip to content

Commit 2f1e5e4

Browse files
vwooltorvalds
authored andcommitted
z3fold: use per-page spinlock
Most of z3fold operations are in-page, such as modifying z3fold page header or moving z3fold objects within a page. Taking per-pool spinlock to protect per-page objects is therefore suboptimal, and the idea of having a per-page spinlock (or rwlock) has been around for some time. This patch implements spinlock-based per-page locking mechanism which is lightweight enough to normally fit ok into the z3fold header. Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Vitaly Wool <[email protected]> Reviewed-by: Dan Streetman <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 1b096e5 commit 2f1e5e4

File tree

1 file changed

+106
-42
lines changed

1 file changed

+106
-42
lines changed

mm/z3fold.c

Lines changed: 106 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,15 @@ enum buddy {
5151
* struct z3fold_header - z3fold page metadata occupying the first chunk of each
5252
* z3fold page, except for HEADLESS pages
5353
* @buddy: links the z3fold page into the relevant list in the pool
54+
* @page_lock: per-page lock
5455
* @first_chunks: the size of the first buddy in chunks, 0 if free
5556
* @middle_chunks: the size of the middle buddy in chunks, 0 if free
5657
* @last_chunks: the size of the last buddy in chunks, 0 if free
5758
* @first_num: the starting number (for the first handle)
5859
*/
5960
struct z3fold_header {
6061
struct list_head buddy;
62+
spinlock_t page_lock;
6163
unsigned short first_chunks;
6264
unsigned short middle_chunks;
6365
unsigned short last_chunks;
@@ -148,6 +150,7 @@ static struct z3fold_header *init_z3fold_page(struct page *page)
148150
clear_bit(PAGE_HEADLESS, &page->private);
149151
clear_bit(MIDDLE_CHUNK_MAPPED, &page->private);
150152

153+
spin_lock_init(&zhdr->page_lock);
151154
zhdr->first_chunks = 0;
152155
zhdr->middle_chunks = 0;
153156
zhdr->last_chunks = 0;
@@ -163,6 +166,19 @@ static void free_z3fold_page(struct z3fold_header *zhdr)
163166
__free_page(virt_to_page(zhdr));
164167
}
165168

169+
/* Lock a z3fold page */
170+
static inline void z3fold_page_lock(struct z3fold_header *zhdr)
171+
{
172+
spin_lock(&zhdr->page_lock);
173+
}
174+
175+
/* Unlock a z3fold page */
176+
static inline void z3fold_page_unlock(struct z3fold_header *zhdr)
177+
{
178+
spin_unlock(&zhdr->page_lock);
179+
}
180+
181+
166182
/*
167183
* Encodes the handle of a particular buddy within a z3fold page
168184
* Pool lock should be held as this function accesses first_num
@@ -351,50 +367,60 @@ static int z3fold_alloc(struct z3fold_pool *pool, size_t size, gfp_t gfp,
351367
bud = HEADLESS;
352368
else {
353369
chunks = size_to_chunks(size);
354-
spin_lock(&pool->lock);
355370

356371
/* First, try to find an unbuddied z3fold page. */
357372
zhdr = NULL;
358373
for_each_unbuddied_list(i, chunks) {
359-
if (!list_empty(&pool->unbuddied[i])) {
360-
zhdr = list_first_entry(&pool->unbuddied[i],
374+
spin_lock(&pool->lock);
375+
zhdr = list_first_entry_or_null(&pool->unbuddied[i],
361376
struct z3fold_header, buddy);
362-
page = virt_to_page(zhdr);
363-
if (zhdr->first_chunks == 0) {
364-
if (zhdr->middle_chunks != 0 &&
365-
chunks >= zhdr->start_middle)
366-
bud = LAST;
367-
else
368-
bud = FIRST;
369-
} else if (zhdr->last_chunks == 0)
377+
if (!zhdr) {
378+
spin_unlock(&pool->lock);
379+
continue;
380+
}
381+
list_del_init(&zhdr->buddy);
382+
spin_unlock(&pool->lock);
383+
384+
page = virt_to_page(zhdr);
385+
z3fold_page_lock(zhdr);
386+
if (zhdr->first_chunks == 0) {
387+
if (zhdr->middle_chunks != 0 &&
388+
chunks >= zhdr->start_middle)
370389
bud = LAST;
371-
else if (zhdr->middle_chunks == 0)
372-
bud = MIDDLE;
373-
else {
374-
pr_err("No free chunks in unbuddied\n");
375-
WARN_ON(1);
376-
continue;
377-
}
378-
list_del(&zhdr->buddy);
379-
goto found;
390+
else
391+
bud = FIRST;
392+
} else if (zhdr->last_chunks == 0)
393+
bud = LAST;
394+
else if (zhdr->middle_chunks == 0)
395+
bud = MIDDLE;
396+
else {
397+
spin_lock(&pool->lock);
398+
list_add(&zhdr->buddy, &pool->buddied);
399+
spin_unlock(&pool->lock);
400+
z3fold_page_unlock(zhdr);
401+
pr_err("No free chunks in unbuddied\n");
402+
WARN_ON(1);
403+
continue;
380404
}
405+
goto found;
381406
}
382407
bud = FIRST;
383-
spin_unlock(&pool->lock);
384408
}
385409

386410
/* Couldn't find unbuddied z3fold page, create new one */
387411
page = alloc_page(gfp);
388412
if (!page)
389413
return -ENOMEM;
390-
spin_lock(&pool->lock);
414+
391415
atomic64_inc(&pool->pages_nr);
392416
zhdr = init_z3fold_page(page);
393417

394418
if (bud == HEADLESS) {
395419
set_bit(PAGE_HEADLESS, &page->private);
420+
spin_lock(&pool->lock);
396421
goto headless;
397422
}
423+
z3fold_page_lock(zhdr);
398424

399425
found:
400426
if (bud == FIRST)
@@ -406,6 +432,7 @@ static int z3fold_alloc(struct z3fold_pool *pool, size_t size, gfp_t gfp,
406432
zhdr->start_middle = zhdr->first_chunks + ZHDR_CHUNKS;
407433
}
408434

435+
spin_lock(&pool->lock);
409436
if (zhdr->first_chunks == 0 || zhdr->last_chunks == 0 ||
410437
zhdr->middle_chunks == 0) {
411438
/* Add to unbuddied list */
@@ -425,6 +452,8 @@ static int z3fold_alloc(struct z3fold_pool *pool, size_t size, gfp_t gfp,
425452

426453
*handle = encode_handle(zhdr, bud);
427454
spin_unlock(&pool->lock);
455+
if (bud != HEADLESS)
456+
z3fold_page_unlock(zhdr);
428457

429458
return 0;
430459
}
@@ -446,14 +475,14 @@ static void z3fold_free(struct z3fold_pool *pool, unsigned long handle)
446475
struct page *page;
447476
enum buddy bud;
448477

449-
spin_lock(&pool->lock);
450478
zhdr = handle_to_z3fold_header(handle);
451479
page = virt_to_page(zhdr);
452480

453481
if (test_bit(PAGE_HEADLESS, &page->private)) {
454482
/* HEADLESS page stored */
455483
bud = HEADLESS;
456484
} else {
485+
z3fold_page_lock(zhdr);
457486
bud = handle_to_buddy(handle);
458487

459488
switch (bud) {
@@ -470,37 +499,59 @@ static void z3fold_free(struct z3fold_pool *pool, unsigned long handle)
470499
default:
471500
pr_err("%s: unknown bud %d\n", __func__, bud);
472501
WARN_ON(1);
473-
spin_unlock(&pool->lock);
502+
z3fold_page_unlock(zhdr);
474503
return;
475504
}
476505
}
477506

478507
if (test_bit(UNDER_RECLAIM, &page->private)) {
479508
/* z3fold page is under reclaim, reclaim will free */
480-
spin_unlock(&pool->lock);
509+
if (bud != HEADLESS)
510+
z3fold_page_unlock(zhdr);
481511
return;
482512
}
483513

484514
/* Remove from existing buddy list */
485-
if (bud != HEADLESS)
486-
list_del(&zhdr->buddy);
515+
if (bud != HEADLESS) {
516+
spin_lock(&pool->lock);
517+
/*
518+
* this object may have been removed from its list by
519+
* z3fold_alloc(). In that case we just do nothing,
520+
* z3fold_alloc() will allocate an object and add the page
521+
* to the relevant list.
522+
*/
523+
if (!list_empty(&zhdr->buddy)) {
524+
list_del(&zhdr->buddy);
525+
} else {
526+
spin_unlock(&pool->lock);
527+
z3fold_page_unlock(zhdr);
528+
return;
529+
}
530+
spin_unlock(&pool->lock);
531+
}
487532

488533
if (bud == HEADLESS ||
489534
(zhdr->first_chunks == 0 && zhdr->middle_chunks == 0 &&
490535
zhdr->last_chunks == 0)) {
491536
/* z3fold page is empty, free */
537+
spin_lock(&pool->lock);
492538
list_del(&page->lru);
539+
spin_unlock(&pool->lock);
493540
clear_bit(PAGE_HEADLESS, &page->private);
541+
if (bud != HEADLESS)
542+
z3fold_page_unlock(zhdr);
494543
free_z3fold_page(zhdr);
495544
atomic64_dec(&pool->pages_nr);
496545
} else {
497546
z3fold_compact_page(zhdr);
498547
/* Add to the unbuddied list */
548+
spin_lock(&pool->lock);
499549
freechunks = num_free_chunks(zhdr);
500550
list_add(&zhdr->buddy, &pool->unbuddied[freechunks]);
551+
spin_unlock(&pool->lock);
552+
z3fold_page_unlock(zhdr);
501553
}
502554

503-
spin_unlock(&pool->lock);
504555
}
505556

506557
/**
@@ -547,12 +598,15 @@ static int z3fold_reclaim_page(struct z3fold_pool *pool, unsigned int retries)
547598
unsigned long first_handle = 0, middle_handle = 0, last_handle = 0;
548599

549600
spin_lock(&pool->lock);
550-
if (!pool->ops || !pool->ops->evict || list_empty(&pool->lru) ||
551-
retries == 0) {
601+
if (!pool->ops || !pool->ops->evict || retries == 0) {
552602
spin_unlock(&pool->lock);
553603
return -EINVAL;
554604
}
555605
for (i = 0; i < retries; i++) {
606+
if (list_empty(&pool->lru)) {
607+
spin_unlock(&pool->lock);
608+
return -EINVAL;
609+
}
556610
page = list_last_entry(&pool->lru, struct page, lru);
557611
list_del(&page->lru);
558612

@@ -561,6 +615,8 @@ static int z3fold_reclaim_page(struct z3fold_pool *pool, unsigned int retries)
561615
zhdr = page_address(page);
562616
if (!test_bit(PAGE_HEADLESS, &page->private)) {
563617
list_del(&zhdr->buddy);
618+
spin_unlock(&pool->lock);
619+
z3fold_page_lock(zhdr);
564620
/*
565621
* We need encode the handles before unlocking, since
566622
* we can race with free that will set
@@ -575,13 +631,13 @@ static int z3fold_reclaim_page(struct z3fold_pool *pool, unsigned int retries)
575631
middle_handle = encode_handle(zhdr, MIDDLE);
576632
if (zhdr->last_chunks)
577633
last_handle = encode_handle(zhdr, LAST);
634+
z3fold_page_unlock(zhdr);
578635
} else {
579636
first_handle = encode_handle(zhdr, HEADLESS);
580637
last_handle = middle_handle = 0;
638+
spin_unlock(&pool->lock);
581639
}
582640

583-
spin_unlock(&pool->lock);
584-
585641
/* Issue the eviction callback(s) */
586642
if (middle_handle) {
587643
ret = pool->ops->evict(pool, middle_handle);
@@ -599,7 +655,8 @@ static int z3fold_reclaim_page(struct z3fold_pool *pool, unsigned int retries)
599655
goto next;
600656
}
601657
next:
602-
spin_lock(&pool->lock);
658+
if (!test_bit(PAGE_HEADLESS, &page->private))
659+
z3fold_page_lock(zhdr);
603660
clear_bit(UNDER_RECLAIM, &page->private);
604661
if ((test_bit(PAGE_HEADLESS, &page->private) && ret == 0) ||
605662
(zhdr->first_chunks == 0 && zhdr->last_chunks == 0 &&
@@ -608,26 +665,34 @@ static int z3fold_reclaim_page(struct z3fold_pool *pool, unsigned int retries)
608665
* All buddies are now free, free the z3fold page and
609666
* return success.
610667
*/
611-
clear_bit(PAGE_HEADLESS, &page->private);
668+
if (!test_and_clear_bit(PAGE_HEADLESS, &page->private))
669+
z3fold_page_unlock(zhdr);
612670
free_z3fold_page(zhdr);
613671
atomic64_dec(&pool->pages_nr);
614-
spin_unlock(&pool->lock);
615672
return 0;
616673
} else if (!test_bit(PAGE_HEADLESS, &page->private)) {
617674
if (zhdr->first_chunks != 0 &&
618675
zhdr->last_chunks != 0 &&
619676
zhdr->middle_chunks != 0) {
620677
/* Full, add to buddied list */
678+
spin_lock(&pool->lock);
621679
list_add(&zhdr->buddy, &pool->buddied);
680+
spin_unlock(&pool->lock);
622681
} else {
623682
z3fold_compact_page(zhdr);
624683
/* add to unbuddied list */
684+
spin_lock(&pool->lock);
625685
freechunks = num_free_chunks(zhdr);
626686
list_add(&zhdr->buddy,
627687
&pool->unbuddied[freechunks]);
688+
spin_unlock(&pool->lock);
628689
}
629690
}
630691

692+
if (!test_bit(PAGE_HEADLESS, &page->private))
693+
z3fold_page_unlock(zhdr);
694+
695+
spin_lock(&pool->lock);
631696
/* add to beginning of LRU */
632697
list_add(&page->lru, &pool->lru);
633698
}
@@ -652,14 +717,14 @@ static void *z3fold_map(struct z3fold_pool *pool, unsigned long handle)
652717
void *addr;
653718
enum buddy buddy;
654719

655-
spin_lock(&pool->lock);
656720
zhdr = handle_to_z3fold_header(handle);
657721
addr = zhdr;
658722
page = virt_to_page(zhdr);
659723

660724
if (test_bit(PAGE_HEADLESS, &page->private))
661725
goto out;
662726

727+
z3fold_page_lock(zhdr);
663728
buddy = handle_to_buddy(handle);
664729
switch (buddy) {
665730
case FIRST:
@@ -678,8 +743,9 @@ static void *z3fold_map(struct z3fold_pool *pool, unsigned long handle)
678743
addr = NULL;
679744
break;
680745
}
746+
747+
z3fold_page_unlock(zhdr);
681748
out:
682-
spin_unlock(&pool->lock);
683749
return addr;
684750
}
685751

@@ -694,19 +760,17 @@ static void z3fold_unmap(struct z3fold_pool *pool, unsigned long handle)
694760
struct page *page;
695761
enum buddy buddy;
696762

697-
spin_lock(&pool->lock);
698763
zhdr = handle_to_z3fold_header(handle);
699764
page = virt_to_page(zhdr);
700765

701-
if (test_bit(PAGE_HEADLESS, &page->private)) {
702-
spin_unlock(&pool->lock);
766+
if (test_bit(PAGE_HEADLESS, &page->private))
703767
return;
704-
}
705768

769+
z3fold_page_lock(zhdr);
706770
buddy = handle_to_buddy(handle);
707771
if (buddy == MIDDLE)
708772
clear_bit(MIDDLE_CHUNK_MAPPED, &page->private);
709-
spin_unlock(&pool->lock);
773+
z3fold_page_unlock(zhdr);
710774
}
711775

712776
/**

0 commit comments

Comments
 (0)