1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
24
25#include <linux/atomic.h>
26#include <linux/list.h>
27#include <linux/mm.h>
28#include <linux/module.h>
29#include <linux/preempt.h>
30#include <linux/slab.h>
31#include <linux/spinlock.h>
32#include <linux/zpool.h>
33
34
35
36
37
38
39
40
41
42
43
44
45
46#define NCHUNKS_ORDER 6
47
48#define CHUNK_SHIFT (PAGE_SHIFT - NCHUNKS_ORDER)
49#define CHUNK_SIZE (1 << CHUNK_SHIFT)
50#define ZHDR_SIZE_ALIGNED CHUNK_SIZE
51#define NCHUNKS ((PAGE_SIZE - ZHDR_SIZE_ALIGNED) >> CHUNK_SHIFT)
52
53#define BUDDY_MASK ((1 << NCHUNKS_ORDER) - 1)
54
55struct z3fold_pool;
56struct z3fold_ops {
57 int (*evict)(struct z3fold_pool *pool, unsigned long handle);
58};
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78struct z3fold_pool {
79 spinlock_t lock;
80 struct list_head unbuddied[NCHUNKS];
81 struct list_head buddied;
82 struct list_head lru;
83 u64 pages_nr;
84 const struct z3fold_ops *ops;
85 struct zpool *zpool;
86 const struct zpool_ops *zpool_ops;
87};
88
89enum buddy {
90 HEADLESS = 0,
91 FIRST,
92 MIDDLE,
93 LAST,
94 BUDDIES_MAX
95};
96
97
98
99
100
101
102
103
104
105
106struct z3fold_header {
107 struct list_head buddy;
108 unsigned short first_chunks;
109 unsigned short middle_chunks;
110 unsigned short last_chunks;
111 unsigned short start_middle;
112 unsigned short first_num:NCHUNKS_ORDER;
113};
114
115
116
117
118enum z3fold_page_flags {
119 UNDER_RECLAIM = 0,
120 PAGE_HEADLESS,
121 MIDDLE_CHUNK_MAPPED,
122};
123
124
125
126
127
128
129static int size_to_chunks(size_t size)
130{
131 return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT;
132}
133
134#define for_each_unbuddied_list(_iter, _begin) \
135 for ((_iter) = (_begin); (_iter) < NCHUNKS; (_iter)++)
136
137
138static struct z3fold_header *init_z3fold_page(struct page *page)
139{
140 struct z3fold_header *zhdr = page_address(page);
141
142 INIT_LIST_HEAD(&page->lru);
143 clear_bit(UNDER_RECLAIM, &page->private);
144 clear_bit(PAGE_HEADLESS, &page->private);
145 clear_bit(MIDDLE_CHUNK_MAPPED, &page->private);
146
147 zhdr->first_chunks = 0;
148 zhdr->middle_chunks = 0;
149 zhdr->last_chunks = 0;
150 zhdr->first_num = 0;
151 zhdr->start_middle = 0;
152 INIT_LIST_HEAD(&zhdr->buddy);
153 return zhdr;
154}
155
156
157static void free_z3fold_page(struct z3fold_header *zhdr)
158{
159 __free_page(virt_to_page(zhdr));
160}
161
162
163
164
165
166static unsigned long encode_handle(struct z3fold_header *zhdr, enum buddy bud)
167{
168 unsigned long handle;
169
170 handle = (unsigned long)zhdr;
171 if (bud != HEADLESS)
172 handle += (bud + zhdr->first_num) & BUDDY_MASK;
173 return handle;
174}
175
176
177static struct z3fold_header *handle_to_z3fold_header(unsigned long handle)
178{
179 return (struct z3fold_header *)(handle & PAGE_MASK);
180}
181
182
183static enum buddy handle_to_buddy(unsigned long handle)
184{
185 struct z3fold_header *zhdr = handle_to_z3fold_header(handle);
186 return (handle - zhdr->first_num) & BUDDY_MASK;
187}
188
189
190
191
192
193static int num_free_chunks(struct z3fold_header *zhdr)
194{
195 int nfree;
196
197
198
199
200
201 if (zhdr->middle_chunks != 0) {
202 int nfree_before = zhdr->first_chunks ?
203 0 : zhdr->start_middle - 1;
204 int nfree_after = zhdr->last_chunks ?
205 0 : NCHUNKS - zhdr->start_middle - zhdr->middle_chunks;
206 nfree = max(nfree_before, nfree_after);
207 } else
208 nfree = NCHUNKS - zhdr->first_chunks - zhdr->last_chunks;
209 return nfree;
210}
211
212
213
214
215
216
217
218
219
220
221
222
223static struct z3fold_pool *z3fold_create_pool(gfp_t gfp,
224 const struct z3fold_ops *ops)
225{
226 struct z3fold_pool *pool;
227 int i;
228
229 pool = kzalloc(sizeof(struct z3fold_pool), gfp);
230 if (!pool)
231 return NULL;
232 spin_lock_init(&pool->lock);
233 for_each_unbuddied_list(i, 0)
234 INIT_LIST_HEAD(&pool->unbuddied[i]);
235 INIT_LIST_HEAD(&pool->buddied);
236 INIT_LIST_HEAD(&pool->lru);
237 pool->pages_nr = 0;
238 pool->ops = ops;
239 return pool;
240}
241
242
243
244
245
246
247
248static void z3fold_destroy_pool(struct z3fold_pool *pool)
249{
250 kfree(pool);
251}
252
253
254static int z3fold_compact_page(struct z3fold_header *zhdr)
255{
256 struct page *page = virt_to_page(zhdr);
257 void *beg = zhdr;
258
259
260 if (!test_bit(MIDDLE_CHUNK_MAPPED, &page->private) &&
261 zhdr->middle_chunks != 0 &&
262 zhdr->first_chunks == 0 && zhdr->last_chunks == 0) {
263 memmove(beg + ZHDR_SIZE_ALIGNED,
264 beg + (zhdr->start_middle << CHUNK_SHIFT),
265 zhdr->middle_chunks << CHUNK_SHIFT);
266 zhdr->first_chunks = zhdr->middle_chunks;
267 zhdr->middle_chunks = 0;
268 zhdr->start_middle = 0;
269 zhdr->first_num++;
270 return 1;
271 }
272 return 0;
273}
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294static int z3fold_alloc(struct z3fold_pool *pool, size_t size, gfp_t gfp,
295 unsigned long *handle)
296{
297 int chunks = 0, i, freechunks;
298 struct z3fold_header *zhdr = NULL;
299 enum buddy bud;
300 struct page *page;
301
302 if (!size || (gfp & __GFP_HIGHMEM))
303 return -EINVAL;
304
305 if (size > PAGE_SIZE)
306 return -ENOSPC;
307
308 if (size > PAGE_SIZE - ZHDR_SIZE_ALIGNED - CHUNK_SIZE)
309 bud = HEADLESS;
310 else {
311 chunks = size_to_chunks(size);
312 spin_lock(&pool->lock);
313
314
315 zhdr = NULL;
316 for_each_unbuddied_list(i, chunks) {
317 if (!list_empty(&pool->unbuddied[i])) {
318 zhdr = list_first_entry(&pool->unbuddied[i],
319 struct z3fold_header, buddy);
320 page = virt_to_page(zhdr);
321 if (zhdr->first_chunks == 0) {
322 if (zhdr->middle_chunks != 0 &&
323 chunks >= zhdr->start_middle)
324 bud = LAST;
325 else
326 bud = FIRST;
327 } else if (zhdr->last_chunks == 0)
328 bud = LAST;
329 else if (zhdr->middle_chunks == 0)
330 bud = MIDDLE;
331 else {
332 pr_err("No free chunks in unbuddied\n");
333 WARN_ON(1);
334 continue;
335 }
336 list_del(&zhdr->buddy);
337 goto found;
338 }
339 }
340 bud = FIRST;
341 spin_unlock(&pool->lock);
342 }
343
344
345 page = alloc_page(gfp);
346 if (!page)
347 return -ENOMEM;
348 spin_lock(&pool->lock);
349 pool->pages_nr++;
350 zhdr = init_z3fold_page(page);
351
352 if (bud == HEADLESS) {
353 set_bit(PAGE_HEADLESS, &page->private);
354 goto headless;
355 }
356
357found:
358 if (bud == FIRST)
359 zhdr->first_chunks = chunks;
360 else if (bud == LAST)
361 zhdr->last_chunks = chunks;
362 else {
363 zhdr->middle_chunks = chunks;
364 zhdr->start_middle = zhdr->first_chunks + 1;
365 }
366
367 if (zhdr->first_chunks == 0 || zhdr->last_chunks == 0 ||
368 zhdr->middle_chunks == 0) {
369
370 freechunks = num_free_chunks(zhdr);
371 list_add(&zhdr->buddy, &pool->unbuddied[freechunks]);
372 } else {
373
374 list_add(&zhdr->buddy, &pool->buddied);
375 }
376
377headless:
378
379 if (!list_empty(&page->lru))
380 list_del(&page->lru);
381
382 list_add(&page->lru, &pool->lru);
383
384 *handle = encode_handle(zhdr, bud);
385 spin_unlock(&pool->lock);
386
387 return 0;
388}
389
390
391
392
393
394
395
396
397
398
399
400static void z3fold_free(struct z3fold_pool *pool, unsigned long handle)
401{
402 struct z3fold_header *zhdr;
403 int freechunks;
404 struct page *page;
405 enum buddy bud;
406
407 spin_lock(&pool->lock);
408 zhdr = handle_to_z3fold_header(handle);
409 page = virt_to_page(zhdr);
410
411 if (test_bit(PAGE_HEADLESS, &page->private)) {
412
413 bud = HEADLESS;
414 } else {
415 bud = handle_to_buddy(handle);
416
417 switch (bud) {
418 case FIRST:
419 zhdr->first_chunks = 0;
420 break;
421 case MIDDLE:
422 zhdr->middle_chunks = 0;
423 zhdr->start_middle = 0;
424 break;
425 case LAST:
426 zhdr->last_chunks = 0;
427 break;
428 default:
429 pr_err("%s: unknown bud %d\n", __func__, bud);
430 WARN_ON(1);
431 spin_unlock(&pool->lock);
432 return;
433 }
434 }
435
436 if (test_bit(UNDER_RECLAIM, &page->private)) {
437
438 spin_unlock(&pool->lock);
439 return;
440 }
441
442 if (bud != HEADLESS) {
443
444 list_del(&zhdr->buddy);
445 }
446
447 if (bud == HEADLESS ||
448 (zhdr->first_chunks == 0 && zhdr->middle_chunks == 0 &&
449 zhdr->last_chunks == 0)) {
450
451 list_del(&page->lru);
452 clear_bit(PAGE_HEADLESS, &page->private);
453 free_z3fold_page(zhdr);
454 pool->pages_nr--;
455 } else {
456 z3fold_compact_page(zhdr);
457
458 freechunks = num_free_chunks(zhdr);
459 list_add(&zhdr->buddy, &pool->unbuddied[freechunks]);
460 }
461
462 spin_unlock(&pool->lock);
463}
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501static int z3fold_reclaim_page(struct z3fold_pool *pool, unsigned int retries)
502{
503 int i, ret = 0, freechunks;
504 struct z3fold_header *zhdr;
505 struct page *page;
506 unsigned long first_handle = 0, middle_handle = 0, last_handle = 0;
507
508 spin_lock(&pool->lock);
509 if (!pool->ops || !pool->ops->evict || list_empty(&pool->lru) ||
510 retries == 0) {
511 spin_unlock(&pool->lock);
512 return -EINVAL;
513 }
514 for (i = 0; i < retries; i++) {
515 page = list_last_entry(&pool->lru, struct page, lru);
516 list_del(&page->lru);
517
518
519 set_bit(UNDER_RECLAIM, &page->private);
520 zhdr = page_address(page);
521 if (!test_bit(PAGE_HEADLESS, &page->private)) {
522 list_del(&zhdr->buddy);
523
524
525
526
527
528 first_handle = 0;
529 last_handle = 0;
530 middle_handle = 0;
531 if (zhdr->first_chunks)
532 first_handle = encode_handle(zhdr, FIRST);
533 if (zhdr->middle_chunks)
534 middle_handle = encode_handle(zhdr, MIDDLE);
535 if (zhdr->last_chunks)
536 last_handle = encode_handle(zhdr, LAST);
537 } else {
538 first_handle = encode_handle(zhdr, HEADLESS);
539 last_handle = middle_handle = 0;
540 }
541
542 spin_unlock(&pool->lock);
543
544
545 if (middle_handle) {
546 ret = pool->ops->evict(pool, middle_handle);
547 if (ret)
548 goto next;
549 }
550 if (first_handle) {
551 ret = pool->ops->evict(pool, first_handle);
552 if (ret)
553 goto next;
554 }
555 if (last_handle) {
556 ret = pool->ops->evict(pool, last_handle);
557 if (ret)
558 goto next;
559 }
560next:
561 spin_lock(&pool->lock);
562 clear_bit(UNDER_RECLAIM, &page->private);
563 if ((test_bit(PAGE_HEADLESS, &page->private) && ret == 0) ||
564 (zhdr->first_chunks == 0 && zhdr->last_chunks == 0 &&
565 zhdr->middle_chunks == 0)) {
566
567
568
569
570 clear_bit(PAGE_HEADLESS, &page->private);
571 free_z3fold_page(zhdr);
572 pool->pages_nr--;
573 spin_unlock(&pool->lock);
574 return 0;
575 } else if (!test_bit(PAGE_HEADLESS, &page->private)) {
576 if (zhdr->first_chunks != 0 &&
577 zhdr->last_chunks != 0 &&
578 zhdr->middle_chunks != 0) {
579
580 list_add(&zhdr->buddy, &pool->buddied);
581 } else {
582 z3fold_compact_page(zhdr);
583
584 freechunks = num_free_chunks(zhdr);
585 list_add(&zhdr->buddy,
586 &pool->unbuddied[freechunks]);
587 }
588 }
589
590
591 list_add(&page->lru, &pool->lru);
592 }
593 spin_unlock(&pool->lock);
594 return -EAGAIN;
595}
596
597
598
599
600
601
602
603
604
605
606
607static void *z3fold_map(struct z3fold_pool *pool, unsigned long handle)
608{
609 struct z3fold_header *zhdr;
610 struct page *page;
611 void *addr;
612 enum buddy buddy;
613
614 spin_lock(&pool->lock);
615 zhdr = handle_to_z3fold_header(handle);
616 addr = zhdr;
617 page = virt_to_page(zhdr);
618
619 if (test_bit(PAGE_HEADLESS, &page->private))
620 goto out;
621
622 buddy = handle_to_buddy(handle);
623 switch (buddy) {
624 case FIRST:
625 addr += ZHDR_SIZE_ALIGNED;
626 break;
627 case MIDDLE:
628 addr += zhdr->start_middle << CHUNK_SHIFT;
629 set_bit(MIDDLE_CHUNK_MAPPED, &page->private);
630 break;
631 case LAST:
632 addr += PAGE_SIZE - (zhdr->last_chunks << CHUNK_SHIFT);
633 break;
634 default:
635 pr_err("unknown buddy id %d\n", buddy);
636 WARN_ON(1);
637 addr = NULL;
638 break;
639 }
640out:
641 spin_unlock(&pool->lock);
642 return addr;
643}
644
645
646
647
648
649
650static void z3fold_unmap(struct z3fold_pool *pool, unsigned long handle)
651{
652 struct z3fold_header *zhdr;
653 struct page *page;
654 enum buddy buddy;
655
656 spin_lock(&pool->lock);
657 zhdr = handle_to_z3fold_header(handle);
658 page = virt_to_page(zhdr);
659
660 if (test_bit(PAGE_HEADLESS, &page->private)) {
661 spin_unlock(&pool->lock);
662 return;
663 }
664
665 buddy = handle_to_buddy(handle);
666 if (buddy == MIDDLE)
667 clear_bit(MIDDLE_CHUNK_MAPPED, &page->private);
668 spin_unlock(&pool->lock);
669}
670
671
672
673
674
675
676
677
678static u64 z3fold_get_pool_size(struct z3fold_pool *pool)
679{
680 return pool->pages_nr;
681}
682
683
684
685
686
687static int z3fold_zpool_evict(struct z3fold_pool *pool, unsigned long handle)
688{
689 if (pool->zpool && pool->zpool_ops && pool->zpool_ops->evict)
690 return pool->zpool_ops->evict(pool->zpool, handle);
691 else
692 return -ENOENT;
693}
694
695static const struct z3fold_ops z3fold_zpool_ops = {
696 .evict = z3fold_zpool_evict
697};
698
699static void *z3fold_zpool_create(const char *name, gfp_t gfp,
700 const struct zpool_ops *zpool_ops,
701 struct zpool *zpool)
702{
703 struct z3fold_pool *pool;
704
705 pool = z3fold_create_pool(gfp, zpool_ops ? &z3fold_zpool_ops : NULL);
706 if (pool) {
707 pool->zpool = zpool;
708 pool->zpool_ops = zpool_ops;
709 }
710 return pool;
711}
712
713static void z3fold_zpool_destroy(void *pool)
714{
715 z3fold_destroy_pool(pool);
716}
717
718static int z3fold_zpool_malloc(void *pool, size_t size, gfp_t gfp,
719 unsigned long *handle)
720{
721 return z3fold_alloc(pool, size, gfp, handle);
722}
723static void z3fold_zpool_free(void *pool, unsigned long handle)
724{
725 z3fold_free(pool, handle);
726}
727
728static int z3fold_zpool_shrink(void *pool, unsigned int pages,
729 unsigned int *reclaimed)
730{
731 unsigned int total = 0;
732 int ret = -EINVAL;
733
734 while (total < pages) {
735 ret = z3fold_reclaim_page(pool, 8);
736 if (ret < 0)
737 break;
738 total++;
739 }
740
741 if (reclaimed)
742 *reclaimed = total;
743
744 return ret;
745}
746
747static void *z3fold_zpool_map(void *pool, unsigned long handle,
748 enum zpool_mapmode mm)
749{
750 return z3fold_map(pool, handle);
751}
752static void z3fold_zpool_unmap(void *pool, unsigned long handle)
753{
754 z3fold_unmap(pool, handle);
755}
756
757static u64 z3fold_zpool_total_size(void *pool)
758{
759 return z3fold_get_pool_size(pool) * PAGE_SIZE;
760}
761
762static struct zpool_driver z3fold_zpool_driver = {
763 .type = "z3fold",
764 .owner = THIS_MODULE,
765 .create = z3fold_zpool_create,
766 .destroy = z3fold_zpool_destroy,
767 .malloc = z3fold_zpool_malloc,
768 .free = z3fold_zpool_free,
769 .shrink = z3fold_zpool_shrink,
770 .map = z3fold_zpool_map,
771 .unmap = z3fold_zpool_unmap,
772 .total_size = z3fold_zpool_total_size,
773};
774
775MODULE_ALIAS("zpool-z3fold");
776
777static int __init init_z3fold(void)
778{
779
780 BUILD_BUG_ON(sizeof(struct z3fold_header) > ZHDR_SIZE_ALIGNED);
781 zpool_register_driver(&z3fold_zpool_driver);
782
783 return 0;
784}
785
786static void __exit exit_z3fold(void)
787{
788 zpool_unregister_driver(&z3fold_zpool_driver);
789}
790
791module_init(init_z3fold);
792module_exit(exit_z3fold);
793
794MODULE_LICENSE("GPL");
795MODULE_AUTHOR("Vitaly Wool <vitalywool@gmail.com>");
796MODULE_DESCRIPTION("3-Fold Allocator for Compressed Pages");
797