diff options
Diffstat (limited to 'mm/z3fold.c')
-rw-r--r-- | mm/z3fold.c | 101 |
1 files changed, 62 insertions, 39 deletions
diff --git a/mm/z3fold.c b/mm/z3fold.c index 4b366d181f35..aee9b0b8d907 100644 --- a/mm/z3fold.c +++ b/mm/z3fold.c | |||
@@ -99,6 +99,7 @@ struct z3fold_header { | |||
99 | #define NCHUNKS ((PAGE_SIZE - ZHDR_SIZE_ALIGNED) >> CHUNK_SHIFT) | 99 | #define NCHUNKS ((PAGE_SIZE - ZHDR_SIZE_ALIGNED) >> CHUNK_SHIFT) |
100 | 100 | ||
101 | #define BUDDY_MASK (0x3) | 101 | #define BUDDY_MASK (0x3) |
102 | #define BUDDY_SHIFT 2 | ||
102 | 103 | ||
103 | /** | 104 | /** |
104 | * struct z3fold_pool - stores metadata for each z3fold pool | 105 | * struct z3fold_pool - stores metadata for each z3fold pool |
@@ -145,7 +146,7 @@ enum z3fold_page_flags { | |||
145 | MIDDLE_CHUNK_MAPPED, | 146 | MIDDLE_CHUNK_MAPPED, |
146 | NEEDS_COMPACTING, | 147 | NEEDS_COMPACTING, |
147 | PAGE_STALE, | 148 | PAGE_STALE, |
148 | UNDER_RECLAIM | 149 | PAGE_CLAIMED, /* by either reclaim or free */ |
149 | }; | 150 | }; |
150 | 151 | ||
151 | /***************** | 152 | /***************** |
@@ -174,7 +175,7 @@ static struct z3fold_header *init_z3fold_page(struct page *page, | |||
174 | clear_bit(MIDDLE_CHUNK_MAPPED, &page->private); | 175 | clear_bit(MIDDLE_CHUNK_MAPPED, &page->private); |
175 | clear_bit(NEEDS_COMPACTING, &page->private); | 176 | clear_bit(NEEDS_COMPACTING, &page->private); |
176 | clear_bit(PAGE_STALE, &page->private); | 177 | clear_bit(PAGE_STALE, &page->private); |
177 | clear_bit(UNDER_RECLAIM, &page->private); | 178 | clear_bit(PAGE_CLAIMED, &page->private); |
178 | 179 | ||
179 | spin_lock_init(&zhdr->page_lock); | 180 | spin_lock_init(&zhdr->page_lock); |
180 | kref_init(&zhdr->refcount); | 181 | kref_init(&zhdr->refcount); |
@@ -223,8 +224,11 @@ static unsigned long encode_handle(struct z3fold_header *zhdr, enum buddy bud) | |||
223 | unsigned long handle; | 224 | unsigned long handle; |
224 | 225 | ||
225 | handle = (unsigned long)zhdr; | 226 | handle = (unsigned long)zhdr; |
226 | if (bud != HEADLESS) | 227 | if (bud != HEADLESS) { |
227 | handle += (bud + zhdr->first_num) & BUDDY_MASK; | 228 | handle |= (bud + zhdr->first_num) & BUDDY_MASK; |
229 | if (bud == LAST) | ||
230 | handle |= (zhdr->last_chunks << BUDDY_SHIFT); | ||
231 | } | ||
228 | return handle; | 232 | return handle; |
229 | } | 233 | } |
230 | 234 | ||
@@ -234,6 +238,12 @@ static struct z3fold_header *handle_to_z3fold_header(unsigned long handle) | |||
234 | return (struct z3fold_header *)(handle & PAGE_MASK); | 238 | return (struct z3fold_header *)(handle & PAGE_MASK); |
235 | } | 239 | } |
236 | 240 | ||
241 | /* only for LAST bud, returns zero otherwise */ | ||
242 | static unsigned short handle_to_chunks(unsigned long handle) | ||
243 | { | ||
244 | return (handle & ~PAGE_MASK) >> BUDDY_SHIFT; | ||
245 | } | ||
246 | |||
237 | /* | 247 | /* |
238 | * (handle & BUDDY_MASK) < zhdr->first_num is possible in encode_handle | 248 | * (handle & BUDDY_MASK) < zhdr->first_num is possible in encode_handle |
239 | * but that doesn't matter. because the masking will result in the | 249 | * but that doesn't matter. because the masking will result in the |
@@ -720,37 +730,39 @@ static void z3fold_free(struct z3fold_pool *pool, unsigned long handle) | |||
720 | page = virt_to_page(zhdr); | 730 | page = virt_to_page(zhdr); |
721 | 731 | ||
722 | if (test_bit(PAGE_HEADLESS, &page->private)) { | 732 | if (test_bit(PAGE_HEADLESS, &page->private)) { |
723 | /* HEADLESS page stored */ | 733 | /* if a headless page is under reclaim, just leave. |
724 | bud = HEADLESS; | 734 | * NB: we use test_and_set_bit for a reason: if the bit |
725 | } else { | 735 | * has not been set before, we release this page |
726 | z3fold_page_lock(zhdr); | 736 | * immediately so we don't care about its value any more. |
727 | bud = handle_to_buddy(handle); | 737 | */ |
728 | 738 | if (!test_and_set_bit(PAGE_CLAIMED, &page->private)) { | |
729 | switch (bud) { | 739 | spin_lock(&pool->lock); |
730 | case FIRST: | 740 | list_del(&page->lru); |
731 | zhdr->first_chunks = 0; | 741 | spin_unlock(&pool->lock); |
732 | break; | 742 | free_z3fold_page(page); |
733 | case MIDDLE: | 743 | atomic64_dec(&pool->pages_nr); |
734 | zhdr->middle_chunks = 0; | ||
735 | zhdr->start_middle = 0; | ||
736 | break; | ||
737 | case LAST: | ||
738 | zhdr->last_chunks = 0; | ||
739 | break; | ||
740 | default: | ||
741 | pr_err("%s: unknown bud %d\n", __func__, bud); | ||
742 | WARN_ON(1); | ||
743 | z3fold_page_unlock(zhdr); | ||
744 | return; | ||
745 | } | 744 | } |
745 | return; | ||
746 | } | 746 | } |
747 | 747 | ||
748 | if (bud == HEADLESS) { | 748 | /* Non-headless case */ |
749 | spin_lock(&pool->lock); | 749 | z3fold_page_lock(zhdr); |
750 | list_del(&page->lru); | 750 | bud = handle_to_buddy(handle); |
751 | spin_unlock(&pool->lock); | 751 | |
752 | free_z3fold_page(page); | 752 | switch (bud) { |
753 | atomic64_dec(&pool->pages_nr); | 753 | case FIRST: |
754 | zhdr->first_chunks = 0; | ||
755 | break; | ||
756 | case MIDDLE: | ||
757 | zhdr->middle_chunks = 0; | ||
758 | break; | ||
759 | case LAST: | ||
760 | zhdr->last_chunks = 0; | ||
761 | break; | ||
762 | default: | ||
763 | pr_err("%s: unknown bud %d\n", __func__, bud); | ||
764 | WARN_ON(1); | ||
765 | z3fold_page_unlock(zhdr); | ||
754 | return; | 766 | return; |
755 | } | 767 | } |
756 | 768 | ||
@@ -758,7 +770,7 @@ static void z3fold_free(struct z3fold_pool *pool, unsigned long handle) | |||
758 | atomic64_dec(&pool->pages_nr); | 770 | atomic64_dec(&pool->pages_nr); |
759 | return; | 771 | return; |
760 | } | 772 | } |
761 | if (test_bit(UNDER_RECLAIM, &page->private)) { | 773 | if (test_bit(PAGE_CLAIMED, &page->private)) { |
762 | z3fold_page_unlock(zhdr); | 774 | z3fold_page_unlock(zhdr); |
763 | return; | 775 | return; |
764 | } | 776 | } |
@@ -836,20 +848,30 @@ static int z3fold_reclaim_page(struct z3fold_pool *pool, unsigned int retries) | |||
836 | } | 848 | } |
837 | list_for_each_prev(pos, &pool->lru) { | 849 | list_for_each_prev(pos, &pool->lru) { |
838 | page = list_entry(pos, struct page, lru); | 850 | page = list_entry(pos, struct page, lru); |
851 | |||
852 | /* this bit could have been set by free, in which case | ||
853 | * we pass over to the next page in the pool. | ||
854 | */ | ||
855 | if (test_and_set_bit(PAGE_CLAIMED, &page->private)) | ||
856 | continue; | ||
857 | |||
858 | zhdr = page_address(page); | ||
839 | if (test_bit(PAGE_HEADLESS, &page->private)) | 859 | if (test_bit(PAGE_HEADLESS, &page->private)) |
840 | /* candidate found */ | ||
841 | break; | 860 | break; |
842 | 861 | ||
843 | zhdr = page_address(page); | 862 | if (!z3fold_page_trylock(zhdr)) { |
844 | if (!z3fold_page_trylock(zhdr)) | 863 | zhdr = NULL; |
845 | continue; /* can't evict at this point */ | 864 | continue; /* can't evict at this point */ |
865 | } | ||
846 | kref_get(&zhdr->refcount); | 866 | kref_get(&zhdr->refcount); |
847 | list_del_init(&zhdr->buddy); | 867 | list_del_init(&zhdr->buddy); |
848 | zhdr->cpu = -1; | 868 | zhdr->cpu = -1; |
849 | set_bit(UNDER_RECLAIM, &page->private); | ||
850 | break; | 869 | break; |
851 | } | 870 | } |
852 | 871 | ||
872 | if (!zhdr) | ||
873 | break; | ||
874 | |||
853 | list_del_init(&page->lru); | 875 | list_del_init(&page->lru); |
854 | spin_unlock(&pool->lock); | 876 | spin_unlock(&pool->lock); |
855 | 877 | ||
@@ -898,6 +920,7 @@ next: | |||
898 | if (test_bit(PAGE_HEADLESS, &page->private)) { | 920 | if (test_bit(PAGE_HEADLESS, &page->private)) { |
899 | if (ret == 0) { | 921 | if (ret == 0) { |
900 | free_z3fold_page(page); | 922 | free_z3fold_page(page); |
923 | atomic64_dec(&pool->pages_nr); | ||
901 | return 0; | 924 | return 0; |
902 | } | 925 | } |
903 | spin_lock(&pool->lock); | 926 | spin_lock(&pool->lock); |
@@ -905,7 +928,7 @@ next: | |||
905 | spin_unlock(&pool->lock); | 928 | spin_unlock(&pool->lock); |
906 | } else { | 929 | } else { |
907 | z3fold_page_lock(zhdr); | 930 | z3fold_page_lock(zhdr); |
908 | clear_bit(UNDER_RECLAIM, &page->private); | 931 | clear_bit(PAGE_CLAIMED, &page->private); |
909 | if (kref_put(&zhdr->refcount, | 932 | if (kref_put(&zhdr->refcount, |
910 | release_z3fold_page_locked)) { | 933 | release_z3fold_page_locked)) { |
911 | atomic64_dec(&pool->pages_nr); | 934 | atomic64_dec(&pool->pages_nr); |
@@ -964,7 +987,7 @@ static void *z3fold_map(struct z3fold_pool *pool, unsigned long handle) | |||
964 | set_bit(MIDDLE_CHUNK_MAPPED, &page->private); | 987 | set_bit(MIDDLE_CHUNK_MAPPED, &page->private); |
965 | break; | 988 | break; |
966 | case LAST: | 989 | case LAST: |
967 | addr += PAGE_SIZE - (zhdr->last_chunks << CHUNK_SHIFT); | 990 | addr += PAGE_SIZE - (handle_to_chunks(handle) << CHUNK_SHIFT); |
968 | break; | 991 | break; |
969 | default: | 992 | default: |
970 | pr_err("unknown buddy id %d\n", buddy); | 993 | pr_err("unknown buddy id %d\n", buddy); |