diff options
Diffstat (limited to 'mm/zswap.c')
-rw-r--r-- | mm/zswap.c | 195 |
1 files changed, 99 insertions, 96 deletions
diff --git a/mm/zswap.c b/mm/zswap.c index d93510c6aa2d..5a63f78a5601 100644 --- a/mm/zswap.c +++ b/mm/zswap.c | |||
@@ -217,6 +217,7 @@ static struct zswap_entry *zswap_entry_cache_alloc(gfp_t gfp) | |||
217 | if (!entry) | 217 | if (!entry) |
218 | return NULL; | 218 | return NULL; |
219 | entry->refcount = 1; | 219 | entry->refcount = 1; |
220 | RB_CLEAR_NODE(&entry->rbnode); | ||
220 | return entry; | 221 | return entry; |
221 | } | 222 | } |
222 | 223 | ||
@@ -225,19 +226,6 @@ static void zswap_entry_cache_free(struct zswap_entry *entry) | |||
225 | kmem_cache_free(zswap_entry_cache, entry); | 226 | kmem_cache_free(zswap_entry_cache, entry); |
226 | } | 227 | } |
227 | 228 | ||
228 | /* caller must hold the tree lock */ | ||
229 | static void zswap_entry_get(struct zswap_entry *entry) | ||
230 | { | ||
231 | entry->refcount++; | ||
232 | } | ||
233 | |||
234 | /* caller must hold the tree lock */ | ||
235 | static int zswap_entry_put(struct zswap_entry *entry) | ||
236 | { | ||
237 | entry->refcount--; | ||
238 | return entry->refcount; | ||
239 | } | ||
240 | |||
241 | /********************************* | 229 | /********************************* |
242 | * rbtree functions | 230 | * rbtree functions |
243 | **********************************/ | 231 | **********************************/ |
@@ -285,6 +273,61 @@ static int zswap_rb_insert(struct rb_root *root, struct zswap_entry *entry, | |||
285 | return 0; | 273 | return 0; |
286 | } | 274 | } |
287 | 275 | ||
276 | static void zswap_rb_erase(struct rb_root *root, struct zswap_entry *entry) | ||
277 | { | ||
278 | if (!RB_EMPTY_NODE(&entry->rbnode)) { | ||
279 | rb_erase(&entry->rbnode, root); | ||
280 | RB_CLEAR_NODE(&entry->rbnode); | ||
281 | } | ||
282 | } | ||
283 | |||
284 | /* | ||
285 | * Carries out the common pattern of freeing and entry's zsmalloc allocation, | ||
286 | * freeing the entry itself, and decrementing the number of stored pages. | ||
287 | */ | ||
288 | static void zswap_free_entry(struct zswap_tree *tree, | ||
289 | struct zswap_entry *entry) | ||
290 | { | ||
291 | zbud_free(tree->pool, entry->handle); | ||
292 | zswap_entry_cache_free(entry); | ||
293 | atomic_dec(&zswap_stored_pages); | ||
294 | zswap_pool_pages = zbud_get_pool_size(tree->pool); | ||
295 | } | ||
296 | |||
297 | /* caller must hold the tree lock */ | ||
298 | static void zswap_entry_get(struct zswap_entry *entry) | ||
299 | { | ||
300 | entry->refcount++; | ||
301 | } | ||
302 | |||
303 | /* caller must hold the tree lock | ||
304 | * remove from the tree and free it, if nobody reference the entry | ||
305 | */ | ||
306 | static void zswap_entry_put(struct zswap_tree *tree, | ||
307 | struct zswap_entry *entry) | ||
308 | { | ||
309 | int refcount = --entry->refcount; | ||
310 | |||
311 | BUG_ON(refcount < 0); | ||
312 | if (refcount == 0) { | ||
313 | zswap_rb_erase(&tree->rbroot, entry); | ||
314 | zswap_free_entry(tree, entry); | ||
315 | } | ||
316 | } | ||
317 | |||
318 | /* caller must hold the tree lock */ | ||
319 | static struct zswap_entry *zswap_entry_find_get(struct rb_root *root, | ||
320 | pgoff_t offset) | ||
321 | { | ||
322 | struct zswap_entry *entry = NULL; | ||
323 | |||
324 | entry = zswap_rb_search(root, offset); | ||
325 | if (entry) | ||
326 | zswap_entry_get(entry); | ||
327 | |||
328 | return entry; | ||
329 | } | ||
330 | |||
288 | /********************************* | 331 | /********************************* |
289 | * per-cpu code | 332 | * per-cpu code |
290 | **********************************/ | 333 | **********************************/ |
@@ -368,18 +411,6 @@ static bool zswap_is_full(void) | |||
368 | zswap_pool_pages); | 411 | zswap_pool_pages); |
369 | } | 412 | } |
370 | 413 | ||
371 | /* | ||
372 | * Carries out the common pattern of freeing and entry's zsmalloc allocation, | ||
373 | * freeing the entry itself, and decrementing the number of stored pages. | ||
374 | */ | ||
375 | static void zswap_free_entry(struct zswap_tree *tree, struct zswap_entry *entry) | ||
376 | { | ||
377 | zbud_free(tree->pool, entry->handle); | ||
378 | zswap_entry_cache_free(entry); | ||
379 | atomic_dec(&zswap_stored_pages); | ||
380 | zswap_pool_pages = zbud_get_pool_size(tree->pool); | ||
381 | } | ||
382 | |||
383 | /********************************* | 414 | /********************************* |
384 | * writeback code | 415 | * writeback code |
385 | **********************************/ | 416 | **********************************/ |
@@ -387,7 +418,7 @@ static void zswap_free_entry(struct zswap_tree *tree, struct zswap_entry *entry) | |||
387 | enum zswap_get_swap_ret { | 418 | enum zswap_get_swap_ret { |
388 | ZSWAP_SWAPCACHE_NEW, | 419 | ZSWAP_SWAPCACHE_NEW, |
389 | ZSWAP_SWAPCACHE_EXIST, | 420 | ZSWAP_SWAPCACHE_EXIST, |
390 | ZSWAP_SWAPCACHE_NOMEM | 421 | ZSWAP_SWAPCACHE_FAIL, |
391 | }; | 422 | }; |
392 | 423 | ||
393 | /* | 424 | /* |
@@ -401,9 +432,10 @@ enum zswap_get_swap_ret { | |||
401 | * added to the swap cache, and returned in retpage. | 432 | * added to the swap cache, and returned in retpage. |
402 | * | 433 | * |
403 | * If success, the swap cache page is returned in retpage | 434 | * If success, the swap cache page is returned in retpage |
404 | * Returns 0 if page was already in the swap cache, page is not locked | 435 | * Returns ZSWAP_SWAPCACHE_EXIST if page was already in the swap cache |
405 | * Returns 1 if the new page needs to be populated, page is locked | 436 | * Returns ZSWAP_SWAPCACHE_NEW if the new page needs to be populated, |
406 | * Returns <0 on error | 437 | * the new page is added to swapcache and locked |
438 | * Returns ZSWAP_SWAPCACHE_FAIL on error | ||
407 | */ | 439 | */ |
408 | static int zswap_get_swap_cache_page(swp_entry_t entry, | 440 | static int zswap_get_swap_cache_page(swp_entry_t entry, |
409 | struct page **retpage) | 441 | struct page **retpage) |
@@ -475,7 +507,7 @@ static int zswap_get_swap_cache_page(swp_entry_t entry, | |||
475 | if (new_page) | 507 | if (new_page) |
476 | page_cache_release(new_page); | 508 | page_cache_release(new_page); |
477 | if (!found_page) | 509 | if (!found_page) |
478 | return ZSWAP_SWAPCACHE_NOMEM; | 510 | return ZSWAP_SWAPCACHE_FAIL; |
479 | *retpage = found_page; | 511 | *retpage = found_page; |
480 | return ZSWAP_SWAPCACHE_EXIST; | 512 | return ZSWAP_SWAPCACHE_EXIST; |
481 | } | 513 | } |
@@ -502,7 +534,7 @@ static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle) | |||
502 | struct page *page; | 534 | struct page *page; |
503 | u8 *src, *dst; | 535 | u8 *src, *dst; |
504 | unsigned int dlen; | 536 | unsigned int dlen; |
505 | int ret, refcount; | 537 | int ret; |
506 | struct writeback_control wbc = { | 538 | struct writeback_control wbc = { |
507 | .sync_mode = WB_SYNC_NONE, | 539 | .sync_mode = WB_SYNC_NONE, |
508 | }; | 540 | }; |
@@ -517,23 +549,22 @@ static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle) | |||
517 | 549 | ||
518 | /* find and ref zswap entry */ | 550 | /* find and ref zswap entry */ |
519 | spin_lock(&tree->lock); | 551 | spin_lock(&tree->lock); |
520 | entry = zswap_rb_search(&tree->rbroot, offset); | 552 | entry = zswap_entry_find_get(&tree->rbroot, offset); |
521 | if (!entry) { | 553 | if (!entry) { |
522 | /* entry was invalidated */ | 554 | /* entry was invalidated */ |
523 | spin_unlock(&tree->lock); | 555 | spin_unlock(&tree->lock); |
524 | return 0; | 556 | return 0; |
525 | } | 557 | } |
526 | zswap_entry_get(entry); | ||
527 | spin_unlock(&tree->lock); | 558 | spin_unlock(&tree->lock); |
528 | BUG_ON(offset != entry->offset); | 559 | BUG_ON(offset != entry->offset); |
529 | 560 | ||
530 | /* try to allocate swap cache page */ | 561 | /* try to allocate swap cache page */ |
531 | switch (zswap_get_swap_cache_page(swpentry, &page)) { | 562 | switch (zswap_get_swap_cache_page(swpentry, &page)) { |
532 | case ZSWAP_SWAPCACHE_NOMEM: /* no memory */ | 563 | case ZSWAP_SWAPCACHE_FAIL: /* no memory or invalidate happened */ |
533 | ret = -ENOMEM; | 564 | ret = -ENOMEM; |
534 | goto fail; | 565 | goto fail; |
535 | 566 | ||
536 | case ZSWAP_SWAPCACHE_EXIST: /* page is unlocked */ | 567 | case ZSWAP_SWAPCACHE_EXIST: |
537 | /* page is already in the swap cache, ignore for now */ | 568 | /* page is already in the swap cache, ignore for now */ |
538 | page_cache_release(page); | 569 | page_cache_release(page); |
539 | ret = -EEXIST; | 570 | ret = -EEXIST; |
@@ -556,43 +587,44 @@ static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle) | |||
556 | SetPageUptodate(page); | 587 | SetPageUptodate(page); |
557 | } | 588 | } |
558 | 589 | ||
590 | /* move it to the tail of the inactive list after end_writeback */ | ||
591 | SetPageReclaim(page); | ||
592 | |||
559 | /* start writeback */ | 593 | /* start writeback */ |
560 | __swap_writepage(page, &wbc, end_swap_bio_write); | 594 | __swap_writepage(page, &wbc, end_swap_bio_write); |
561 | page_cache_release(page); | 595 | page_cache_release(page); |
562 | zswap_written_back_pages++; | 596 | zswap_written_back_pages++; |
563 | 597 | ||
564 | spin_lock(&tree->lock); | 598 | spin_lock(&tree->lock); |
565 | |||
566 | /* drop local reference */ | 599 | /* drop local reference */ |
567 | zswap_entry_put(entry); | 600 | zswap_entry_put(tree, entry); |
568 | /* drop the initial reference from entry creation */ | ||
569 | refcount = zswap_entry_put(entry); | ||
570 | 601 | ||
571 | /* | 602 | /* |
572 | * There are three possible values for refcount here: | 603 | * There are two possible situations for entry here: |
573 | * (1) refcount is 1, load is in progress, unlink from rbtree, | 604 | * (1) refcount is 1(normal case), entry is valid and on the tree |
574 | * load will free | 605 | * (2) refcount is 0, entry is freed and not on the tree |
575 | * (2) refcount is 0, (normal case) entry is valid, | 606 | * because invalidate happened during writeback |
576 | * remove from rbtree and free entry | 607 | * search the tree and free the entry if find entry |
577 | * (3) refcount is -1, invalidate happened during writeback, | 608 | */ |
578 | * free entry | 609 | if (entry == zswap_rb_search(&tree->rbroot, offset)) |
579 | */ | 610 | zswap_entry_put(tree, entry); |
580 | if (refcount >= 0) { | ||
581 | /* no invalidate yet, remove from rbtree */ | ||
582 | rb_erase(&entry->rbnode, &tree->rbroot); | ||
583 | } | ||
584 | spin_unlock(&tree->lock); | 611 | spin_unlock(&tree->lock); |
585 | if (refcount <= 0) { | ||
586 | /* free the entry */ | ||
587 | zswap_free_entry(tree, entry); | ||
588 | return 0; | ||
589 | } | ||
590 | return -EAGAIN; | ||
591 | 612 | ||
613 | goto end; | ||
614 | |||
615 | /* | ||
616 | * if we get here due to ZSWAP_SWAPCACHE_EXIST | ||
617 | * a load may happening concurrently | ||
618 | * it is safe and okay to not free the entry | ||
619 | * if we free the entry in the following put | ||
620 | * it it either okay to return !0 | ||
621 | */ | ||
592 | fail: | 622 | fail: |
593 | spin_lock(&tree->lock); | 623 | spin_lock(&tree->lock); |
594 | zswap_entry_put(entry); | 624 | zswap_entry_put(tree, entry); |
595 | spin_unlock(&tree->lock); | 625 | spin_unlock(&tree->lock); |
626 | |||
627 | end: | ||
596 | return ret; | 628 | return ret; |
597 | } | 629 | } |
598 | 630 | ||
@@ -676,11 +708,8 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset, | |||
676 | if (ret == -EEXIST) { | 708 | if (ret == -EEXIST) { |
677 | zswap_duplicate_entry++; | 709 | zswap_duplicate_entry++; |
678 | /* remove from rbtree */ | 710 | /* remove from rbtree */ |
679 | rb_erase(&dupentry->rbnode, &tree->rbroot); | 711 | zswap_rb_erase(&tree->rbroot, dupentry); |
680 | if (!zswap_entry_put(dupentry)) { | 712 | zswap_entry_put(tree, dupentry); |
681 | /* free */ | ||
682 | zswap_free_entry(tree, dupentry); | ||
683 | } | ||
684 | } | 713 | } |
685 | } while (ret == -EEXIST); | 714 | } while (ret == -EEXIST); |
686 | spin_unlock(&tree->lock); | 715 | spin_unlock(&tree->lock); |
@@ -709,17 +738,16 @@ static int zswap_frontswap_load(unsigned type, pgoff_t offset, | |||
709 | struct zswap_entry *entry; | 738 | struct zswap_entry *entry; |
710 | u8 *src, *dst; | 739 | u8 *src, *dst; |
711 | unsigned int dlen; | 740 | unsigned int dlen; |
712 | int refcount, ret; | 741 | int ret; |
713 | 742 | ||
714 | /* find */ | 743 | /* find */ |
715 | spin_lock(&tree->lock); | 744 | spin_lock(&tree->lock); |
716 | entry = zswap_rb_search(&tree->rbroot, offset); | 745 | entry = zswap_entry_find_get(&tree->rbroot, offset); |
717 | if (!entry) { | 746 | if (!entry) { |
718 | /* entry was written back */ | 747 | /* entry was written back */ |
719 | spin_unlock(&tree->lock); | 748 | spin_unlock(&tree->lock); |
720 | return -1; | 749 | return -1; |
721 | } | 750 | } |
722 | zswap_entry_get(entry); | ||
723 | spin_unlock(&tree->lock); | 751 | spin_unlock(&tree->lock); |
724 | 752 | ||
725 | /* decompress */ | 753 | /* decompress */ |
@@ -734,22 +762,9 @@ static int zswap_frontswap_load(unsigned type, pgoff_t offset, | |||
734 | BUG_ON(ret); | 762 | BUG_ON(ret); |
735 | 763 | ||
736 | spin_lock(&tree->lock); | 764 | spin_lock(&tree->lock); |
737 | refcount = zswap_entry_put(entry); | 765 | zswap_entry_put(tree, entry); |
738 | if (likely(refcount)) { | ||
739 | spin_unlock(&tree->lock); | ||
740 | return 0; | ||
741 | } | ||
742 | spin_unlock(&tree->lock); | 766 | spin_unlock(&tree->lock); |
743 | 767 | ||
744 | /* | ||
745 | * We don't have to unlink from the rbtree because | ||
746 | * zswap_writeback_entry() or zswap_frontswap_invalidate page() | ||
747 | * has already done this for us if we are the last reference. | ||
748 | */ | ||
749 | /* free */ | ||
750 | |||
751 | zswap_free_entry(tree, entry); | ||
752 | |||
753 | return 0; | 768 | return 0; |
754 | } | 769 | } |
755 | 770 | ||
@@ -758,7 +773,6 @@ static void zswap_frontswap_invalidate_page(unsigned type, pgoff_t offset) | |||
758 | { | 773 | { |
759 | struct zswap_tree *tree = zswap_trees[type]; | 774 | struct zswap_tree *tree = zswap_trees[type]; |
760 | struct zswap_entry *entry; | 775 | struct zswap_entry *entry; |
761 | int refcount; | ||
762 | 776 | ||
763 | /* find */ | 777 | /* find */ |
764 | spin_lock(&tree->lock); | 778 | spin_lock(&tree->lock); |
@@ -770,20 +784,12 @@ static void zswap_frontswap_invalidate_page(unsigned type, pgoff_t offset) | |||
770 | } | 784 | } |
771 | 785 | ||
772 | /* remove from rbtree */ | 786 | /* remove from rbtree */ |
773 | rb_erase(&entry->rbnode, &tree->rbroot); | 787 | zswap_rb_erase(&tree->rbroot, entry); |
774 | 788 | ||
775 | /* drop the initial reference from entry creation */ | 789 | /* drop the initial reference from entry creation */ |
776 | refcount = zswap_entry_put(entry); | 790 | zswap_entry_put(tree, entry); |
777 | 791 | ||
778 | spin_unlock(&tree->lock); | 792 | spin_unlock(&tree->lock); |
779 | |||
780 | if (refcount) { | ||
781 | /* writeback in progress, writeback will free */ | ||
782 | return; | ||
783 | } | ||
784 | |||
785 | /* free */ | ||
786 | zswap_free_entry(tree, entry); | ||
787 | } | 793 | } |
788 | 794 | ||
789 | /* frees all zswap entries for the given swap type */ | 795 | /* frees all zswap entries for the given swap type */ |
@@ -797,11 +803,8 @@ static void zswap_frontswap_invalidate_area(unsigned type) | |||
797 | 803 | ||
798 | /* walk the tree and free everything */ | 804 | /* walk the tree and free everything */ |
799 | spin_lock(&tree->lock); | 805 | spin_lock(&tree->lock); |
800 | rbtree_postorder_for_each_entry_safe(entry, n, &tree->rbroot, rbnode) { | 806 | rbtree_postorder_for_each_entry_safe(entry, n, &tree->rbroot, rbnode) |
801 | zbud_free(tree->pool, entry->handle); | 807 | zswap_free_entry(tree, entry); |
802 | zswap_entry_cache_free(entry); | ||
803 | atomic_dec(&zswap_stored_pages); | ||
804 | } | ||
805 | tree->rbroot = RB_ROOT; | 808 | tree->rbroot = RB_ROOT; |
806 | spin_unlock(&tree->lock); | 809 | spin_unlock(&tree->lock); |
807 | 810 | ||