aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVitaly Wool <vitalywool@gmail.com>2019-05-13 20:22:49 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2019-05-14 12:47:50 -0400
commit7c2b8baa61fe578af905342938ad12f8dbaeae79 (patch)
tree9949c29e92a010ded8f1628e6920cd4d30be4412
parent351618b203acef13946a03ecf18fbe328c3cdb58 (diff)
mm/z3fold.c: add structure for buddy handles
For z3fold to be able to move its pages per request of the memory subsystem, it should not use direct object addresses in handles. Instead, it will create abstract handles (3 per page) which will contain pointers to z3fold objects. Thus, it will be possible to change these pointers when z3fold page is moved. Link: http://lkml.kernel.org/r/20190417103826.484eaf18c1294d682769880f@gmail.com Signed-off-by: Vitaly Wool <vitaly.vul@sony.com> Cc: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> Cc: Dan Streetman <ddstreet@ieee.org> Cc: Krzysztof Kozlowski <k.kozlowski@samsung.com> Cc: Oleksiy Avramchenko <oleksiy.avramchenko@sonymobile.com> Cc: Uladzislau Rezki <urezki@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--mm/z3fold.c185
1 files changed, 145 insertions, 40 deletions
diff --git a/mm/z3fold.c b/mm/z3fold.c
index 29a4f1249bef..bebc10083f1c 100644
--- a/mm/z3fold.c
+++ b/mm/z3fold.c
@@ -34,6 +34,29 @@
34#include <linux/spinlock.h> 34#include <linux/spinlock.h>
35#include <linux/zpool.h> 35#include <linux/zpool.h>
36 36
37/*
38 * NCHUNKS_ORDER determines the internal allocation granularity, effectively
39 * adjusting internal fragmentation. It also determines the number of
40 * freelists maintained in each pool. NCHUNKS_ORDER of 6 means that the
41 * allocation granularity will be in chunks of size PAGE_SIZE/64. Some chunks
42 * in the beginning of an allocated page are occupied by z3fold header, so
43 * NCHUNKS will be calculated to 63 (or 62 in case CONFIG_DEBUG_SPINLOCK=y),
44 * which shows the max number of free chunks in z3fold page, also there will
45 * be 63, or 62, respectively, freelists per pool.
46 */
47#define NCHUNKS_ORDER 6
48
49#define CHUNK_SHIFT (PAGE_SHIFT - NCHUNKS_ORDER)
50#define CHUNK_SIZE (1 << CHUNK_SHIFT)
51#define ZHDR_SIZE_ALIGNED round_up(sizeof(struct z3fold_header), CHUNK_SIZE)
52#define ZHDR_CHUNKS (ZHDR_SIZE_ALIGNED >> CHUNK_SHIFT)
53#define TOTAL_CHUNKS (PAGE_SIZE >> CHUNK_SHIFT)
54#define NCHUNKS ((PAGE_SIZE - ZHDR_SIZE_ALIGNED) >> CHUNK_SHIFT)
55
56#define BUDDY_MASK (0x3)
57#define BUDDY_SHIFT 2
58#define SLOTS_ALIGN (0x40)
59
37/***************** 60/*****************
38 * Structures 61 * Structures
39*****************/ 62*****************/
@@ -47,9 +70,19 @@ enum buddy {
47 FIRST, 70 FIRST,
48 MIDDLE, 71 MIDDLE,
49 LAST, 72 LAST,
50 BUDDIES_MAX 73 BUDDIES_MAX = LAST
51}; 74};
52 75
76struct z3fold_buddy_slots {
77 /*
78 * we are using BUDDY_MASK in handle_to_buddy etc. so there should
79 * be enough slots to hold all possible variants
80 */
81 unsigned long slot[BUDDY_MASK + 1];
82 unsigned long pool; /* back link + flags */
83};
84#define HANDLE_FLAG_MASK (0x03)
85
53/* 86/*
54 * struct z3fold_header - z3fold page metadata occupying first chunks of each 87 * struct z3fold_header - z3fold page metadata occupying first chunks of each
55 * z3fold page, except for HEADLESS pages 88 * z3fold page, except for HEADLESS pages
@@ -58,7 +91,7 @@ enum buddy {
58 * @page_lock: per-page lock 91 * @page_lock: per-page lock
59 * @refcount: reference count for the z3fold page 92 * @refcount: reference count for the z3fold page
60 * @work: work_struct for page layout optimization 93 * @work: work_struct for page layout optimization
61 * @pool: pointer to the pool which this page belongs to 94 * @slots: pointer to the structure holding buddy slots
62 * @cpu: CPU which this page "belongs" to 95 * @cpu: CPU which this page "belongs" to
63 * @first_chunks: the size of the first buddy in chunks, 0 if free 96 * @first_chunks: the size of the first buddy in chunks, 0 if free
64 * @middle_chunks: the size of the middle buddy in chunks, 0 if free 97 * @middle_chunks: the size of the middle buddy in chunks, 0 if free
@@ -70,7 +103,7 @@ struct z3fold_header {
70 spinlock_t page_lock; 103 spinlock_t page_lock;
71 struct kref refcount; 104 struct kref refcount;
72 struct work_struct work; 105 struct work_struct work;
73 struct z3fold_pool *pool; 106 struct z3fold_buddy_slots *slots;
74 short cpu; 107 short cpu;
75 unsigned short first_chunks; 108 unsigned short first_chunks;
76 unsigned short middle_chunks; 109 unsigned short middle_chunks;
@@ -79,28 +112,6 @@ struct z3fold_header {
79 unsigned short first_num:2; 112 unsigned short first_num:2;
80}; 113};
81 114
82/*
83 * NCHUNKS_ORDER determines the internal allocation granularity, effectively
84 * adjusting internal fragmentation. It also determines the number of
85 * freelists maintained in each pool. NCHUNKS_ORDER of 6 means that the
86 * allocation granularity will be in chunks of size PAGE_SIZE/64. Some chunks
87 * in the beginning of an allocated page are occupied by z3fold header, so
88 * NCHUNKS will be calculated to 63 (or 62 in case CONFIG_DEBUG_SPINLOCK=y),
89 * which shows the max number of free chunks in z3fold page, also there will
90 * be 63, or 62, respectively, freelists per pool.
91 */
92#define NCHUNKS_ORDER 6
93
94#define CHUNK_SHIFT (PAGE_SHIFT - NCHUNKS_ORDER)
95#define CHUNK_SIZE (1 << CHUNK_SHIFT)
96#define ZHDR_SIZE_ALIGNED round_up(sizeof(struct z3fold_header), CHUNK_SIZE)
97#define ZHDR_CHUNKS (ZHDR_SIZE_ALIGNED >> CHUNK_SHIFT)
98#define TOTAL_CHUNKS (PAGE_SIZE >> CHUNK_SHIFT)
99#define NCHUNKS ((PAGE_SIZE - ZHDR_SIZE_ALIGNED) >> CHUNK_SHIFT)
100
101#define BUDDY_MASK (0x3)
102#define BUDDY_SHIFT 2
103
104/** 115/**
105 * struct z3fold_pool - stores metadata for each z3fold pool 116 * struct z3fold_pool - stores metadata for each z3fold pool
106 * @name: pool name 117 * @name: pool name
@@ -113,6 +124,7 @@ struct z3fold_header {
113 * added buddy. 124 * added buddy.
114 * @stale: list of pages marked for freeing 125 * @stale: list of pages marked for freeing
115 * @pages_nr: number of z3fold pages in the pool. 126 * @pages_nr: number of z3fold pages in the pool.
127 * @c_handle: cache for z3fold_buddy_slots allocation
116 * @ops: pointer to a structure of user defined operations specified at 128 * @ops: pointer to a structure of user defined operations specified at
117 * pool creation time. 129 * pool creation time.
118 * @compact_wq: workqueue for page layout background optimization 130 * @compact_wq: workqueue for page layout background optimization
@@ -130,6 +142,7 @@ struct z3fold_pool {
130 struct list_head lru; 142 struct list_head lru;
131 struct list_head stale; 143 struct list_head stale;
132 atomic64_t pages_nr; 144 atomic64_t pages_nr;
145 struct kmem_cache *c_handle;
133 const struct z3fold_ops *ops; 146 const struct z3fold_ops *ops;
134 struct zpool *zpool; 147 struct zpool *zpool;
135 const struct zpool_ops *zpool_ops; 148 const struct zpool_ops *zpool_ops;
@@ -164,11 +177,65 @@ static int size_to_chunks(size_t size)
164 177
165static void compact_page_work(struct work_struct *w); 178static void compact_page_work(struct work_struct *w);
166 179
180static inline struct z3fold_buddy_slots *alloc_slots(struct z3fold_pool *pool)
181{
182 struct z3fold_buddy_slots *slots = kmem_cache_alloc(pool->c_handle,
183 GFP_KERNEL);
184
185 if (slots) {
186 memset(slots->slot, 0, sizeof(slots->slot));
187 slots->pool = (unsigned long)pool;
188 }
189
190 return slots;
191}
192
193static inline struct z3fold_pool *slots_to_pool(struct z3fold_buddy_slots *s)
194{
195 return (struct z3fold_pool *)(s->pool & ~HANDLE_FLAG_MASK);
196}
197
198static inline struct z3fold_buddy_slots *handle_to_slots(unsigned long handle)
199{
200 return (struct z3fold_buddy_slots *)(handle & ~(SLOTS_ALIGN - 1));
201}
202
203static inline void free_handle(unsigned long handle)
204{
205 struct z3fold_buddy_slots *slots;
206 int i;
207 bool is_free;
208
209 if (handle & (1 << PAGE_HEADLESS))
210 return;
211
212 WARN_ON(*(unsigned long *)handle == 0);
213 *(unsigned long *)handle = 0;
214 slots = handle_to_slots(handle);
215 is_free = true;
216 for (i = 0; i <= BUDDY_MASK; i++) {
217 if (slots->slot[i]) {
218 is_free = false;
219 break;
220 }
221 }
222
223 if (is_free) {
224 struct z3fold_pool *pool = slots_to_pool(slots);
225
226 kmem_cache_free(pool->c_handle, slots);
227 }
228}
229
167/* Initializes the z3fold header of a newly allocated z3fold page */ 230/* Initializes the z3fold header of a newly allocated z3fold page */
168static struct z3fold_header *init_z3fold_page(struct page *page, 231static struct z3fold_header *init_z3fold_page(struct page *page,
169 struct z3fold_pool *pool) 232 struct z3fold_pool *pool)
170{ 233{
171 struct z3fold_header *zhdr = page_address(page); 234 struct z3fold_header *zhdr = page_address(page);
235 struct z3fold_buddy_slots *slots = alloc_slots(pool);
236
237 if (!slots)
238 return NULL;
172 239
173 INIT_LIST_HEAD(&page->lru); 240 INIT_LIST_HEAD(&page->lru);
174 clear_bit(PAGE_HEADLESS, &page->private); 241 clear_bit(PAGE_HEADLESS, &page->private);
@@ -185,7 +252,7 @@ static struct z3fold_header *init_z3fold_page(struct page *page,
185 zhdr->first_num = 0; 252 zhdr->first_num = 0;
186 zhdr->start_middle = 0; 253 zhdr->start_middle = 0;
187 zhdr->cpu = -1; 254 zhdr->cpu = -1;
188 zhdr->pool = pool; 255 zhdr->slots = slots;
189 INIT_LIST_HEAD(&zhdr->buddy); 256 INIT_LIST_HEAD(&zhdr->buddy);
190 INIT_WORK(&zhdr->work, compact_page_work); 257 INIT_WORK(&zhdr->work, compact_page_work);
191 return zhdr; 258 return zhdr;
@@ -215,33 +282,57 @@ static inline void z3fold_page_unlock(struct z3fold_header *zhdr)
215 spin_unlock(&zhdr->page_lock); 282 spin_unlock(&zhdr->page_lock);
216} 283}
217 284
285/* Helper function to build the index */
286static inline int __idx(struct z3fold_header *zhdr, enum buddy bud)
287{
288 return (bud + zhdr->first_num) & BUDDY_MASK;
289}
290
218/* 291/*
219 * Encodes the handle of a particular buddy within a z3fold page 292 * Encodes the handle of a particular buddy within a z3fold page
220 * Pool lock should be held as this function accesses first_num 293 * Pool lock should be held as this function accesses first_num
221 */ 294 */
222static unsigned long encode_handle(struct z3fold_header *zhdr, enum buddy bud) 295static unsigned long encode_handle(struct z3fold_header *zhdr, enum buddy bud)
223{ 296{
224 unsigned long handle; 297 struct z3fold_buddy_slots *slots;
298 unsigned long h = (unsigned long)zhdr;
299 int idx = 0;
225 300
226 handle = (unsigned long)zhdr; 301 /*
227 if (bud != HEADLESS) { 302 * For a headless page, its handle is its pointer with the extra
228 handle |= (bud + zhdr->first_num) & BUDDY_MASK; 303 * PAGE_HEADLESS bit set
229 if (bud == LAST) 304 */
230 handle |= (zhdr->last_chunks << BUDDY_SHIFT); 305 if (bud == HEADLESS)
231 } 306 return h | (1 << PAGE_HEADLESS);
232 return handle; 307
308 /* otherwise, return pointer to encoded handle */
309 idx = __idx(zhdr, bud);
310 h += idx;
311 if (bud == LAST)
312 h |= (zhdr->last_chunks << BUDDY_SHIFT);
313
314 slots = zhdr->slots;
315 slots->slot[idx] = h;
316 return (unsigned long)&slots->slot[idx];
233} 317}
234 318
235/* Returns the z3fold page where a given handle is stored */ 319/* Returns the z3fold page where a given handle is stored */
236static struct z3fold_header *handle_to_z3fold_header(unsigned long handle) 320static inline struct z3fold_header *handle_to_z3fold_header(unsigned long handle)
237{ 321{
238 return (struct z3fold_header *)(handle & PAGE_MASK); 322 unsigned long addr = handle;
323
324 if (!(addr & (1 << PAGE_HEADLESS)))
325 addr = *(unsigned long *)handle;
326
327 return (struct z3fold_header *)(addr & PAGE_MASK);
239} 328}
240 329
241/* only for LAST bud, returns zero otherwise */ 330/* only for LAST bud, returns zero otherwise */
242static unsigned short handle_to_chunks(unsigned long handle) 331static unsigned short handle_to_chunks(unsigned long handle)
243{ 332{
244 return (handle & ~PAGE_MASK) >> BUDDY_SHIFT; 333 unsigned long addr = *(unsigned long *)handle;
334
335 return (addr & ~PAGE_MASK) >> BUDDY_SHIFT;
245} 336}
246 337
247/* 338/*
@@ -251,13 +342,18 @@ static unsigned short handle_to_chunks(unsigned long handle)
251 */ 342 */
252static enum buddy handle_to_buddy(unsigned long handle) 343static enum buddy handle_to_buddy(unsigned long handle)
253{ 344{
254 struct z3fold_header *zhdr = handle_to_z3fold_header(handle); 345 struct z3fold_header *zhdr;
255 return (handle - zhdr->first_num) & BUDDY_MASK; 346 unsigned long addr;
347
348 WARN_ON(handle & (1 << PAGE_HEADLESS));
349 addr = *(unsigned long *)handle;
350 zhdr = (struct z3fold_header *)(addr & PAGE_MASK);
351 return (addr - zhdr->first_num) & BUDDY_MASK;
256} 352}
257 353
258static inline struct z3fold_pool *zhdr_to_pool(struct z3fold_header *zhdr) 354static inline struct z3fold_pool *zhdr_to_pool(struct z3fold_header *zhdr)
259{ 355{
260 return zhdr->pool; 356 return slots_to_pool(zhdr->slots);
261} 357}
262 358
263static void __release_z3fold_page(struct z3fold_header *zhdr, bool locked) 359static void __release_z3fold_page(struct z3fold_header *zhdr, bool locked)
@@ -583,6 +679,11 @@ static struct z3fold_pool *z3fold_create_pool(const char *name, gfp_t gfp,
583 pool = kzalloc(sizeof(struct z3fold_pool), gfp); 679 pool = kzalloc(sizeof(struct z3fold_pool), gfp);
584 if (!pool) 680 if (!pool)
585 goto out; 681 goto out;
682 pool->c_handle = kmem_cache_create("z3fold_handle",
683 sizeof(struct z3fold_buddy_slots),
684 SLOTS_ALIGN, 0, NULL);
685 if (!pool->c_handle)
686 goto out_c;
586 spin_lock_init(&pool->lock); 687 spin_lock_init(&pool->lock);
587 spin_lock_init(&pool->stale_lock); 688 spin_lock_init(&pool->stale_lock);
588 pool->unbuddied = __alloc_percpu(sizeof(struct list_head)*NCHUNKS, 2); 689 pool->unbuddied = __alloc_percpu(sizeof(struct list_head)*NCHUNKS, 2);
@@ -613,6 +714,8 @@ out_wq:
613out_unbuddied: 714out_unbuddied:
614 free_percpu(pool->unbuddied); 715 free_percpu(pool->unbuddied);
615out_pool: 716out_pool:
717 kmem_cache_destroy(pool->c_handle);
718out_c:
616 kfree(pool); 719 kfree(pool);
617out: 720out:
618 return NULL; 721 return NULL;
@@ -626,6 +729,7 @@ out:
626 */ 729 */
627static void z3fold_destroy_pool(struct z3fold_pool *pool) 730static void z3fold_destroy_pool(struct z3fold_pool *pool)
628{ 731{
732 kmem_cache_destroy(pool->c_handle);
629 destroy_workqueue(pool->release_wq); 733 destroy_workqueue(pool->release_wq);
630 destroy_workqueue(pool->compact_wq); 734 destroy_workqueue(pool->compact_wq);
631 kfree(pool); 735 kfree(pool);
@@ -818,6 +922,7 @@ static void z3fold_free(struct z3fold_pool *pool, unsigned long handle)
818 return; 922 return;
819 } 923 }
820 924
925 free_handle(handle);
821 if (kref_put(&zhdr->refcount, release_z3fold_page_locked_list)) { 926 if (kref_put(&zhdr->refcount, release_z3fold_page_locked_list)) {
822 atomic64_dec(&pool->pages_nr); 927 atomic64_dec(&pool->pages_nr);
823 return; 928 return;