xref: /linux/mm/z3fold.c (revision af4798a5bba60af881626780cbc6457cb162bfd1)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29a001fc1SVitaly Wool /*
39a001fc1SVitaly Wool  * z3fold.c
49a001fc1SVitaly Wool  *
59a001fc1SVitaly Wool  * Author: Vitaly Wool <vitaly.wool@konsulko.com>
69a001fc1SVitaly Wool  * Copyright (C) 2016, Sony Mobile Communications Inc.
79a001fc1SVitaly Wool  *
89a001fc1SVitaly Wool  * This implementation is based on zbud written by Seth Jennings.
99a001fc1SVitaly Wool  *
109a001fc1SVitaly Wool  * z3fold is an special purpose allocator for storing compressed pages. It
119a001fc1SVitaly Wool  * can store up to three compressed pages per page which improves the
129a001fc1SVitaly Wool  * compression ratio of zbud while retaining its main concepts (e. g. always
139a001fc1SVitaly Wool  * storing an integral number of objects per page) and simplicity.
149a001fc1SVitaly Wool  * It still has simple and deterministic reclaim properties that make it
159a001fc1SVitaly Wool  * preferable to a higher density approach (with no requirement on integral
169a001fc1SVitaly Wool  * number of object per page) when reclaim is used.
179a001fc1SVitaly Wool  *
189a001fc1SVitaly Wool  * As in zbud, pages are divided into "chunks".  The size of the chunks is
199a001fc1SVitaly Wool  * fixed at compile time and is determined by NCHUNKS_ORDER below.
209a001fc1SVitaly Wool  *
219a001fc1SVitaly Wool  * z3fold doesn't export any API and is meant to be used via zpool API.
229a001fc1SVitaly Wool  */
239a001fc1SVitaly Wool 
249a001fc1SVitaly Wool #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
259a001fc1SVitaly Wool 
269a001fc1SVitaly Wool #include <linux/atomic.h>
27d30561c5SVitaly Wool #include <linux/sched.h>
281f862989SVitaly Wool #include <linux/cpumask.h>
299a001fc1SVitaly Wool #include <linux/list.h>
309a001fc1SVitaly Wool #include <linux/mm.h>
319a001fc1SVitaly Wool #include <linux/module.h>
321f862989SVitaly Wool #include <linux/page-flags.h>
331f862989SVitaly Wool #include <linux/migrate.h>
341f862989SVitaly Wool #include <linux/node.h>
351f862989SVitaly Wool #include <linux/compaction.h>
36d30561c5SVitaly Wool #include <linux/percpu.h>
371f862989SVitaly Wool #include <linux/mount.h>
38ea8157abSDavid Howells #include <linux/pseudo_fs.h>
391f862989SVitaly Wool #include <linux/fs.h>
409a001fc1SVitaly Wool #include <linux/preempt.h>
41d30561c5SVitaly Wool #include <linux/workqueue.h>
429a001fc1SVitaly Wool #include <linux/slab.h>
439a001fc1SVitaly Wool #include <linux/spinlock.h>
449a001fc1SVitaly Wool #include <linux/zpool.h>
45ea8157abSDavid Howells #include <linux/magic.h>
46*af4798a5SQian Cai #include <linux/kmemleak.h>
479a001fc1SVitaly Wool 
489a001fc1SVitaly Wool /*
499a001fc1SVitaly Wool  * NCHUNKS_ORDER determines the internal allocation granularity, effectively
509a001fc1SVitaly Wool  * adjusting internal fragmentation.  It also determines the number of
519a001fc1SVitaly Wool  * freelists maintained in each pool. NCHUNKS_ORDER of 6 means that the
52ede93213SVitaly Wool  * allocation granularity will be in chunks of size PAGE_SIZE/64. Some chunks
53ede93213SVitaly Wool  * in the beginning of an allocated page are occupied by z3fold header, so
54ede93213SVitaly Wool  * NCHUNKS will be calculated to 63 (or 62 in case CONFIG_DEBUG_SPINLOCK=y),
55ede93213SVitaly Wool  * which shows the max number of free chunks in z3fold page, also there will
56ede93213SVitaly Wool  * be 63, or 62, respectively, freelists per pool.
579a001fc1SVitaly Wool  */
589a001fc1SVitaly Wool #define NCHUNKS_ORDER	6
599a001fc1SVitaly Wool 
609a001fc1SVitaly Wool #define CHUNK_SHIFT	(PAGE_SHIFT - NCHUNKS_ORDER)
619a001fc1SVitaly Wool #define CHUNK_SIZE	(1 << CHUNK_SHIFT)
62ede93213SVitaly Wool #define ZHDR_SIZE_ALIGNED round_up(sizeof(struct z3fold_header), CHUNK_SIZE)
63ede93213SVitaly Wool #define ZHDR_CHUNKS	(ZHDR_SIZE_ALIGNED >> CHUNK_SHIFT)
64ede93213SVitaly Wool #define TOTAL_CHUNKS	(PAGE_SIZE >> CHUNK_SHIFT)
659a001fc1SVitaly Wool #define NCHUNKS		((PAGE_SIZE - ZHDR_SIZE_ALIGNED) >> CHUNK_SHIFT)
669a001fc1SVitaly Wool 
67f201ebd8Szhong jiang #define BUDDY_MASK	(0x3)
68ca0246bbSVitaly Wool #define BUDDY_SHIFT	2
697c2b8baaSVitaly Wool #define SLOTS_ALIGN	(0x40)
707c2b8baaSVitaly Wool 
717c2b8baaSVitaly Wool /*****************
727c2b8baaSVitaly Wool  * Structures
737c2b8baaSVitaly Wool *****************/
747c2b8baaSVitaly Wool struct z3fold_pool;
757c2b8baaSVitaly Wool struct z3fold_ops {
767c2b8baaSVitaly Wool 	int (*evict)(struct z3fold_pool *pool, unsigned long handle);
777c2b8baaSVitaly Wool };
787c2b8baaSVitaly Wool 
797c2b8baaSVitaly Wool enum buddy {
807c2b8baaSVitaly Wool 	HEADLESS = 0,
817c2b8baaSVitaly Wool 	FIRST,
827c2b8baaSVitaly Wool 	MIDDLE,
837c2b8baaSVitaly Wool 	LAST,
847c2b8baaSVitaly Wool 	BUDDIES_MAX = LAST
857c2b8baaSVitaly Wool };
867c2b8baaSVitaly Wool 
877c2b8baaSVitaly Wool struct z3fold_buddy_slots {
887c2b8baaSVitaly Wool 	/*
897c2b8baaSVitaly Wool 	 * we are using BUDDY_MASK in handle_to_buddy etc. so there should
907c2b8baaSVitaly Wool 	 * be enough slots to hold all possible variants
917c2b8baaSVitaly Wool 	 */
927c2b8baaSVitaly Wool 	unsigned long slot[BUDDY_MASK + 1];
937c2b8baaSVitaly Wool 	unsigned long pool; /* back link + flags */
944a3ac931SVitaly Wool 	rwlock_t lock;
957c2b8baaSVitaly Wool };
967c2b8baaSVitaly Wool #define HANDLE_FLAG_MASK	(0x03)
977c2b8baaSVitaly Wool 
987c2b8baaSVitaly Wool /*
997c2b8baaSVitaly Wool  * struct z3fold_header - z3fold page metadata occupying first chunks of each
1007c2b8baaSVitaly Wool  *			z3fold page, except for HEADLESS pages
1017c2b8baaSVitaly Wool  * @buddy:		links the z3fold page into the relevant list in the
1027c2b8baaSVitaly Wool  *			pool
1037c2b8baaSVitaly Wool  * @page_lock:		per-page lock
1047c2b8baaSVitaly Wool  * @refcount:		reference count for the z3fold page
1057c2b8baaSVitaly Wool  * @work:		work_struct for page layout optimization
1067c2b8baaSVitaly Wool  * @slots:		pointer to the structure holding buddy slots
107bb9a374dSVitaly Wool  * @pool:		pointer to the containing pool
1087c2b8baaSVitaly Wool  * @cpu:		CPU which this page "belongs" to
1097c2b8baaSVitaly Wool  * @first_chunks:	the size of the first buddy in chunks, 0 if free
1107c2b8baaSVitaly Wool  * @middle_chunks:	the size of the middle buddy in chunks, 0 if free
1117c2b8baaSVitaly Wool  * @last_chunks:	the size of the last buddy in chunks, 0 if free
1127c2b8baaSVitaly Wool  * @first_num:		the starting number (for the first handle)
1131f862989SVitaly Wool  * @mapped_count:	the number of objects currently mapped
1147c2b8baaSVitaly Wool  */
1157c2b8baaSVitaly Wool struct z3fold_header {
1167c2b8baaSVitaly Wool 	struct list_head buddy;
1177c2b8baaSVitaly Wool 	spinlock_t page_lock;
1187c2b8baaSVitaly Wool 	struct kref refcount;
1197c2b8baaSVitaly Wool 	struct work_struct work;
1207c2b8baaSVitaly Wool 	struct z3fold_buddy_slots *slots;
121bb9a374dSVitaly Wool 	struct z3fold_pool *pool;
1227c2b8baaSVitaly Wool 	short cpu;
1237c2b8baaSVitaly Wool 	unsigned short first_chunks;
1247c2b8baaSVitaly Wool 	unsigned short middle_chunks;
1257c2b8baaSVitaly Wool 	unsigned short last_chunks;
1267c2b8baaSVitaly Wool 	unsigned short start_middle;
1277c2b8baaSVitaly Wool 	unsigned short first_num:2;
1281f862989SVitaly Wool 	unsigned short mapped_count:2;
1294a3ac931SVitaly Wool 	unsigned short foreign_handles:2;
1307c2b8baaSVitaly Wool };
1319a001fc1SVitaly Wool 
1329a001fc1SVitaly Wool /**
1339a001fc1SVitaly Wool  * struct z3fold_pool - stores metadata for each z3fold pool
134d30561c5SVitaly Wool  * @name:	pool name
135d30561c5SVitaly Wool  * @lock:	protects pool unbuddied/lru lists
136d30561c5SVitaly Wool  * @stale_lock:	protects pool stale page list
137d30561c5SVitaly Wool  * @unbuddied:	per-cpu array of lists tracking z3fold pages that contain 2-
138d30561c5SVitaly Wool  *		buddies; the list each z3fold page is added to depends on
139d30561c5SVitaly Wool  *		the size of its free region.
1409a001fc1SVitaly Wool  * @lru:	list tracking the z3fold pages in LRU order by most recently
1419a001fc1SVitaly Wool  *		added buddy.
142d30561c5SVitaly Wool  * @stale:	list of pages marked for freeing
1439a001fc1SVitaly Wool  * @pages_nr:	number of z3fold pages in the pool.
1447c2b8baaSVitaly Wool  * @c_handle:	cache for z3fold_buddy_slots allocation
1459a001fc1SVitaly Wool  * @ops:	pointer to a structure of user defined operations specified at
1469a001fc1SVitaly Wool  *		pool creation time.
147d30561c5SVitaly Wool  * @compact_wq:	workqueue for page layout background optimization
148d30561c5SVitaly Wool  * @release_wq:	workqueue for safe page release
149d30561c5SVitaly Wool  * @work:	work_struct for safe page release
1501f862989SVitaly Wool  * @inode:	inode for z3fold pseudo filesystem
1519a001fc1SVitaly Wool  *
1529a001fc1SVitaly Wool  * This structure is allocated at pool creation time and maintains metadata
1539a001fc1SVitaly Wool  * pertaining to a particular z3fold pool.
1549a001fc1SVitaly Wool  */
1559a001fc1SVitaly Wool struct z3fold_pool {
156d30561c5SVitaly Wool 	const char *name;
1579a001fc1SVitaly Wool 	spinlock_t lock;
158d30561c5SVitaly Wool 	spinlock_t stale_lock;
159d30561c5SVitaly Wool 	struct list_head *unbuddied;
1609a001fc1SVitaly Wool 	struct list_head lru;
161d30561c5SVitaly Wool 	struct list_head stale;
16212d59ae6SVitaly Wool 	atomic64_t pages_nr;
1637c2b8baaSVitaly Wool 	struct kmem_cache *c_handle;
1649a001fc1SVitaly Wool 	const struct z3fold_ops *ops;
1659a001fc1SVitaly Wool 	struct zpool *zpool;
1669a001fc1SVitaly Wool 	const struct zpool_ops *zpool_ops;
167d30561c5SVitaly Wool 	struct workqueue_struct *compact_wq;
168d30561c5SVitaly Wool 	struct workqueue_struct *release_wq;
169d30561c5SVitaly Wool 	struct work_struct work;
1701f862989SVitaly Wool 	struct inode *inode;
1719a001fc1SVitaly Wool };
1729a001fc1SVitaly Wool 
1739a001fc1SVitaly Wool /*
1749a001fc1SVitaly Wool  * Internal z3fold page flags
1759a001fc1SVitaly Wool  */
1769a001fc1SVitaly Wool enum z3fold_page_flags {
1775a27aa82SVitaly Wool 	PAGE_HEADLESS = 0,
1789a001fc1SVitaly Wool 	MIDDLE_CHUNK_MAPPED,
179d30561c5SVitaly Wool 	NEEDS_COMPACTING,
1806098d7e1SVitaly Wool 	PAGE_STALE,
181ca0246bbSVitaly Wool 	PAGE_CLAIMED, /* by either reclaim or free */
1829a001fc1SVitaly Wool };
1839a001fc1SVitaly Wool 
1844a3ac931SVitaly Wool /*
1854a3ac931SVitaly Wool  * handle flags, go under HANDLE_FLAG_MASK
1864a3ac931SVitaly Wool  */
1874a3ac931SVitaly Wool enum z3fold_handle_flags {
1884a3ac931SVitaly Wool 	HANDLES_ORPHANED = 0,
1894a3ac931SVitaly Wool };
1904a3ac931SVitaly Wool 
1914a3ac931SVitaly Wool /*
1924a3ac931SVitaly Wool  * Forward declarations
1934a3ac931SVitaly Wool  */
1944a3ac931SVitaly Wool static struct z3fold_header *__z3fold_alloc(struct z3fold_pool *, size_t, bool);
1954a3ac931SVitaly Wool static void compact_page_work(struct work_struct *w);
1964a3ac931SVitaly Wool 
1979a001fc1SVitaly Wool /*****************
1989a001fc1SVitaly Wool  * Helpers
1999a001fc1SVitaly Wool *****************/
2009a001fc1SVitaly Wool 
2019a001fc1SVitaly Wool /* Converts an allocation size in bytes to size in z3fold chunks */
2029a001fc1SVitaly Wool static int size_to_chunks(size_t size)
2039a001fc1SVitaly Wool {
2049a001fc1SVitaly Wool 	return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT;
2059a001fc1SVitaly Wool }
2069a001fc1SVitaly Wool 
2079a001fc1SVitaly Wool #define for_each_unbuddied_list(_iter, _begin) \
2089a001fc1SVitaly Wool 	for ((_iter) = (_begin); (_iter) < NCHUNKS; (_iter)++)
2099a001fc1SVitaly Wool 
210bb9f6f63SVitaly Wool static inline struct z3fold_buddy_slots *alloc_slots(struct z3fold_pool *pool,
211bb9f6f63SVitaly Wool 							gfp_t gfp)
2127c2b8baaSVitaly Wool {
213f1549cb5SHenry Burns 	struct z3fold_buddy_slots *slots;
214f1549cb5SHenry Burns 
215f1549cb5SHenry Burns 	slots = kmem_cache_alloc(pool->c_handle,
216f1549cb5SHenry Burns 				 (gfp & ~(__GFP_HIGHMEM | __GFP_MOVABLE)));
2177c2b8baaSVitaly Wool 
2187c2b8baaSVitaly Wool 	if (slots) {
219*af4798a5SQian Cai 		/* It will be freed separately in free_handle(). */
220*af4798a5SQian Cai 		kmemleak_not_leak(slots);
2217c2b8baaSVitaly Wool 		memset(slots->slot, 0, sizeof(slots->slot));
2227c2b8baaSVitaly Wool 		slots->pool = (unsigned long)pool;
2234a3ac931SVitaly Wool 		rwlock_init(&slots->lock);
2247c2b8baaSVitaly Wool 	}
2257c2b8baaSVitaly Wool 
2267c2b8baaSVitaly Wool 	return slots;
2277c2b8baaSVitaly Wool }
2287c2b8baaSVitaly Wool 
2297c2b8baaSVitaly Wool static inline struct z3fold_pool *slots_to_pool(struct z3fold_buddy_slots *s)
2307c2b8baaSVitaly Wool {
2317c2b8baaSVitaly Wool 	return (struct z3fold_pool *)(s->pool & ~HANDLE_FLAG_MASK);
2327c2b8baaSVitaly Wool }
2337c2b8baaSVitaly Wool 
2347c2b8baaSVitaly Wool static inline struct z3fold_buddy_slots *handle_to_slots(unsigned long handle)
2357c2b8baaSVitaly Wool {
2367c2b8baaSVitaly Wool 	return (struct z3fold_buddy_slots *)(handle & ~(SLOTS_ALIGN - 1));
2377c2b8baaSVitaly Wool }
2387c2b8baaSVitaly Wool 
2394a3ac931SVitaly Wool /* Lock a z3fold page */
2404a3ac931SVitaly Wool static inline void z3fold_page_lock(struct z3fold_header *zhdr)
2414a3ac931SVitaly Wool {
2424a3ac931SVitaly Wool 	spin_lock(&zhdr->page_lock);
2434a3ac931SVitaly Wool }
2444a3ac931SVitaly Wool 
2454a3ac931SVitaly Wool /* Try to lock a z3fold page */
2464a3ac931SVitaly Wool static inline int z3fold_page_trylock(struct z3fold_header *zhdr)
2474a3ac931SVitaly Wool {
2484a3ac931SVitaly Wool 	return spin_trylock(&zhdr->page_lock);
2494a3ac931SVitaly Wool }
2504a3ac931SVitaly Wool 
2514a3ac931SVitaly Wool /* Unlock a z3fold page */
2524a3ac931SVitaly Wool static inline void z3fold_page_unlock(struct z3fold_header *zhdr)
2534a3ac931SVitaly Wool {
2544a3ac931SVitaly Wool 	spin_unlock(&zhdr->page_lock);
2554a3ac931SVitaly Wool }
2564a3ac931SVitaly Wool 
2574a3ac931SVitaly Wool 
2584a3ac931SVitaly Wool static inline struct z3fold_header *__get_z3fold_header(unsigned long handle,
2594a3ac931SVitaly Wool 							bool lock)
2604a3ac931SVitaly Wool {
2614a3ac931SVitaly Wool 	struct z3fold_buddy_slots *slots;
2624a3ac931SVitaly Wool 	struct z3fold_header *zhdr;
2634a3ac931SVitaly Wool 	int locked = 0;
2644a3ac931SVitaly Wool 
2654a3ac931SVitaly Wool 	if (!(handle & (1 << PAGE_HEADLESS))) {
2664a3ac931SVitaly Wool 		slots = handle_to_slots(handle);
2674a3ac931SVitaly Wool 		do {
2684a3ac931SVitaly Wool 			unsigned long addr;
2694a3ac931SVitaly Wool 
2704a3ac931SVitaly Wool 			read_lock(&slots->lock);
2714a3ac931SVitaly Wool 			addr = *(unsigned long *)handle;
2724a3ac931SVitaly Wool 			zhdr = (struct z3fold_header *)(addr & PAGE_MASK);
2734a3ac931SVitaly Wool 			if (lock)
2744a3ac931SVitaly Wool 				locked = z3fold_page_trylock(zhdr);
2754a3ac931SVitaly Wool 			read_unlock(&slots->lock);
2764a3ac931SVitaly Wool 			if (locked)
2774a3ac931SVitaly Wool 				break;
2784a3ac931SVitaly Wool 			cpu_relax();
2794a3ac931SVitaly Wool 		} while (lock);
2804a3ac931SVitaly Wool 	} else {
2814a3ac931SVitaly Wool 		zhdr = (struct z3fold_header *)(handle & PAGE_MASK);
2824a3ac931SVitaly Wool 	}
2834a3ac931SVitaly Wool 
2844a3ac931SVitaly Wool 	return zhdr;
2854a3ac931SVitaly Wool }
2864a3ac931SVitaly Wool 
2874a3ac931SVitaly Wool /* Returns the z3fold page where a given handle is stored */
2884a3ac931SVitaly Wool static inline struct z3fold_header *handle_to_z3fold_header(unsigned long h)
2894a3ac931SVitaly Wool {
2904a3ac931SVitaly Wool 	return __get_z3fold_header(h, false);
2914a3ac931SVitaly Wool }
2924a3ac931SVitaly Wool 
2934a3ac931SVitaly Wool /* return locked z3fold page if it's not headless */
2944a3ac931SVitaly Wool static inline struct z3fold_header *get_z3fold_header(unsigned long h)
2954a3ac931SVitaly Wool {
2964a3ac931SVitaly Wool 	return __get_z3fold_header(h, true);
2974a3ac931SVitaly Wool }
2984a3ac931SVitaly Wool 
2994a3ac931SVitaly Wool static inline void put_z3fold_header(struct z3fold_header *zhdr)
3004a3ac931SVitaly Wool {
3014a3ac931SVitaly Wool 	struct page *page = virt_to_page(zhdr);
3024a3ac931SVitaly Wool 
3034a3ac931SVitaly Wool 	if (!test_bit(PAGE_HEADLESS, &page->private))
3044a3ac931SVitaly Wool 		z3fold_page_unlock(zhdr);
3054a3ac931SVitaly Wool }
3064a3ac931SVitaly Wool 
3077c2b8baaSVitaly Wool static inline void free_handle(unsigned long handle)
3087c2b8baaSVitaly Wool {
3097c2b8baaSVitaly Wool 	struct z3fold_buddy_slots *slots;
3104a3ac931SVitaly Wool 	struct z3fold_header *zhdr;
3117c2b8baaSVitaly Wool 	int i;
3127c2b8baaSVitaly Wool 	bool is_free;
3137c2b8baaSVitaly Wool 
3147c2b8baaSVitaly Wool 	if (handle & (1 << PAGE_HEADLESS))
3157c2b8baaSVitaly Wool 		return;
3167c2b8baaSVitaly Wool 
3174a3ac931SVitaly Wool 	if (WARN_ON(*(unsigned long *)handle == 0))
3184a3ac931SVitaly Wool 		return;
3194a3ac931SVitaly Wool 
3204a3ac931SVitaly Wool 	zhdr = handle_to_z3fold_header(handle);
3217c2b8baaSVitaly Wool 	slots = handle_to_slots(handle);
3224a3ac931SVitaly Wool 	write_lock(&slots->lock);
3234a3ac931SVitaly Wool 	*(unsigned long *)handle = 0;
324d8f117abSUladzislau Rezki 	if (zhdr->slots == slots) {
3254a3ac931SVitaly Wool 		write_unlock(&slots->lock);
3264a3ac931SVitaly Wool 		return; /* simple case, nothing else to do */
327d8f117abSUladzislau Rezki 	}
3284a3ac931SVitaly Wool 
3294a3ac931SVitaly Wool 	/* we are freeing a foreign handle if we are here */
3304a3ac931SVitaly Wool 	zhdr->foreign_handles--;
3317c2b8baaSVitaly Wool 	is_free = true;
3324a3ac931SVitaly Wool 	if (!test_bit(HANDLES_ORPHANED, &slots->pool)) {
333d8f117abSUladzislau Rezki 		write_unlock(&slots->lock);
3344a3ac931SVitaly Wool 		return;
3354a3ac931SVitaly Wool 	}
3367c2b8baaSVitaly Wool 	for (i = 0; i <= BUDDY_MASK; i++) {
3377c2b8baaSVitaly Wool 		if (slots->slot[i]) {
3387c2b8baaSVitaly Wool 			is_free = false;
3397c2b8baaSVitaly Wool 			break;
3407c2b8baaSVitaly Wool 		}
3417c2b8baaSVitaly Wool 	}
342d8f117abSUladzislau Rezki 	write_unlock(&slots->lock);
3437c2b8baaSVitaly Wool 
3447c2b8baaSVitaly Wool 	if (is_free) {
3457c2b8baaSVitaly Wool 		struct z3fold_pool *pool = slots_to_pool(slots);
3467c2b8baaSVitaly Wool 
3477c2b8baaSVitaly Wool 		kmem_cache_free(pool->c_handle, slots);
3487c2b8baaSVitaly Wool 	}
3497c2b8baaSVitaly Wool }
3507c2b8baaSVitaly Wool 
351ea8157abSDavid Howells static int z3fold_init_fs_context(struct fs_context *fc)
3521f862989SVitaly Wool {
353ea8157abSDavid Howells 	return init_pseudo(fc, Z3FOLD_MAGIC) ? 0 : -ENOMEM;
3541f862989SVitaly Wool }
3551f862989SVitaly Wool 
3561f862989SVitaly Wool static struct file_system_type z3fold_fs = {
3571f862989SVitaly Wool 	.name		= "z3fold",
358ea8157abSDavid Howells 	.init_fs_context = z3fold_init_fs_context,
3591f862989SVitaly Wool 	.kill_sb	= kill_anon_super,
3601f862989SVitaly Wool };
3611f862989SVitaly Wool 
3621f862989SVitaly Wool static struct vfsmount *z3fold_mnt;
3631f862989SVitaly Wool static int z3fold_mount(void)
3641f862989SVitaly Wool {
3651f862989SVitaly Wool 	int ret = 0;
3661f862989SVitaly Wool 
3671f862989SVitaly Wool 	z3fold_mnt = kern_mount(&z3fold_fs);
3681f862989SVitaly Wool 	if (IS_ERR(z3fold_mnt))
3691f862989SVitaly Wool 		ret = PTR_ERR(z3fold_mnt);
3701f862989SVitaly Wool 
3711f862989SVitaly Wool 	return ret;
3721f862989SVitaly Wool }
3731f862989SVitaly Wool 
3741f862989SVitaly Wool static void z3fold_unmount(void)
3751f862989SVitaly Wool {
3761f862989SVitaly Wool 	kern_unmount(z3fold_mnt);
3771f862989SVitaly Wool }
3781f862989SVitaly Wool 
3791f862989SVitaly Wool static const struct address_space_operations z3fold_aops;
3801f862989SVitaly Wool static int z3fold_register_migration(struct z3fold_pool *pool)
3811f862989SVitaly Wool {
3821f862989SVitaly Wool 	pool->inode = alloc_anon_inode(z3fold_mnt->mnt_sb);
3831f862989SVitaly Wool 	if (IS_ERR(pool->inode)) {
3841f862989SVitaly Wool 		pool->inode = NULL;
3851f862989SVitaly Wool 		return 1;
3861f862989SVitaly Wool 	}
3871f862989SVitaly Wool 
3881f862989SVitaly Wool 	pool->inode->i_mapping->private_data = pool;
3891f862989SVitaly Wool 	pool->inode->i_mapping->a_ops = &z3fold_aops;
3901f862989SVitaly Wool 	return 0;
3911f862989SVitaly Wool }
3921f862989SVitaly Wool 
3931f862989SVitaly Wool static void z3fold_unregister_migration(struct z3fold_pool *pool)
3941f862989SVitaly Wool {
3951f862989SVitaly Wool 	if (pool->inode)
3961f862989SVitaly Wool 		iput(pool->inode);
3971f862989SVitaly Wool  }
3981f862989SVitaly Wool 
3999a001fc1SVitaly Wool /* Initializes the z3fold header of a newly allocated z3fold page */
40063398413SVitaly Wool static struct z3fold_header *init_z3fold_page(struct page *page, bool headless,
401bb9f6f63SVitaly Wool 					struct z3fold_pool *pool, gfp_t gfp)
4029a001fc1SVitaly Wool {
4039a001fc1SVitaly Wool 	struct z3fold_header *zhdr = page_address(page);
40463398413SVitaly Wool 	struct z3fold_buddy_slots *slots;
4059a001fc1SVitaly Wool 
4069a001fc1SVitaly Wool 	INIT_LIST_HEAD(&page->lru);
4079a001fc1SVitaly Wool 	clear_bit(PAGE_HEADLESS, &page->private);
4089a001fc1SVitaly Wool 	clear_bit(MIDDLE_CHUNK_MAPPED, &page->private);
409d30561c5SVitaly Wool 	clear_bit(NEEDS_COMPACTING, &page->private);
410d30561c5SVitaly Wool 	clear_bit(PAGE_STALE, &page->private);
411ca0246bbSVitaly Wool 	clear_bit(PAGE_CLAIMED, &page->private);
41263398413SVitaly Wool 	if (headless)
41363398413SVitaly Wool 		return zhdr;
41463398413SVitaly Wool 
41563398413SVitaly Wool 	slots = alloc_slots(pool, gfp);
41663398413SVitaly Wool 	if (!slots)
41763398413SVitaly Wool 		return NULL;
4189a001fc1SVitaly Wool 
4192f1e5e4dSVitaly Wool 	spin_lock_init(&zhdr->page_lock);
4205a27aa82SVitaly Wool 	kref_init(&zhdr->refcount);
4219a001fc1SVitaly Wool 	zhdr->first_chunks = 0;
4229a001fc1SVitaly Wool 	zhdr->middle_chunks = 0;
4239a001fc1SVitaly Wool 	zhdr->last_chunks = 0;
4249a001fc1SVitaly Wool 	zhdr->first_num = 0;
4259a001fc1SVitaly Wool 	zhdr->start_middle = 0;
426d30561c5SVitaly Wool 	zhdr->cpu = -1;
4274a3ac931SVitaly Wool 	zhdr->foreign_handles = 0;
428d8f117abSUladzislau Rezki 	zhdr->mapped_count = 0;
4297c2b8baaSVitaly Wool 	zhdr->slots = slots;
430bb9a374dSVitaly Wool 	zhdr->pool = pool;
4319a001fc1SVitaly Wool 	INIT_LIST_HEAD(&zhdr->buddy);
432d30561c5SVitaly Wool 	INIT_WORK(&zhdr->work, compact_page_work);
4339a001fc1SVitaly Wool 	return zhdr;
4349a001fc1SVitaly Wool }
4359a001fc1SVitaly Wool 
4369a001fc1SVitaly Wool /* Resets the struct page fields and frees the page */
4371f862989SVitaly Wool static void free_z3fold_page(struct page *page, bool headless)
4389a001fc1SVitaly Wool {
4391f862989SVitaly Wool 	if (!headless) {
4401f862989SVitaly Wool 		lock_page(page);
4411f862989SVitaly Wool 		__ClearPageMovable(page);
4421f862989SVitaly Wool 		unlock_page(page);
4431f862989SVitaly Wool 	}
4441f862989SVitaly Wool 	ClearPagePrivate(page);
4455a27aa82SVitaly Wool 	__free_page(page);
4465a27aa82SVitaly Wool }
4475a27aa82SVitaly Wool 
4487c2b8baaSVitaly Wool /* Helper function to build the index */
4497c2b8baaSVitaly Wool static inline int __idx(struct z3fold_header *zhdr, enum buddy bud)
4507c2b8baaSVitaly Wool {
4517c2b8baaSVitaly Wool 	return (bud + zhdr->first_num) & BUDDY_MASK;
4527c2b8baaSVitaly Wool }
4537c2b8baaSVitaly Wool 
4549a001fc1SVitaly Wool /*
4559a001fc1SVitaly Wool  * Encodes the handle of a particular buddy within a z3fold page
4569a001fc1SVitaly Wool  * Pool lock should be held as this function accesses first_num
4579a001fc1SVitaly Wool  */
4583f9d2b57SVitaly Wool static unsigned long __encode_handle(struct z3fold_header *zhdr,
4593f9d2b57SVitaly Wool 				struct z3fold_buddy_slots *slots,
4603f9d2b57SVitaly Wool 				enum buddy bud)
4619a001fc1SVitaly Wool {
4627c2b8baaSVitaly Wool 	unsigned long h = (unsigned long)zhdr;
4637c2b8baaSVitaly Wool 	int idx = 0;
4649a001fc1SVitaly Wool 
4657c2b8baaSVitaly Wool 	/*
4667c2b8baaSVitaly Wool 	 * For a headless page, its handle is its pointer with the extra
4677c2b8baaSVitaly Wool 	 * PAGE_HEADLESS bit set
4687c2b8baaSVitaly Wool 	 */
4697c2b8baaSVitaly Wool 	if (bud == HEADLESS)
4707c2b8baaSVitaly Wool 		return h | (1 << PAGE_HEADLESS);
4717c2b8baaSVitaly Wool 
4727c2b8baaSVitaly Wool 	/* otherwise, return pointer to encoded handle */
4737c2b8baaSVitaly Wool 	idx = __idx(zhdr, bud);
4747c2b8baaSVitaly Wool 	h += idx;
475ca0246bbSVitaly Wool 	if (bud == LAST)
4767c2b8baaSVitaly Wool 		h |= (zhdr->last_chunks << BUDDY_SHIFT);
4777c2b8baaSVitaly Wool 
4784a3ac931SVitaly Wool 	write_lock(&slots->lock);
4797c2b8baaSVitaly Wool 	slots->slot[idx] = h;
4804a3ac931SVitaly Wool 	write_unlock(&slots->lock);
4817c2b8baaSVitaly Wool 	return (unsigned long)&slots->slot[idx];
4829a001fc1SVitaly Wool }
4839a001fc1SVitaly Wool 
4843f9d2b57SVitaly Wool static unsigned long encode_handle(struct z3fold_header *zhdr, enum buddy bud)
4853f9d2b57SVitaly Wool {
4863f9d2b57SVitaly Wool 	return __encode_handle(zhdr, zhdr->slots, bud);
4873f9d2b57SVitaly Wool }
4883f9d2b57SVitaly Wool 
489ca0246bbSVitaly Wool /* only for LAST bud, returns zero otherwise */
490ca0246bbSVitaly Wool static unsigned short handle_to_chunks(unsigned long handle)
491ca0246bbSVitaly Wool {
4924a3ac931SVitaly Wool 	struct z3fold_buddy_slots *slots = handle_to_slots(handle);
4934a3ac931SVitaly Wool 	unsigned long addr;
4947c2b8baaSVitaly Wool 
4954a3ac931SVitaly Wool 	read_lock(&slots->lock);
4964a3ac931SVitaly Wool 	addr = *(unsigned long *)handle;
4974a3ac931SVitaly Wool 	read_unlock(&slots->lock);
4987c2b8baaSVitaly Wool 	return (addr & ~PAGE_MASK) >> BUDDY_SHIFT;
499ca0246bbSVitaly Wool }
500ca0246bbSVitaly Wool 
501f201ebd8Szhong jiang /*
502f201ebd8Szhong jiang  * (handle & BUDDY_MASK) < zhdr->first_num is possible in encode_handle
503f201ebd8Szhong jiang  *  but that doesn't matter. because the masking will result in the
504f201ebd8Szhong jiang  *  correct buddy number.
505f201ebd8Szhong jiang  */
5069a001fc1SVitaly Wool static enum buddy handle_to_buddy(unsigned long handle)
5079a001fc1SVitaly Wool {
5087c2b8baaSVitaly Wool 	struct z3fold_header *zhdr;
5094a3ac931SVitaly Wool 	struct z3fold_buddy_slots *slots = handle_to_slots(handle);
5107c2b8baaSVitaly Wool 	unsigned long addr;
5117c2b8baaSVitaly Wool 
5124a3ac931SVitaly Wool 	read_lock(&slots->lock);
5137c2b8baaSVitaly Wool 	WARN_ON(handle & (1 << PAGE_HEADLESS));
5147c2b8baaSVitaly Wool 	addr = *(unsigned long *)handle;
5154a3ac931SVitaly Wool 	read_unlock(&slots->lock);
5167c2b8baaSVitaly Wool 	zhdr = (struct z3fold_header *)(addr & PAGE_MASK);
5177c2b8baaSVitaly Wool 	return (addr - zhdr->first_num) & BUDDY_MASK;
5189a001fc1SVitaly Wool }
5199a001fc1SVitaly Wool 
5209050cce1SVitaly Wool static inline struct z3fold_pool *zhdr_to_pool(struct z3fold_header *zhdr)
5219050cce1SVitaly Wool {
522bb9a374dSVitaly Wool 	return zhdr->pool;
5239050cce1SVitaly Wool }
5249050cce1SVitaly Wool 
525d30561c5SVitaly Wool static void __release_z3fold_page(struct z3fold_header *zhdr, bool locked)
526d30561c5SVitaly Wool {
527d30561c5SVitaly Wool 	struct page *page = virt_to_page(zhdr);
5289050cce1SVitaly Wool 	struct z3fold_pool *pool = zhdr_to_pool(zhdr);
5294a3ac931SVitaly Wool 	bool is_free = true;
5304a3ac931SVitaly Wool 	int i;
531d30561c5SVitaly Wool 
532d30561c5SVitaly Wool 	WARN_ON(!list_empty(&zhdr->buddy));
533d30561c5SVitaly Wool 	set_bit(PAGE_STALE, &page->private);
53435529357SVitaly Wool 	clear_bit(NEEDS_COMPACTING, &page->private);
535d30561c5SVitaly Wool 	spin_lock(&pool->lock);
536d30561c5SVitaly Wool 	if (!list_empty(&page->lru))
5371f862989SVitaly Wool 		list_del_init(&page->lru);
538d30561c5SVitaly Wool 	spin_unlock(&pool->lock);
5394a3ac931SVitaly Wool 
5404a3ac931SVitaly Wool 	/* If there are no foreign handles, free the handles array */
5414a3ac931SVitaly Wool 	read_lock(&zhdr->slots->lock);
5424a3ac931SVitaly Wool 	for (i = 0; i <= BUDDY_MASK; i++) {
5434a3ac931SVitaly Wool 		if (zhdr->slots->slot[i]) {
5444a3ac931SVitaly Wool 			is_free = false;
5454a3ac931SVitaly Wool 			break;
5464a3ac931SVitaly Wool 		}
5474a3ac931SVitaly Wool 	}
5484a3ac931SVitaly Wool 	if (!is_free)
5494a3ac931SVitaly Wool 		set_bit(HANDLES_ORPHANED, &zhdr->slots->pool);
5504a3ac931SVitaly Wool 	read_unlock(&zhdr->slots->lock);
5514a3ac931SVitaly Wool 
5524a3ac931SVitaly Wool 	if (is_free)
5534a3ac931SVitaly Wool 		kmem_cache_free(pool->c_handle, zhdr->slots);
5544a3ac931SVitaly Wool 
555d30561c5SVitaly Wool 	if (locked)
556d30561c5SVitaly Wool 		z3fold_page_unlock(zhdr);
5574a3ac931SVitaly Wool 
558d30561c5SVitaly Wool 	spin_lock(&pool->stale_lock);
559d30561c5SVitaly Wool 	list_add(&zhdr->buddy, &pool->stale);
560d30561c5SVitaly Wool 	queue_work(pool->release_wq, &pool->work);
561d30561c5SVitaly Wool 	spin_unlock(&pool->stale_lock);
562d30561c5SVitaly Wool }
563d30561c5SVitaly Wool 
564d30561c5SVitaly Wool static void __attribute__((__unused__))
565d30561c5SVitaly Wool 			release_z3fold_page(struct kref *ref)
566d30561c5SVitaly Wool {
567d30561c5SVitaly Wool 	struct z3fold_header *zhdr = container_of(ref, struct z3fold_header,
568d30561c5SVitaly Wool 						refcount);
569d30561c5SVitaly Wool 	__release_z3fold_page(zhdr, false);
570d30561c5SVitaly Wool }
571d30561c5SVitaly Wool 
572d30561c5SVitaly Wool static void release_z3fold_page_locked(struct kref *ref)
573d30561c5SVitaly Wool {
574d30561c5SVitaly Wool 	struct z3fold_header *zhdr = container_of(ref, struct z3fold_header,
575d30561c5SVitaly Wool 						refcount);
576d30561c5SVitaly Wool 	WARN_ON(z3fold_page_trylock(zhdr));
577d30561c5SVitaly Wool 	__release_z3fold_page(zhdr, true);
578d30561c5SVitaly Wool }
579d30561c5SVitaly Wool 
580d30561c5SVitaly Wool static void release_z3fold_page_locked_list(struct kref *ref)
581d30561c5SVitaly Wool {
582d30561c5SVitaly Wool 	struct z3fold_header *zhdr = container_of(ref, struct z3fold_header,
583d30561c5SVitaly Wool 					       refcount);
5849050cce1SVitaly Wool 	struct z3fold_pool *pool = zhdr_to_pool(zhdr);
5854a3ac931SVitaly Wool 
5869050cce1SVitaly Wool 	spin_lock(&pool->lock);
587d30561c5SVitaly Wool 	list_del_init(&zhdr->buddy);
5889050cce1SVitaly Wool 	spin_unlock(&pool->lock);
589d30561c5SVitaly Wool 
590d30561c5SVitaly Wool 	WARN_ON(z3fold_page_trylock(zhdr));
591d30561c5SVitaly Wool 	__release_z3fold_page(zhdr, true);
592d30561c5SVitaly Wool }
593d30561c5SVitaly Wool 
594d30561c5SVitaly Wool static void free_pages_work(struct work_struct *w)
595d30561c5SVitaly Wool {
596d30561c5SVitaly Wool 	struct z3fold_pool *pool = container_of(w, struct z3fold_pool, work);
597d30561c5SVitaly Wool 
598d30561c5SVitaly Wool 	spin_lock(&pool->stale_lock);
599d30561c5SVitaly Wool 	while (!list_empty(&pool->stale)) {
600d30561c5SVitaly Wool 		struct z3fold_header *zhdr = list_first_entry(&pool->stale,
601d30561c5SVitaly Wool 						struct z3fold_header, buddy);
602d30561c5SVitaly Wool 		struct page *page = virt_to_page(zhdr);
603d30561c5SVitaly Wool 
604d30561c5SVitaly Wool 		list_del(&zhdr->buddy);
605d30561c5SVitaly Wool 		if (WARN_ON(!test_bit(PAGE_STALE, &page->private)))
606d30561c5SVitaly Wool 			continue;
607d30561c5SVitaly Wool 		spin_unlock(&pool->stale_lock);
608d30561c5SVitaly Wool 		cancel_work_sync(&zhdr->work);
6091f862989SVitaly Wool 		free_z3fold_page(page, false);
610d30561c5SVitaly Wool 		cond_resched();
611d30561c5SVitaly Wool 		spin_lock(&pool->stale_lock);
612d30561c5SVitaly Wool 	}
613d30561c5SVitaly Wool 	spin_unlock(&pool->stale_lock);
614d30561c5SVitaly Wool }
615d30561c5SVitaly Wool 
6169a001fc1SVitaly Wool /*
6179a001fc1SVitaly Wool  * Returns the number of free chunks in a z3fold page.
6189a001fc1SVitaly Wool  * NB: can't be used with HEADLESS pages.
6199a001fc1SVitaly Wool  */
6209a001fc1SVitaly Wool static int num_free_chunks(struct z3fold_header *zhdr)
6219a001fc1SVitaly Wool {
6229a001fc1SVitaly Wool 	int nfree;
6239a001fc1SVitaly Wool 	/*
6249a001fc1SVitaly Wool 	 * If there is a middle object, pick up the bigger free space
6259a001fc1SVitaly Wool 	 * either before or after it. Otherwise just subtract the number
6269a001fc1SVitaly Wool 	 * of chunks occupied by the first and the last objects.
6279a001fc1SVitaly Wool 	 */
6289a001fc1SVitaly Wool 	if (zhdr->middle_chunks != 0) {
6299a001fc1SVitaly Wool 		int nfree_before = zhdr->first_chunks ?
630ede93213SVitaly Wool 			0 : zhdr->start_middle - ZHDR_CHUNKS;
6319a001fc1SVitaly Wool 		int nfree_after = zhdr->last_chunks ?
632ede93213SVitaly Wool 			0 : TOTAL_CHUNKS -
633ede93213SVitaly Wool 				(zhdr->start_middle + zhdr->middle_chunks);
6349a001fc1SVitaly Wool 		nfree = max(nfree_before, nfree_after);
6359a001fc1SVitaly Wool 	} else
6369a001fc1SVitaly Wool 		nfree = NCHUNKS - zhdr->first_chunks - zhdr->last_chunks;
6379a001fc1SVitaly Wool 	return nfree;
6389a001fc1SVitaly Wool }
6399a001fc1SVitaly Wool 
6409050cce1SVitaly Wool /* Add to the appropriate unbuddied list */
6419050cce1SVitaly Wool static inline void add_to_unbuddied(struct z3fold_pool *pool,
6429050cce1SVitaly Wool 				struct z3fold_header *zhdr)
6439050cce1SVitaly Wool {
6449050cce1SVitaly Wool 	if (zhdr->first_chunks == 0 || zhdr->last_chunks == 0 ||
6459050cce1SVitaly Wool 			zhdr->middle_chunks == 0) {
6469050cce1SVitaly Wool 		struct list_head *unbuddied = get_cpu_ptr(pool->unbuddied);
6479050cce1SVitaly Wool 
6489050cce1SVitaly Wool 		int freechunks = num_free_chunks(zhdr);
6499050cce1SVitaly Wool 		spin_lock(&pool->lock);
6509050cce1SVitaly Wool 		list_add(&zhdr->buddy, &unbuddied[freechunks]);
6519050cce1SVitaly Wool 		spin_unlock(&pool->lock);
6529050cce1SVitaly Wool 		zhdr->cpu = smp_processor_id();
6539050cce1SVitaly Wool 		put_cpu_ptr(pool->unbuddied);
6549050cce1SVitaly Wool 	}
6559050cce1SVitaly Wool }
6569050cce1SVitaly Wool 
657ede93213SVitaly Wool static inline void *mchunk_memmove(struct z3fold_header *zhdr,
658ede93213SVitaly Wool 				unsigned short dst_chunk)
659ede93213SVitaly Wool {
660ede93213SVitaly Wool 	void *beg = zhdr;
661ede93213SVitaly Wool 	return memmove(beg + (dst_chunk << CHUNK_SHIFT),
662ede93213SVitaly Wool 		       beg + (zhdr->start_middle << CHUNK_SHIFT),
663ede93213SVitaly Wool 		       zhdr->middle_chunks << CHUNK_SHIFT);
664ede93213SVitaly Wool }
665ede93213SVitaly Wool 
6664a3ac931SVitaly Wool static inline bool buddy_single(struct z3fold_header *zhdr)
6674a3ac931SVitaly Wool {
6684a3ac931SVitaly Wool 	return !((zhdr->first_chunks && zhdr->middle_chunks) ||
6694a3ac931SVitaly Wool 			(zhdr->first_chunks && zhdr->last_chunks) ||
6704a3ac931SVitaly Wool 			(zhdr->middle_chunks && zhdr->last_chunks));
6714a3ac931SVitaly Wool }
6724a3ac931SVitaly Wool 
6734a3ac931SVitaly Wool static struct z3fold_header *compact_single_buddy(struct z3fold_header *zhdr)
6744a3ac931SVitaly Wool {
6754a3ac931SVitaly Wool 	struct z3fold_pool *pool = zhdr_to_pool(zhdr);
6764a3ac931SVitaly Wool 	void *p = zhdr;
6774a3ac931SVitaly Wool 	unsigned long old_handle = 0;
6784a3ac931SVitaly Wool 	size_t sz = 0;
6794a3ac931SVitaly Wool 	struct z3fold_header *new_zhdr = NULL;
6804a3ac931SVitaly Wool 	int first_idx = __idx(zhdr, FIRST);
6814a3ac931SVitaly Wool 	int middle_idx = __idx(zhdr, MIDDLE);
6824a3ac931SVitaly Wool 	int last_idx = __idx(zhdr, LAST);
6834a3ac931SVitaly Wool 	unsigned short *moved_chunks = NULL;
6844a3ac931SVitaly Wool 
6854a3ac931SVitaly Wool 	/*
6864a3ac931SVitaly Wool 	 * No need to protect slots here -- all the slots are "local" and
6874a3ac931SVitaly Wool 	 * the page lock is already taken
6884a3ac931SVitaly Wool 	 */
6894a3ac931SVitaly Wool 	if (zhdr->first_chunks && zhdr->slots->slot[first_idx]) {
6904a3ac931SVitaly Wool 		p += ZHDR_SIZE_ALIGNED;
6914a3ac931SVitaly Wool 		sz = zhdr->first_chunks << CHUNK_SHIFT;
6924a3ac931SVitaly Wool 		old_handle = (unsigned long)&zhdr->slots->slot[first_idx];
6934a3ac931SVitaly Wool 		moved_chunks = &zhdr->first_chunks;
6944a3ac931SVitaly Wool 	} else if (zhdr->middle_chunks && zhdr->slots->slot[middle_idx]) {
6954a3ac931SVitaly Wool 		p += zhdr->start_middle << CHUNK_SHIFT;
6964a3ac931SVitaly Wool 		sz = zhdr->middle_chunks << CHUNK_SHIFT;
6974a3ac931SVitaly Wool 		old_handle = (unsigned long)&zhdr->slots->slot[middle_idx];
6984a3ac931SVitaly Wool 		moved_chunks = &zhdr->middle_chunks;
6994a3ac931SVitaly Wool 	} else if (zhdr->last_chunks && zhdr->slots->slot[last_idx]) {
7004a3ac931SVitaly Wool 		p += PAGE_SIZE - (zhdr->last_chunks << CHUNK_SHIFT);
7014a3ac931SVitaly Wool 		sz = zhdr->last_chunks << CHUNK_SHIFT;
7024a3ac931SVitaly Wool 		old_handle = (unsigned long)&zhdr->slots->slot[last_idx];
7034a3ac931SVitaly Wool 		moved_chunks = &zhdr->last_chunks;
7044a3ac931SVitaly Wool 	}
7054a3ac931SVitaly Wool 
7064a3ac931SVitaly Wool 	if (sz > 0) {
7074a3ac931SVitaly Wool 		enum buddy new_bud = HEADLESS;
7084a3ac931SVitaly Wool 		short chunks = size_to_chunks(sz);
7094a3ac931SVitaly Wool 		void *q;
7104a3ac931SVitaly Wool 
7114a3ac931SVitaly Wool 		new_zhdr = __z3fold_alloc(pool, sz, false);
7124a3ac931SVitaly Wool 		if (!new_zhdr)
7134a3ac931SVitaly Wool 			return NULL;
7144a3ac931SVitaly Wool 
7154a3ac931SVitaly Wool 		if (WARN_ON(new_zhdr == zhdr))
7164a3ac931SVitaly Wool 			goto out_fail;
7174a3ac931SVitaly Wool 
7184a3ac931SVitaly Wool 		if (new_zhdr->first_chunks == 0) {
7194a3ac931SVitaly Wool 			if (new_zhdr->middle_chunks != 0 &&
7204a3ac931SVitaly Wool 					chunks >= new_zhdr->start_middle) {
7214a3ac931SVitaly Wool 				new_bud = LAST;
7224a3ac931SVitaly Wool 			} else {
7234a3ac931SVitaly Wool 				new_bud = FIRST;
7244a3ac931SVitaly Wool 			}
7254a3ac931SVitaly Wool 		} else if (new_zhdr->last_chunks == 0) {
7264a3ac931SVitaly Wool 			new_bud = LAST;
7274a3ac931SVitaly Wool 		} else if (new_zhdr->middle_chunks == 0) {
7284a3ac931SVitaly Wool 			new_bud = MIDDLE;
7294a3ac931SVitaly Wool 		}
7304a3ac931SVitaly Wool 		q = new_zhdr;
7314a3ac931SVitaly Wool 		switch (new_bud) {
7324a3ac931SVitaly Wool 		case FIRST:
7334a3ac931SVitaly Wool 			new_zhdr->first_chunks = chunks;
7344a3ac931SVitaly Wool 			q += ZHDR_SIZE_ALIGNED;
7354a3ac931SVitaly Wool 			break;
7364a3ac931SVitaly Wool 		case MIDDLE:
7374a3ac931SVitaly Wool 			new_zhdr->middle_chunks = chunks;
7384a3ac931SVitaly Wool 			new_zhdr->start_middle =
7394a3ac931SVitaly Wool 				new_zhdr->first_chunks + ZHDR_CHUNKS;
7404a3ac931SVitaly Wool 			q += new_zhdr->start_middle << CHUNK_SHIFT;
7414a3ac931SVitaly Wool 			break;
7424a3ac931SVitaly Wool 		case LAST:
7434a3ac931SVitaly Wool 			new_zhdr->last_chunks = chunks;
7444a3ac931SVitaly Wool 			q += PAGE_SIZE - (new_zhdr->last_chunks << CHUNK_SHIFT);
7454a3ac931SVitaly Wool 			break;
7464a3ac931SVitaly Wool 		default:
7474a3ac931SVitaly Wool 			goto out_fail;
7484a3ac931SVitaly Wool 		}
7494a3ac931SVitaly Wool 		new_zhdr->foreign_handles++;
7504a3ac931SVitaly Wool 		memcpy(q, p, sz);
7514a3ac931SVitaly Wool 		write_lock(&zhdr->slots->lock);
7524a3ac931SVitaly Wool 		*(unsigned long *)old_handle = (unsigned long)new_zhdr +
7534a3ac931SVitaly Wool 			__idx(new_zhdr, new_bud);
7544a3ac931SVitaly Wool 		if (new_bud == LAST)
7554a3ac931SVitaly Wool 			*(unsigned long *)old_handle |=
7564a3ac931SVitaly Wool 					(new_zhdr->last_chunks << BUDDY_SHIFT);
7574a3ac931SVitaly Wool 		write_unlock(&zhdr->slots->lock);
7584a3ac931SVitaly Wool 		add_to_unbuddied(pool, new_zhdr);
7594a3ac931SVitaly Wool 		z3fold_page_unlock(new_zhdr);
7604a3ac931SVitaly Wool 
7614a3ac931SVitaly Wool 		*moved_chunks = 0;
7624a3ac931SVitaly Wool 	}
7634a3ac931SVitaly Wool 
7644a3ac931SVitaly Wool 	return new_zhdr;
7654a3ac931SVitaly Wool 
7664a3ac931SVitaly Wool out_fail:
7674a3ac931SVitaly Wool 	if (new_zhdr) {
7684a3ac931SVitaly Wool 		if (kref_put(&new_zhdr->refcount, release_z3fold_page_locked))
7694a3ac931SVitaly Wool 			atomic64_dec(&pool->pages_nr);
7704a3ac931SVitaly Wool 		else {
7714a3ac931SVitaly Wool 			add_to_unbuddied(pool, new_zhdr);
7724a3ac931SVitaly Wool 			z3fold_page_unlock(new_zhdr);
7734a3ac931SVitaly Wool 		}
7744a3ac931SVitaly Wool 	}
7754a3ac931SVitaly Wool 	return NULL;
7764a3ac931SVitaly Wool 
7774a3ac931SVitaly Wool }
7784a3ac931SVitaly Wool 
7791b096e5aSVitaly Wool #define BIG_CHUNK_GAP	3
7809a001fc1SVitaly Wool /* Has to be called with lock held */
7819a001fc1SVitaly Wool static int z3fold_compact_page(struct z3fold_header *zhdr)
7829a001fc1SVitaly Wool {
7839a001fc1SVitaly Wool 	struct page *page = virt_to_page(zhdr);
7849a001fc1SVitaly Wool 
785ede93213SVitaly Wool 	if (test_bit(MIDDLE_CHUNK_MAPPED, &page->private))
786ede93213SVitaly Wool 		return 0; /* can't move middle chunk, it's used */
7879a001fc1SVitaly Wool 
7881f862989SVitaly Wool 	if (unlikely(PageIsolated(page)))
7891f862989SVitaly Wool 		return 0;
7901f862989SVitaly Wool 
791ede93213SVitaly Wool 	if (zhdr->middle_chunks == 0)
792ede93213SVitaly Wool 		return 0; /* nothing to compact */
793ede93213SVitaly Wool 
794ede93213SVitaly Wool 	if (zhdr->first_chunks == 0 && zhdr->last_chunks == 0) {
795ede93213SVitaly Wool 		/* move to the beginning */
796ede93213SVitaly Wool 		mchunk_memmove(zhdr, ZHDR_CHUNKS);
7979a001fc1SVitaly Wool 		zhdr->first_chunks = zhdr->middle_chunks;
7989a001fc1SVitaly Wool 		zhdr->middle_chunks = 0;
7999a001fc1SVitaly Wool 		zhdr->start_middle = 0;
8009a001fc1SVitaly Wool 		zhdr->first_num++;
801ede93213SVitaly Wool 		return 1;
8029a001fc1SVitaly Wool 	}
8039a001fc1SVitaly Wool 
8041b096e5aSVitaly Wool 	/*
8051b096e5aSVitaly Wool 	 * moving data is expensive, so let's only do that if
8061b096e5aSVitaly Wool 	 * there's substantial gain (at least BIG_CHUNK_GAP chunks)
8071b096e5aSVitaly Wool 	 */
8081b096e5aSVitaly Wool 	if (zhdr->first_chunks != 0 && zhdr->last_chunks == 0 &&
8091b096e5aSVitaly Wool 	    zhdr->start_middle - (zhdr->first_chunks + ZHDR_CHUNKS) >=
8101b096e5aSVitaly Wool 			BIG_CHUNK_GAP) {
8111b096e5aSVitaly Wool 		mchunk_memmove(zhdr, zhdr->first_chunks + ZHDR_CHUNKS);
8121b096e5aSVitaly Wool 		zhdr->start_middle = zhdr->first_chunks + ZHDR_CHUNKS;
8131b096e5aSVitaly Wool 		return 1;
8141b096e5aSVitaly Wool 	} else if (zhdr->last_chunks != 0 && zhdr->first_chunks == 0 &&
8151b096e5aSVitaly Wool 		   TOTAL_CHUNKS - (zhdr->last_chunks + zhdr->start_middle
8161b096e5aSVitaly Wool 					+ zhdr->middle_chunks) >=
8171b096e5aSVitaly Wool 			BIG_CHUNK_GAP) {
8181b096e5aSVitaly Wool 		unsigned short new_start = TOTAL_CHUNKS - zhdr->last_chunks -
8191b096e5aSVitaly Wool 			zhdr->middle_chunks;
8201b096e5aSVitaly Wool 		mchunk_memmove(zhdr, new_start);
8211b096e5aSVitaly Wool 		zhdr->start_middle = new_start;
8221b096e5aSVitaly Wool 		return 1;
8231b096e5aSVitaly Wool 	}
8241b096e5aSVitaly Wool 
8251b096e5aSVitaly Wool 	return 0;
8261b096e5aSVitaly Wool }
8271b096e5aSVitaly Wool 
828d30561c5SVitaly Wool static void do_compact_page(struct z3fold_header *zhdr, bool locked)
829d30561c5SVitaly Wool {
8309050cce1SVitaly Wool 	struct z3fold_pool *pool = zhdr_to_pool(zhdr);
831d30561c5SVitaly Wool 	struct page *page;
832d30561c5SVitaly Wool 
833d30561c5SVitaly Wool 	page = virt_to_page(zhdr);
834d30561c5SVitaly Wool 	if (locked)
835d30561c5SVitaly Wool 		WARN_ON(z3fold_page_trylock(zhdr));
836d30561c5SVitaly Wool 	else
837d30561c5SVitaly Wool 		z3fold_page_lock(zhdr);
8385d03a661SVitaly Wool 	if (WARN_ON(!test_and_clear_bit(NEEDS_COMPACTING, &page->private))) {
839d30561c5SVitaly Wool 		z3fold_page_unlock(zhdr);
840d30561c5SVitaly Wool 		return;
841d30561c5SVitaly Wool 	}
842d30561c5SVitaly Wool 	spin_lock(&pool->lock);
843d30561c5SVitaly Wool 	list_del_init(&zhdr->buddy);
844d30561c5SVitaly Wool 	spin_unlock(&pool->lock);
845d30561c5SVitaly Wool 
8465d03a661SVitaly Wool 	if (kref_put(&zhdr->refcount, release_z3fold_page_locked)) {
8475d03a661SVitaly Wool 		atomic64_dec(&pool->pages_nr);
8485d03a661SVitaly Wool 		return;
8495d03a661SVitaly Wool 	}
8505d03a661SVitaly Wool 
8511f862989SVitaly Wool 	if (unlikely(PageIsolated(page) ||
8523f9d2b57SVitaly Wool 		     test_bit(PAGE_CLAIMED, &page->private) ||
8531f862989SVitaly Wool 		     test_bit(PAGE_STALE, &page->private))) {
8541f862989SVitaly Wool 		z3fold_page_unlock(zhdr);
8551f862989SVitaly Wool 		return;
8561f862989SVitaly Wool 	}
8571f862989SVitaly Wool 
8584a3ac931SVitaly Wool 	if (!zhdr->foreign_handles && buddy_single(zhdr) &&
8594a3ac931SVitaly Wool 	    zhdr->mapped_count == 0 && compact_single_buddy(zhdr)) {
8604a3ac931SVitaly Wool 		if (kref_put(&zhdr->refcount, release_z3fold_page_locked))
8614a3ac931SVitaly Wool 			atomic64_dec(&pool->pages_nr);
8624a3ac931SVitaly Wool 		else
8634a3ac931SVitaly Wool 			z3fold_page_unlock(zhdr);
8644a3ac931SVitaly Wool 		return;
8654a3ac931SVitaly Wool 	}
8664a3ac931SVitaly Wool 
867d30561c5SVitaly Wool 	z3fold_compact_page(zhdr);
8689050cce1SVitaly Wool 	add_to_unbuddied(pool, zhdr);
869d30561c5SVitaly Wool 	z3fold_page_unlock(zhdr);
870d30561c5SVitaly Wool }
871d30561c5SVitaly Wool 
872d30561c5SVitaly Wool static void compact_page_work(struct work_struct *w)
873d30561c5SVitaly Wool {
874d30561c5SVitaly Wool 	struct z3fold_header *zhdr = container_of(w, struct z3fold_header,
875d30561c5SVitaly Wool 						work);
876d30561c5SVitaly Wool 
877d30561c5SVitaly Wool 	do_compact_page(zhdr, false);
878d30561c5SVitaly Wool }
879d30561c5SVitaly Wool 
8809050cce1SVitaly Wool /* returns _locked_ z3fold page header or NULL */
8819050cce1SVitaly Wool static inline struct z3fold_header *__z3fold_alloc(struct z3fold_pool *pool,
8829050cce1SVitaly Wool 						size_t size, bool can_sleep)
8839050cce1SVitaly Wool {
8849050cce1SVitaly Wool 	struct z3fold_header *zhdr = NULL;
8859050cce1SVitaly Wool 	struct page *page;
8869050cce1SVitaly Wool 	struct list_head *unbuddied;
8879050cce1SVitaly Wool 	int chunks = size_to_chunks(size), i;
8889050cce1SVitaly Wool 
8899050cce1SVitaly Wool lookup:
8909050cce1SVitaly Wool 	/* First, try to find an unbuddied z3fold page. */
8919050cce1SVitaly Wool 	unbuddied = get_cpu_ptr(pool->unbuddied);
8929050cce1SVitaly Wool 	for_each_unbuddied_list(i, chunks) {
8939050cce1SVitaly Wool 		struct list_head *l = &unbuddied[i];
8949050cce1SVitaly Wool 
8959050cce1SVitaly Wool 		zhdr = list_first_entry_or_null(READ_ONCE(l),
8969050cce1SVitaly Wool 					struct z3fold_header, buddy);
8979050cce1SVitaly Wool 
8989050cce1SVitaly Wool 		if (!zhdr)
8999050cce1SVitaly Wool 			continue;
9009050cce1SVitaly Wool 
9019050cce1SVitaly Wool 		/* Re-check under lock. */
9029050cce1SVitaly Wool 		spin_lock(&pool->lock);
9039050cce1SVitaly Wool 		l = &unbuddied[i];
9049050cce1SVitaly Wool 		if (unlikely(zhdr != list_first_entry(READ_ONCE(l),
9059050cce1SVitaly Wool 						struct z3fold_header, buddy)) ||
9069050cce1SVitaly Wool 		    !z3fold_page_trylock(zhdr)) {
9079050cce1SVitaly Wool 			spin_unlock(&pool->lock);
9089050cce1SVitaly Wool 			zhdr = NULL;
9099050cce1SVitaly Wool 			put_cpu_ptr(pool->unbuddied);
9109050cce1SVitaly Wool 			if (can_sleep)
9119050cce1SVitaly Wool 				cond_resched();
9129050cce1SVitaly Wool 			goto lookup;
9139050cce1SVitaly Wool 		}
9149050cce1SVitaly Wool 		list_del_init(&zhdr->buddy);
9159050cce1SVitaly Wool 		zhdr->cpu = -1;
9169050cce1SVitaly Wool 		spin_unlock(&pool->lock);
9179050cce1SVitaly Wool 
9189050cce1SVitaly Wool 		page = virt_to_page(zhdr);
9194a3ac931SVitaly Wool 		if (test_bit(NEEDS_COMPACTING, &page->private) ||
9204a3ac931SVitaly Wool 		    test_bit(PAGE_CLAIMED, &page->private)) {
9219050cce1SVitaly Wool 			z3fold_page_unlock(zhdr);
9229050cce1SVitaly Wool 			zhdr = NULL;
9239050cce1SVitaly Wool 			put_cpu_ptr(pool->unbuddied);
9249050cce1SVitaly Wool 			if (can_sleep)
9259050cce1SVitaly Wool 				cond_resched();
9269050cce1SVitaly Wool 			goto lookup;
9279050cce1SVitaly Wool 		}
9289050cce1SVitaly Wool 
9299050cce1SVitaly Wool 		/*
9309050cce1SVitaly Wool 		 * this page could not be removed from its unbuddied
9319050cce1SVitaly Wool 		 * list while pool lock was held, and then we've taken
9329050cce1SVitaly Wool 		 * page lock so kref_put could not be called before
9339050cce1SVitaly Wool 		 * we got here, so it's safe to just call kref_get()
9349050cce1SVitaly Wool 		 */
9359050cce1SVitaly Wool 		kref_get(&zhdr->refcount);
9369050cce1SVitaly Wool 		break;
9379050cce1SVitaly Wool 	}
9389050cce1SVitaly Wool 	put_cpu_ptr(pool->unbuddied);
9399050cce1SVitaly Wool 
940351618b2SVitaly Wool 	if (!zhdr) {
941351618b2SVitaly Wool 		int cpu;
942351618b2SVitaly Wool 
943351618b2SVitaly Wool 		/* look for _exact_ match on other cpus' lists */
944351618b2SVitaly Wool 		for_each_online_cpu(cpu) {
945351618b2SVitaly Wool 			struct list_head *l;
946351618b2SVitaly Wool 
947351618b2SVitaly Wool 			unbuddied = per_cpu_ptr(pool->unbuddied, cpu);
948351618b2SVitaly Wool 			spin_lock(&pool->lock);
949351618b2SVitaly Wool 			l = &unbuddied[chunks];
950351618b2SVitaly Wool 
951351618b2SVitaly Wool 			zhdr = list_first_entry_or_null(READ_ONCE(l),
952351618b2SVitaly Wool 						struct z3fold_header, buddy);
953351618b2SVitaly Wool 
954351618b2SVitaly Wool 			if (!zhdr || !z3fold_page_trylock(zhdr)) {
955351618b2SVitaly Wool 				spin_unlock(&pool->lock);
956351618b2SVitaly Wool 				zhdr = NULL;
957351618b2SVitaly Wool 				continue;
958351618b2SVitaly Wool 			}
959351618b2SVitaly Wool 			list_del_init(&zhdr->buddy);
960351618b2SVitaly Wool 			zhdr->cpu = -1;
961351618b2SVitaly Wool 			spin_unlock(&pool->lock);
962351618b2SVitaly Wool 
963351618b2SVitaly Wool 			page = virt_to_page(zhdr);
9644a3ac931SVitaly Wool 			if (test_bit(NEEDS_COMPACTING, &page->private) ||
9654a3ac931SVitaly Wool 			    test_bit(PAGE_CLAIMED, &page->private)) {
966351618b2SVitaly Wool 				z3fold_page_unlock(zhdr);
967351618b2SVitaly Wool 				zhdr = NULL;
968351618b2SVitaly Wool 				if (can_sleep)
969351618b2SVitaly Wool 					cond_resched();
970351618b2SVitaly Wool 				continue;
971351618b2SVitaly Wool 			}
972351618b2SVitaly Wool 			kref_get(&zhdr->refcount);
973351618b2SVitaly Wool 			break;
974351618b2SVitaly Wool 		}
975351618b2SVitaly Wool 	}
976351618b2SVitaly Wool 
9779050cce1SVitaly Wool 	return zhdr;
9789050cce1SVitaly Wool }
979d30561c5SVitaly Wool 
980d30561c5SVitaly Wool /*
981d30561c5SVitaly Wool  * API Functions
982d30561c5SVitaly Wool  */
983d30561c5SVitaly Wool 
984d30561c5SVitaly Wool /**
985d30561c5SVitaly Wool  * z3fold_create_pool() - create a new z3fold pool
986d30561c5SVitaly Wool  * @name:	pool name
987d30561c5SVitaly Wool  * @gfp:	gfp flags when allocating the z3fold pool structure
988d30561c5SVitaly Wool  * @ops:	user-defined operations for the z3fold pool
989d30561c5SVitaly Wool  *
990d30561c5SVitaly Wool  * Return: pointer to the new z3fold pool or NULL if the metadata allocation
991d30561c5SVitaly Wool  * failed.
992d30561c5SVitaly Wool  */
993d30561c5SVitaly Wool static struct z3fold_pool *z3fold_create_pool(const char *name, gfp_t gfp,
994d30561c5SVitaly Wool 		const struct z3fold_ops *ops)
995d30561c5SVitaly Wool {
996d30561c5SVitaly Wool 	struct z3fold_pool *pool = NULL;
997d30561c5SVitaly Wool 	int i, cpu;
998d30561c5SVitaly Wool 
999d30561c5SVitaly Wool 	pool = kzalloc(sizeof(struct z3fold_pool), gfp);
1000d30561c5SVitaly Wool 	if (!pool)
1001d30561c5SVitaly Wool 		goto out;
10027c2b8baaSVitaly Wool 	pool->c_handle = kmem_cache_create("z3fold_handle",
10037c2b8baaSVitaly Wool 				sizeof(struct z3fold_buddy_slots),
10047c2b8baaSVitaly Wool 				SLOTS_ALIGN, 0, NULL);
10057c2b8baaSVitaly Wool 	if (!pool->c_handle)
10067c2b8baaSVitaly Wool 		goto out_c;
1007d30561c5SVitaly Wool 	spin_lock_init(&pool->lock);
1008d30561c5SVitaly Wool 	spin_lock_init(&pool->stale_lock);
1009d30561c5SVitaly Wool 	pool->unbuddied = __alloc_percpu(sizeof(struct list_head)*NCHUNKS, 2);
10101ec6995dSXidong Wang 	if (!pool->unbuddied)
10111ec6995dSXidong Wang 		goto out_pool;
1012d30561c5SVitaly Wool 	for_each_possible_cpu(cpu) {
1013d30561c5SVitaly Wool 		struct list_head *unbuddied =
1014d30561c5SVitaly Wool 				per_cpu_ptr(pool->unbuddied, cpu);
1015d30561c5SVitaly Wool 		for_each_unbuddied_list(i, 0)
1016d30561c5SVitaly Wool 			INIT_LIST_HEAD(&unbuddied[i]);
1017d30561c5SVitaly Wool 	}
1018d30561c5SVitaly Wool 	INIT_LIST_HEAD(&pool->lru);
1019d30561c5SVitaly Wool 	INIT_LIST_HEAD(&pool->stale);
1020d30561c5SVitaly Wool 	atomic64_set(&pool->pages_nr, 0);
1021d30561c5SVitaly Wool 	pool->name = name;
1022d30561c5SVitaly Wool 	pool->compact_wq = create_singlethread_workqueue(pool->name);
1023d30561c5SVitaly Wool 	if (!pool->compact_wq)
10241ec6995dSXidong Wang 		goto out_unbuddied;
1025d30561c5SVitaly Wool 	pool->release_wq = create_singlethread_workqueue(pool->name);
1026d30561c5SVitaly Wool 	if (!pool->release_wq)
1027d30561c5SVitaly Wool 		goto out_wq;
10281f862989SVitaly Wool 	if (z3fold_register_migration(pool))
10291f862989SVitaly Wool 		goto out_rwq;
1030d30561c5SVitaly Wool 	INIT_WORK(&pool->work, free_pages_work);
1031d30561c5SVitaly Wool 	pool->ops = ops;
1032d30561c5SVitaly Wool 	return pool;
1033d30561c5SVitaly Wool 
10341f862989SVitaly Wool out_rwq:
10351f862989SVitaly Wool 	destroy_workqueue(pool->release_wq);
1036d30561c5SVitaly Wool out_wq:
1037d30561c5SVitaly Wool 	destroy_workqueue(pool->compact_wq);
10381ec6995dSXidong Wang out_unbuddied:
10391ec6995dSXidong Wang 	free_percpu(pool->unbuddied);
10401ec6995dSXidong Wang out_pool:
10417c2b8baaSVitaly Wool 	kmem_cache_destroy(pool->c_handle);
10427c2b8baaSVitaly Wool out_c:
1043d30561c5SVitaly Wool 	kfree(pool);
10441ec6995dSXidong Wang out:
1045d30561c5SVitaly Wool 	return NULL;
1046d30561c5SVitaly Wool }
1047d30561c5SVitaly Wool 
1048d30561c5SVitaly Wool /**
1049d30561c5SVitaly Wool  * z3fold_destroy_pool() - destroys an existing z3fold pool
1050d30561c5SVitaly Wool  * @pool:	the z3fold pool to be destroyed
1051d30561c5SVitaly Wool  *
1052d30561c5SVitaly Wool  * The pool should be emptied before this function is called.
1053d30561c5SVitaly Wool  */
1054d30561c5SVitaly Wool static void z3fold_destroy_pool(struct z3fold_pool *pool)
1055d30561c5SVitaly Wool {
10567c2b8baaSVitaly Wool 	kmem_cache_destroy(pool->c_handle);
10576051d3bdSHenry Burns 
10586051d3bdSHenry Burns 	/*
10596051d3bdSHenry Burns 	 * We need to destroy pool->compact_wq before pool->release_wq,
10606051d3bdSHenry Burns 	 * as any pending work on pool->compact_wq will call
10616051d3bdSHenry Burns 	 * queue_work(pool->release_wq, &pool->work).
1062b997052bSHenry Burns 	 *
1063b997052bSHenry Burns 	 * There are still outstanding pages until both workqueues are drained,
1064b997052bSHenry Burns 	 * so we cannot unregister migration until then.
10656051d3bdSHenry Burns 	 */
10666051d3bdSHenry Burns 
1067d30561c5SVitaly Wool 	destroy_workqueue(pool->compact_wq);
10686051d3bdSHenry Burns 	destroy_workqueue(pool->release_wq);
1069b997052bSHenry Burns 	z3fold_unregister_migration(pool);
1070d30561c5SVitaly Wool 	kfree(pool);
1071d30561c5SVitaly Wool }
1072d30561c5SVitaly Wool 
10739a001fc1SVitaly Wool /**
10749a001fc1SVitaly Wool  * z3fold_alloc() - allocates a region of a given size
10759a001fc1SVitaly Wool  * @pool:	z3fold pool from which to allocate
10769a001fc1SVitaly Wool  * @size:	size in bytes of the desired allocation
10779a001fc1SVitaly Wool  * @gfp:	gfp flags used if the pool needs to grow
10789a001fc1SVitaly Wool  * @handle:	handle of the new allocation
10799a001fc1SVitaly Wool  *
10809a001fc1SVitaly Wool  * This function will attempt to find a free region in the pool large enough to
10819a001fc1SVitaly Wool  * satisfy the allocation request.  A search of the unbuddied lists is
10829a001fc1SVitaly Wool  * performed first. If no suitable free region is found, then a new page is
10839a001fc1SVitaly Wool  * allocated and added to the pool to satisfy the request.
10849a001fc1SVitaly Wool  *
10859a001fc1SVitaly Wool  * gfp should not set __GFP_HIGHMEM as highmem pages cannot be used
10869a001fc1SVitaly Wool  * as z3fold pool pages.
10879a001fc1SVitaly Wool  *
10889a001fc1SVitaly Wool  * Return: 0 if success and handle is set, otherwise -EINVAL if the size or
10899a001fc1SVitaly Wool  * gfp arguments are invalid or -ENOMEM if the pool was unable to allocate
10909a001fc1SVitaly Wool  * a new page.
10919a001fc1SVitaly Wool  */
10929a001fc1SVitaly Wool static int z3fold_alloc(struct z3fold_pool *pool, size_t size, gfp_t gfp,
10939a001fc1SVitaly Wool 			unsigned long *handle)
10949a001fc1SVitaly Wool {
10959050cce1SVitaly Wool 	int chunks = size_to_chunks(size);
10969a001fc1SVitaly Wool 	struct z3fold_header *zhdr = NULL;
1097d30561c5SVitaly Wool 	struct page *page = NULL;
10989a001fc1SVitaly Wool 	enum buddy bud;
10998a97ea54SMatthew Wilcox 	bool can_sleep = gfpflags_allow_blocking(gfp);
11009a001fc1SVitaly Wool 
1101f1549cb5SHenry Burns 	if (!size)
11029a001fc1SVitaly Wool 		return -EINVAL;
11039a001fc1SVitaly Wool 
11049a001fc1SVitaly Wool 	if (size > PAGE_SIZE)
11059a001fc1SVitaly Wool 		return -ENOSPC;
11069a001fc1SVitaly Wool 
11079a001fc1SVitaly Wool 	if (size > PAGE_SIZE - ZHDR_SIZE_ALIGNED - CHUNK_SIZE)
11089a001fc1SVitaly Wool 		bud = HEADLESS;
11099a001fc1SVitaly Wool 	else {
11109050cce1SVitaly Wool retry:
11119050cce1SVitaly Wool 		zhdr = __z3fold_alloc(pool, size, can_sleep);
1112d30561c5SVitaly Wool 		if (zhdr) {
11139a001fc1SVitaly Wool 			if (zhdr->first_chunks == 0) {
11149a001fc1SVitaly Wool 				if (zhdr->middle_chunks != 0 &&
11159a001fc1SVitaly Wool 				    chunks >= zhdr->start_middle)
11169a001fc1SVitaly Wool 					bud = LAST;
11179a001fc1SVitaly Wool 				else
11189a001fc1SVitaly Wool 					bud = FIRST;
11199a001fc1SVitaly Wool 			} else if (zhdr->last_chunks == 0)
11209a001fc1SVitaly Wool 				bud = LAST;
11219a001fc1SVitaly Wool 			else if (zhdr->middle_chunks == 0)
11229a001fc1SVitaly Wool 				bud = MIDDLE;
11239a001fc1SVitaly Wool 			else {
11245a27aa82SVitaly Wool 				if (kref_put(&zhdr->refcount,
1125d30561c5SVitaly Wool 					     release_z3fold_page_locked))
11265a27aa82SVitaly Wool 					atomic64_dec(&pool->pages_nr);
1127d30561c5SVitaly Wool 				else
1128d30561c5SVitaly Wool 					z3fold_page_unlock(zhdr);
11299a001fc1SVitaly Wool 				pr_err("No free chunks in unbuddied\n");
11309a001fc1SVitaly Wool 				WARN_ON(1);
11319050cce1SVitaly Wool 				goto retry;
11329a001fc1SVitaly Wool 			}
11339050cce1SVitaly Wool 			page = virt_to_page(zhdr);
11349a001fc1SVitaly Wool 			goto found;
11359a001fc1SVitaly Wool 		}
11369a001fc1SVitaly Wool 		bud = FIRST;
11379a001fc1SVitaly Wool 	}
11389a001fc1SVitaly Wool 
11395c9bab59SVitaly Wool 	page = NULL;
11405c9bab59SVitaly Wool 	if (can_sleep) {
1141d30561c5SVitaly Wool 		spin_lock(&pool->stale_lock);
1142d30561c5SVitaly Wool 		zhdr = list_first_entry_or_null(&pool->stale,
1143d30561c5SVitaly Wool 						struct z3fold_header, buddy);
1144d30561c5SVitaly Wool 		/*
11455c9bab59SVitaly Wool 		 * Before allocating a page, let's see if we can take one from
11465c9bab59SVitaly Wool 		 * the stale pages list. cancel_work_sync() can sleep so we
11475c9bab59SVitaly Wool 		 * limit this case to the contexts where we can sleep
1148d30561c5SVitaly Wool 		 */
11495c9bab59SVitaly Wool 		if (zhdr) {
1150d30561c5SVitaly Wool 			list_del(&zhdr->buddy);
1151d30561c5SVitaly Wool 			spin_unlock(&pool->stale_lock);
1152d30561c5SVitaly Wool 			cancel_work_sync(&zhdr->work);
1153d30561c5SVitaly Wool 			page = virt_to_page(zhdr);
1154d30561c5SVitaly Wool 		} else {
1155d30561c5SVitaly Wool 			spin_unlock(&pool->stale_lock);
1156d30561c5SVitaly Wool 		}
11575c9bab59SVitaly Wool 	}
11585c9bab59SVitaly Wool 	if (!page)
11595c9bab59SVitaly Wool 		page = alloc_page(gfp);
1160d30561c5SVitaly Wool 
11619a001fc1SVitaly Wool 	if (!page)
11629a001fc1SVitaly Wool 		return -ENOMEM;
11632f1e5e4dSVitaly Wool 
116463398413SVitaly Wool 	zhdr = init_z3fold_page(page, bud == HEADLESS, pool, gfp);
11659050cce1SVitaly Wool 	if (!zhdr) {
11669050cce1SVitaly Wool 		__free_page(page);
11679050cce1SVitaly Wool 		return -ENOMEM;
11689050cce1SVitaly Wool 	}
11699050cce1SVitaly Wool 	atomic64_inc(&pool->pages_nr);
11709a001fc1SVitaly Wool 
11719a001fc1SVitaly Wool 	if (bud == HEADLESS) {
11729a001fc1SVitaly Wool 		set_bit(PAGE_HEADLESS, &page->private);
11739a001fc1SVitaly Wool 		goto headless;
11749a001fc1SVitaly Wool 	}
1175810481a2SHenry Burns 	if (can_sleep) {
1176810481a2SHenry Burns 		lock_page(page);
11771f862989SVitaly Wool 		__SetPageMovable(page, pool->inode->i_mapping);
1178810481a2SHenry Burns 		unlock_page(page);
1179810481a2SHenry Burns 	} else {
1180810481a2SHenry Burns 		if (trylock_page(page)) {
1181810481a2SHenry Burns 			__SetPageMovable(page, pool->inode->i_mapping);
1182810481a2SHenry Burns 			unlock_page(page);
1183810481a2SHenry Burns 		}
1184810481a2SHenry Burns 	}
11852f1e5e4dSVitaly Wool 	z3fold_page_lock(zhdr);
11869a001fc1SVitaly Wool 
11879a001fc1SVitaly Wool found:
11889a001fc1SVitaly Wool 	if (bud == FIRST)
11899a001fc1SVitaly Wool 		zhdr->first_chunks = chunks;
11909a001fc1SVitaly Wool 	else if (bud == LAST)
11919a001fc1SVitaly Wool 		zhdr->last_chunks = chunks;
11929a001fc1SVitaly Wool 	else {
11939a001fc1SVitaly Wool 		zhdr->middle_chunks = chunks;
1194ede93213SVitaly Wool 		zhdr->start_middle = zhdr->first_chunks + ZHDR_CHUNKS;
11959a001fc1SVitaly Wool 	}
11969050cce1SVitaly Wool 	add_to_unbuddied(pool, zhdr);
11979a001fc1SVitaly Wool 
11989a001fc1SVitaly Wool headless:
1199d30561c5SVitaly Wool 	spin_lock(&pool->lock);
12009a001fc1SVitaly Wool 	/* Add/move z3fold page to beginning of LRU */
12019a001fc1SVitaly Wool 	if (!list_empty(&page->lru))
12029a001fc1SVitaly Wool 		list_del(&page->lru);
12039a001fc1SVitaly Wool 
12049a001fc1SVitaly Wool 	list_add(&page->lru, &pool->lru);
12059a001fc1SVitaly Wool 
12069a001fc1SVitaly Wool 	*handle = encode_handle(zhdr, bud);
12079a001fc1SVitaly Wool 	spin_unlock(&pool->lock);
12082f1e5e4dSVitaly Wool 	if (bud != HEADLESS)
12092f1e5e4dSVitaly Wool 		z3fold_page_unlock(zhdr);
12109a001fc1SVitaly Wool 
12119a001fc1SVitaly Wool 	return 0;
12129a001fc1SVitaly Wool }
12139a001fc1SVitaly Wool 
12149a001fc1SVitaly Wool /**
12159a001fc1SVitaly Wool  * z3fold_free() - frees the allocation associated with the given handle
12169a001fc1SVitaly Wool  * @pool:	pool in which the allocation resided
12179a001fc1SVitaly Wool  * @handle:	handle associated with the allocation returned by z3fold_alloc()
12189a001fc1SVitaly Wool  *
12199a001fc1SVitaly Wool  * In the case that the z3fold page in which the allocation resides is under
12209a001fc1SVitaly Wool  * reclaim, as indicated by the PG_reclaim flag being set, this function
12219a001fc1SVitaly Wool  * only sets the first|last_chunks to 0.  The page is actually freed
12229a001fc1SVitaly Wool  * once both buddies are evicted (see z3fold_reclaim_page() below).
12239a001fc1SVitaly Wool  */
12249a001fc1SVitaly Wool static void z3fold_free(struct z3fold_pool *pool, unsigned long handle)
12259a001fc1SVitaly Wool {
12269a001fc1SVitaly Wool 	struct z3fold_header *zhdr;
12279a001fc1SVitaly Wool 	struct page *page;
12289a001fc1SVitaly Wool 	enum buddy bud;
12295b6807deSVitaly Wool 	bool page_claimed;
12309a001fc1SVitaly Wool 
12314a3ac931SVitaly Wool 	zhdr = get_z3fold_header(handle);
12329a001fc1SVitaly Wool 	page = virt_to_page(zhdr);
12335b6807deSVitaly Wool 	page_claimed = test_and_set_bit(PAGE_CLAIMED, &page->private);
12349a001fc1SVitaly Wool 
12359a001fc1SVitaly Wool 	if (test_bit(PAGE_HEADLESS, &page->private)) {
1236ca0246bbSVitaly Wool 		/* if a headless page is under reclaim, just leave.
1237ca0246bbSVitaly Wool 		 * NB: we use test_and_set_bit for a reason: if the bit
1238ca0246bbSVitaly Wool 		 * has not been set before, we release this page
1239ca0246bbSVitaly Wool 		 * immediately so we don't care about its value any more.
1240ca0246bbSVitaly Wool 		 */
12415b6807deSVitaly Wool 		if (!page_claimed) {
1242ca0246bbSVitaly Wool 			spin_lock(&pool->lock);
1243ca0246bbSVitaly Wool 			list_del(&page->lru);
1244ca0246bbSVitaly Wool 			spin_unlock(&pool->lock);
12454a3ac931SVitaly Wool 			put_z3fold_header(zhdr);
12461f862989SVitaly Wool 			free_z3fold_page(page, true);
1247ca0246bbSVitaly Wool 			atomic64_dec(&pool->pages_nr);
1248ca0246bbSVitaly Wool 		}
1249ca0246bbSVitaly Wool 		return;
1250ca0246bbSVitaly Wool 	}
1251ca0246bbSVitaly Wool 
1252ca0246bbSVitaly Wool 	/* Non-headless case */
125343afc194SVitaly Wool 	bud = handle_to_buddy(handle);
12549a001fc1SVitaly Wool 
12559a001fc1SVitaly Wool 	switch (bud) {
12569a001fc1SVitaly Wool 	case FIRST:
12579a001fc1SVitaly Wool 		zhdr->first_chunks = 0;
12589a001fc1SVitaly Wool 		break;
12599a001fc1SVitaly Wool 	case MIDDLE:
12609a001fc1SVitaly Wool 		zhdr->middle_chunks = 0;
12619a001fc1SVitaly Wool 		break;
12629a001fc1SVitaly Wool 	case LAST:
12639a001fc1SVitaly Wool 		zhdr->last_chunks = 0;
12649a001fc1SVitaly Wool 		break;
12659a001fc1SVitaly Wool 	default:
12669a001fc1SVitaly Wool 		pr_err("%s: unknown bud %d\n", __func__, bud);
12679a001fc1SVitaly Wool 		WARN_ON(1);
12684a3ac931SVitaly Wool 		put_z3fold_header(zhdr);
12694a3ac931SVitaly Wool 		clear_bit(PAGE_CLAIMED, &page->private);
12709a001fc1SVitaly Wool 		return;
12719a001fc1SVitaly Wool 	}
12729a001fc1SVitaly Wool 
12734a3ac931SVitaly Wool 	if (!page_claimed)
12747c2b8baaSVitaly Wool 		free_handle(handle);
1275d30561c5SVitaly Wool 	if (kref_put(&zhdr->refcount, release_z3fold_page_locked_list)) {
1276d30561c5SVitaly Wool 		atomic64_dec(&pool->pages_nr);
1277d30561c5SVitaly Wool 		return;
1278d30561c5SVitaly Wool 	}
12795b6807deSVitaly Wool 	if (page_claimed) {
12805b6807deSVitaly Wool 		/* the page has not been claimed by us */
12816098d7e1SVitaly Wool 		z3fold_page_unlock(zhdr);
12826098d7e1SVitaly Wool 		return;
12836098d7e1SVitaly Wool 	}
12841f862989SVitaly Wool 	if (unlikely(PageIsolated(page)) ||
12851f862989SVitaly Wool 	    test_and_set_bit(NEEDS_COMPACTING, &page->private)) {
12864a3ac931SVitaly Wool 		put_z3fold_header(zhdr);
12875b6807deSVitaly Wool 		clear_bit(PAGE_CLAIMED, &page->private);
1288d30561c5SVitaly Wool 		return;
1289d30561c5SVitaly Wool 	}
1290d30561c5SVitaly Wool 	if (zhdr->cpu < 0 || !cpu_online(zhdr->cpu)) {
1291d30561c5SVitaly Wool 		spin_lock(&pool->lock);
1292d30561c5SVitaly Wool 		list_del_init(&zhdr->buddy);
1293d30561c5SVitaly Wool 		spin_unlock(&pool->lock);
1294d30561c5SVitaly Wool 		zhdr->cpu = -1;
12955d03a661SVitaly Wool 		kref_get(&zhdr->refcount);
12965b6807deSVitaly Wool 		clear_bit(PAGE_CLAIMED, &page->private);
12974a3ac931SVitaly Wool 		do_compact_page(zhdr, true);
1298d30561c5SVitaly Wool 		return;
1299d30561c5SVitaly Wool 	}
13005d03a661SVitaly Wool 	kref_get(&zhdr->refcount);
13015b6807deSVitaly Wool 	clear_bit(PAGE_CLAIMED, &page->private);
13024a3ac931SVitaly Wool 	queue_work_on(zhdr->cpu, pool->compact_wq, &zhdr->work);
13034a3ac931SVitaly Wool 	put_z3fold_header(zhdr);
13049a001fc1SVitaly Wool }
13059a001fc1SVitaly Wool 
13069a001fc1SVitaly Wool /**
13079a001fc1SVitaly Wool  * z3fold_reclaim_page() - evicts allocations from a pool page and frees it
13089a001fc1SVitaly Wool  * @pool:	pool from which a page will attempt to be evicted
1309f144c390SMike Rapoport  * @retries:	number of pages on the LRU list for which eviction will
13109a001fc1SVitaly Wool  *		be attempted before failing
13119a001fc1SVitaly Wool  *
13129a001fc1SVitaly Wool  * z3fold reclaim is different from normal system reclaim in that it is done
13139a001fc1SVitaly Wool  * from the bottom, up. This is because only the bottom layer, z3fold, has
13149a001fc1SVitaly Wool  * information on how the allocations are organized within each z3fold page.
13159a001fc1SVitaly Wool  * This has the potential to create interesting locking situations between
13169a001fc1SVitaly Wool  * z3fold and the user, however.
13179a001fc1SVitaly Wool  *
13189a001fc1SVitaly Wool  * To avoid these, this is how z3fold_reclaim_page() should be called:
1319f144c390SMike Rapoport  *
13209a001fc1SVitaly Wool  * The user detects a page should be reclaimed and calls z3fold_reclaim_page().
13219a001fc1SVitaly Wool  * z3fold_reclaim_page() will remove a z3fold page from the pool LRU list and
13229a001fc1SVitaly Wool  * call the user-defined eviction handler with the pool and handle as
13239a001fc1SVitaly Wool  * arguments.
13249a001fc1SVitaly Wool  *
13259a001fc1SVitaly Wool  * If the handle can not be evicted, the eviction handler should return
13269a001fc1SVitaly Wool  * non-zero. z3fold_reclaim_page() will add the z3fold page back to the
13279a001fc1SVitaly Wool  * appropriate list and try the next z3fold page on the LRU up to
13289a001fc1SVitaly Wool  * a user defined number of retries.
13299a001fc1SVitaly Wool  *
13309a001fc1SVitaly Wool  * If the handle is successfully evicted, the eviction handler should
13319a001fc1SVitaly Wool  * return 0 _and_ should have called z3fold_free() on the handle. z3fold_free()
13329a001fc1SVitaly Wool  * contains logic to delay freeing the page if the page is under reclaim,
13339a001fc1SVitaly Wool  * as indicated by the setting of the PG_reclaim flag on the underlying page.
13349a001fc1SVitaly Wool  *
13359a001fc1SVitaly Wool  * If all buddies in the z3fold page are successfully evicted, then the
13369a001fc1SVitaly Wool  * z3fold page can be freed.
13379a001fc1SVitaly Wool  *
13389a001fc1SVitaly Wool  * Returns: 0 if page is successfully freed, otherwise -EINVAL if there are
13399a001fc1SVitaly Wool  * no pages to evict or an eviction handler is not registered, -EAGAIN if
13409a001fc1SVitaly Wool  * the retry limit was hit.
13419a001fc1SVitaly Wool  */
13429a001fc1SVitaly Wool static int z3fold_reclaim_page(struct z3fold_pool *pool, unsigned int retries)
13439a001fc1SVitaly Wool {
13444a3ac931SVitaly Wool 	int i, ret = -1;
1345d30561c5SVitaly Wool 	struct z3fold_header *zhdr = NULL;
1346d30561c5SVitaly Wool 	struct page *page = NULL;
1347d30561c5SVitaly Wool 	struct list_head *pos;
13489a001fc1SVitaly Wool 	unsigned long first_handle = 0, middle_handle = 0, last_handle = 0;
13499a001fc1SVitaly Wool 
13509a001fc1SVitaly Wool 	spin_lock(&pool->lock);
13512f1e5e4dSVitaly Wool 	if (!pool->ops || !pool->ops->evict || retries == 0) {
13529a001fc1SVitaly Wool 		spin_unlock(&pool->lock);
13539a001fc1SVitaly Wool 		return -EINVAL;
13549a001fc1SVitaly Wool 	}
13559a001fc1SVitaly Wool 	for (i = 0; i < retries; i++) {
13562f1e5e4dSVitaly Wool 		if (list_empty(&pool->lru)) {
13572f1e5e4dSVitaly Wool 			spin_unlock(&pool->lock);
13582f1e5e4dSVitaly Wool 			return -EINVAL;
13592f1e5e4dSVitaly Wool 		}
1360d30561c5SVitaly Wool 		list_for_each_prev(pos, &pool->lru) {
1361d30561c5SVitaly Wool 			page = list_entry(pos, struct page, lru);
1362ca0246bbSVitaly Wool 
1363ca0246bbSVitaly Wool 			/* this bit could have been set by free, in which case
1364ca0246bbSVitaly Wool 			 * we pass over to the next page in the pool.
1365ca0246bbSVitaly Wool 			 */
13663f9d2b57SVitaly Wool 			if (test_and_set_bit(PAGE_CLAIMED, &page->private)) {
13673f9d2b57SVitaly Wool 				page = NULL;
1368ca0246bbSVitaly Wool 				continue;
13693f9d2b57SVitaly Wool 			}
13709a001fc1SVitaly Wool 
13713f9d2b57SVitaly Wool 			if (unlikely(PageIsolated(page))) {
13723f9d2b57SVitaly Wool 				clear_bit(PAGE_CLAIMED, &page->private);
13733f9d2b57SVitaly Wool 				page = NULL;
13741f862989SVitaly Wool 				continue;
13753f9d2b57SVitaly Wool 			}
13763f9d2b57SVitaly Wool 			zhdr = page_address(page);
1377ca0246bbSVitaly Wool 			if (test_bit(PAGE_HEADLESS, &page->private))
1378ca0246bbSVitaly Wool 				break;
1379ca0246bbSVitaly Wool 
1380ca0246bbSVitaly Wool 			if (!z3fold_page_trylock(zhdr)) {
13813f9d2b57SVitaly Wool 				clear_bit(PAGE_CLAIMED, &page->private);
1382ca0246bbSVitaly Wool 				zhdr = NULL;
1383d30561c5SVitaly Wool 				continue; /* can't evict at this point */
1384ca0246bbSVitaly Wool 			}
13854a3ac931SVitaly Wool 			if (zhdr->foreign_handles) {
13864a3ac931SVitaly Wool 				clear_bit(PAGE_CLAIMED, &page->private);
13874a3ac931SVitaly Wool 				z3fold_page_unlock(zhdr);
13884a3ac931SVitaly Wool 				zhdr = NULL;
13894a3ac931SVitaly Wool 				continue; /* can't evict such page */
13904a3ac931SVitaly Wool 			}
13915a27aa82SVitaly Wool 			kref_get(&zhdr->refcount);
1392d30561c5SVitaly Wool 			list_del_init(&zhdr->buddy);
1393d30561c5SVitaly Wool 			zhdr->cpu = -1;
13946098d7e1SVitaly Wool 			break;
1395d30561c5SVitaly Wool 		}
1396d30561c5SVitaly Wool 
1397ca0246bbSVitaly Wool 		if (!zhdr)
1398ca0246bbSVitaly Wool 			break;
1399ca0246bbSVitaly Wool 
1400d30561c5SVitaly Wool 		list_del_init(&page->lru);
14012f1e5e4dSVitaly Wool 		spin_unlock(&pool->lock);
1402d30561c5SVitaly Wool 
1403d30561c5SVitaly Wool 		if (!test_bit(PAGE_HEADLESS, &page->private)) {
14049a001fc1SVitaly Wool 			/*
14053f9d2b57SVitaly Wool 			 * We need encode the handles before unlocking, and
14063f9d2b57SVitaly Wool 			 * use our local slots structure because z3fold_free
14073f9d2b57SVitaly Wool 			 * can zero out zhdr->slots and we can't do much
14083f9d2b57SVitaly Wool 			 * about that
14099a001fc1SVitaly Wool 			 */
14109a001fc1SVitaly Wool 			first_handle = 0;
14119a001fc1SVitaly Wool 			last_handle = 0;
14129a001fc1SVitaly Wool 			middle_handle = 0;
14139a001fc1SVitaly Wool 			if (zhdr->first_chunks)
14144a3ac931SVitaly Wool 				first_handle = encode_handle(zhdr, FIRST);
14159a001fc1SVitaly Wool 			if (zhdr->middle_chunks)
14164a3ac931SVitaly Wool 				middle_handle = encode_handle(zhdr, MIDDLE);
14179a001fc1SVitaly Wool 			if (zhdr->last_chunks)
14184a3ac931SVitaly Wool 				last_handle = encode_handle(zhdr, LAST);
1419d30561c5SVitaly Wool 			/*
1420d30561c5SVitaly Wool 			 * it's safe to unlock here because we hold a
1421d30561c5SVitaly Wool 			 * reference to this page
1422d30561c5SVitaly Wool 			 */
14232f1e5e4dSVitaly Wool 			z3fold_page_unlock(zhdr);
14249a001fc1SVitaly Wool 		} else {
14254a3ac931SVitaly Wool 			first_handle = encode_handle(zhdr, HEADLESS);
14269a001fc1SVitaly Wool 			last_handle = middle_handle = 0;
14272f1e5e4dSVitaly Wool 		}
14289a001fc1SVitaly Wool 		/* Issue the eviction callback(s) */
14299a001fc1SVitaly Wool 		if (middle_handle) {
14309a001fc1SVitaly Wool 			ret = pool->ops->evict(pool, middle_handle);
14319a001fc1SVitaly Wool 			if (ret)
14329a001fc1SVitaly Wool 				goto next;
14334a3ac931SVitaly Wool 			free_handle(middle_handle);
14349a001fc1SVitaly Wool 		}
14359a001fc1SVitaly Wool 		if (first_handle) {
14369a001fc1SVitaly Wool 			ret = pool->ops->evict(pool, first_handle);
14379a001fc1SVitaly Wool 			if (ret)
14389a001fc1SVitaly Wool 				goto next;
14394a3ac931SVitaly Wool 			free_handle(first_handle);
14409a001fc1SVitaly Wool 		}
14419a001fc1SVitaly Wool 		if (last_handle) {
14429a001fc1SVitaly Wool 			ret = pool->ops->evict(pool, last_handle);
14439a001fc1SVitaly Wool 			if (ret)
14449a001fc1SVitaly Wool 				goto next;
14454a3ac931SVitaly Wool 			free_handle(last_handle);
14469a001fc1SVitaly Wool 		}
14479a001fc1SVitaly Wool next:
14485a27aa82SVitaly Wool 		if (test_bit(PAGE_HEADLESS, &page->private)) {
14495a27aa82SVitaly Wool 			if (ret == 0) {
14501f862989SVitaly Wool 				free_z3fold_page(page, true);
1451ca0246bbSVitaly Wool 				atomic64_dec(&pool->pages_nr);
14529a001fc1SVitaly Wool 				return 0;
14535a27aa82SVitaly Wool 			}
14546098d7e1SVitaly Wool 			spin_lock(&pool->lock);
14556098d7e1SVitaly Wool 			list_add(&page->lru, &pool->lru);
1456d5567c9dSVitaly Wool 			spin_unlock(&pool->lock);
14573f9d2b57SVitaly Wool 			clear_bit(PAGE_CLAIMED, &page->private);
14586098d7e1SVitaly Wool 		} else {
14596098d7e1SVitaly Wool 			z3fold_page_lock(zhdr);
14606098d7e1SVitaly Wool 			if (kref_put(&zhdr->refcount,
14616098d7e1SVitaly Wool 					release_z3fold_page_locked)) {
14626098d7e1SVitaly Wool 				atomic64_dec(&pool->pages_nr);
14635a27aa82SVitaly Wool 				return 0;
14645a27aa82SVitaly Wool 			}
14655a27aa82SVitaly Wool 			/*
14666098d7e1SVitaly Wool 			 * if we are here, the page is still not completely
14676098d7e1SVitaly Wool 			 * free. Take the global pool lock then to be able
14686098d7e1SVitaly Wool 			 * to add it back to the lru list
14695a27aa82SVitaly Wool 			 */
14706098d7e1SVitaly Wool 			spin_lock(&pool->lock);
14719a001fc1SVitaly Wool 			list_add(&page->lru, &pool->lru);
14726098d7e1SVitaly Wool 			spin_unlock(&pool->lock);
14736098d7e1SVitaly Wool 			z3fold_page_unlock(zhdr);
14743f9d2b57SVitaly Wool 			clear_bit(PAGE_CLAIMED, &page->private);
14756098d7e1SVitaly Wool 		}
14766098d7e1SVitaly Wool 
14776098d7e1SVitaly Wool 		/* We started off locked to we need to lock the pool back */
14786098d7e1SVitaly Wool 		spin_lock(&pool->lock);
14799a001fc1SVitaly Wool 	}
14809a001fc1SVitaly Wool 	spin_unlock(&pool->lock);
14819a001fc1SVitaly Wool 	return -EAGAIN;
14829a001fc1SVitaly Wool }
14839a001fc1SVitaly Wool 
14849a001fc1SVitaly Wool /**
14859a001fc1SVitaly Wool  * z3fold_map() - maps the allocation associated with the given handle
14869a001fc1SVitaly Wool  * @pool:	pool in which the allocation resides
14879a001fc1SVitaly Wool  * @handle:	handle associated with the allocation to be mapped
14889a001fc1SVitaly Wool  *
14899a001fc1SVitaly Wool  * Extracts the buddy number from handle and constructs the pointer to the
14909a001fc1SVitaly Wool  * correct starting chunk within the page.
14919a001fc1SVitaly Wool  *
14929a001fc1SVitaly Wool  * Returns: a pointer to the mapped allocation
14939a001fc1SVitaly Wool  */
14949a001fc1SVitaly Wool static void *z3fold_map(struct z3fold_pool *pool, unsigned long handle)
14959a001fc1SVitaly Wool {
14969a001fc1SVitaly Wool 	struct z3fold_header *zhdr;
14979a001fc1SVitaly Wool 	struct page *page;
14989a001fc1SVitaly Wool 	void *addr;
14999a001fc1SVitaly Wool 	enum buddy buddy;
15009a001fc1SVitaly Wool 
15014a3ac931SVitaly Wool 	zhdr = get_z3fold_header(handle);
15029a001fc1SVitaly Wool 	addr = zhdr;
15039a001fc1SVitaly Wool 	page = virt_to_page(zhdr);
15049a001fc1SVitaly Wool 
15059a001fc1SVitaly Wool 	if (test_bit(PAGE_HEADLESS, &page->private))
15069a001fc1SVitaly Wool 		goto out;
15079a001fc1SVitaly Wool 
15089a001fc1SVitaly Wool 	buddy = handle_to_buddy(handle);
15099a001fc1SVitaly Wool 	switch (buddy) {
15109a001fc1SVitaly Wool 	case FIRST:
15119a001fc1SVitaly Wool 		addr += ZHDR_SIZE_ALIGNED;
15129a001fc1SVitaly Wool 		break;
15139a001fc1SVitaly Wool 	case MIDDLE:
15149a001fc1SVitaly Wool 		addr += zhdr->start_middle << CHUNK_SHIFT;
15159a001fc1SVitaly Wool 		set_bit(MIDDLE_CHUNK_MAPPED, &page->private);
15169a001fc1SVitaly Wool 		break;
15179a001fc1SVitaly Wool 	case LAST:
1518ca0246bbSVitaly Wool 		addr += PAGE_SIZE - (handle_to_chunks(handle) << CHUNK_SHIFT);
15199a001fc1SVitaly Wool 		break;
15209a001fc1SVitaly Wool 	default:
15219a001fc1SVitaly Wool 		pr_err("unknown buddy id %d\n", buddy);
15229a001fc1SVitaly Wool 		WARN_ON(1);
15239a001fc1SVitaly Wool 		addr = NULL;
15249a001fc1SVitaly Wool 		break;
15259a001fc1SVitaly Wool 	}
15262f1e5e4dSVitaly Wool 
15271f862989SVitaly Wool 	if (addr)
15281f862989SVitaly Wool 		zhdr->mapped_count++;
15299a001fc1SVitaly Wool out:
15304a3ac931SVitaly Wool 	put_z3fold_header(zhdr);
15319a001fc1SVitaly Wool 	return addr;
15329a001fc1SVitaly Wool }
15339a001fc1SVitaly Wool 
15349a001fc1SVitaly Wool /**
15359a001fc1SVitaly Wool  * z3fold_unmap() - unmaps the allocation associated with the given handle
15369a001fc1SVitaly Wool  * @pool:	pool in which the allocation resides
15379a001fc1SVitaly Wool  * @handle:	handle associated with the allocation to be unmapped
15389a001fc1SVitaly Wool  */
15399a001fc1SVitaly Wool static void z3fold_unmap(struct z3fold_pool *pool, unsigned long handle)
15409a001fc1SVitaly Wool {
15419a001fc1SVitaly Wool 	struct z3fold_header *zhdr;
15429a001fc1SVitaly Wool 	struct page *page;
15439a001fc1SVitaly Wool 	enum buddy buddy;
15449a001fc1SVitaly Wool 
15454a3ac931SVitaly Wool 	zhdr = get_z3fold_header(handle);
15469a001fc1SVitaly Wool 	page = virt_to_page(zhdr);
15479a001fc1SVitaly Wool 
15482f1e5e4dSVitaly Wool 	if (test_bit(PAGE_HEADLESS, &page->private))
15499a001fc1SVitaly Wool 		return;
15509a001fc1SVitaly Wool 
15519a001fc1SVitaly Wool 	buddy = handle_to_buddy(handle);
15529a001fc1SVitaly Wool 	if (buddy == MIDDLE)
15539a001fc1SVitaly Wool 		clear_bit(MIDDLE_CHUNK_MAPPED, &page->private);
15541f862989SVitaly Wool 	zhdr->mapped_count--;
15554a3ac931SVitaly Wool 	put_z3fold_header(zhdr);
15569a001fc1SVitaly Wool }
15579a001fc1SVitaly Wool 
15589a001fc1SVitaly Wool /**
15599a001fc1SVitaly Wool  * z3fold_get_pool_size() - gets the z3fold pool size in pages
15609a001fc1SVitaly Wool  * @pool:	pool whose size is being queried
15619a001fc1SVitaly Wool  *
156212d59ae6SVitaly Wool  * Returns: size in pages of the given pool.
15639a001fc1SVitaly Wool  */
15649a001fc1SVitaly Wool static u64 z3fold_get_pool_size(struct z3fold_pool *pool)
15659a001fc1SVitaly Wool {
156612d59ae6SVitaly Wool 	return atomic64_read(&pool->pages_nr);
15679a001fc1SVitaly Wool }
15689a001fc1SVitaly Wool 
15691f862989SVitaly Wool static bool z3fold_page_isolate(struct page *page, isolate_mode_t mode)
15701f862989SVitaly Wool {
15711f862989SVitaly Wool 	struct z3fold_header *zhdr;
15721f862989SVitaly Wool 	struct z3fold_pool *pool;
15731f862989SVitaly Wool 
15741f862989SVitaly Wool 	VM_BUG_ON_PAGE(!PageMovable(page), page);
15751f862989SVitaly Wool 	VM_BUG_ON_PAGE(PageIsolated(page), page);
15761f862989SVitaly Wool 
15773f9d2b57SVitaly Wool 	if (test_bit(PAGE_HEADLESS, &page->private) ||
15783f9d2b57SVitaly Wool 	    test_bit(PAGE_CLAIMED, &page->private))
15791f862989SVitaly Wool 		return false;
15801f862989SVitaly Wool 
15811f862989SVitaly Wool 	zhdr = page_address(page);
15821f862989SVitaly Wool 	z3fold_page_lock(zhdr);
15831f862989SVitaly Wool 	if (test_bit(NEEDS_COMPACTING, &page->private) ||
15841f862989SVitaly Wool 	    test_bit(PAGE_STALE, &page->private))
15851f862989SVitaly Wool 		goto out;
15861f862989SVitaly Wool 
15874a3ac931SVitaly Wool 	if (zhdr->mapped_count != 0 || zhdr->foreign_handles != 0)
15884a3ac931SVitaly Wool 		goto out;
15891f862989SVitaly Wool 
15904a3ac931SVitaly Wool 	pool = zhdr_to_pool(zhdr);
15914a3ac931SVitaly Wool 	spin_lock(&pool->lock);
15921f862989SVitaly Wool 	if (!list_empty(&zhdr->buddy))
15931f862989SVitaly Wool 		list_del_init(&zhdr->buddy);
15941f862989SVitaly Wool 	if (!list_empty(&page->lru))
15954a3ac931SVitaly Wool 		list_del_init(&page->lru);
15961f862989SVitaly Wool 	spin_unlock(&pool->lock);
15974a3ac931SVitaly Wool 
15984a3ac931SVitaly Wool 	kref_get(&zhdr->refcount);
15991f862989SVitaly Wool 	z3fold_page_unlock(zhdr);
16001f862989SVitaly Wool 	return true;
16014a3ac931SVitaly Wool 
16021f862989SVitaly Wool out:
16031f862989SVitaly Wool 	z3fold_page_unlock(zhdr);
16041f862989SVitaly Wool 	return false;
16051f862989SVitaly Wool }
16061f862989SVitaly Wool 
16071f862989SVitaly Wool static int z3fold_page_migrate(struct address_space *mapping, struct page *newpage,
16081f862989SVitaly Wool 			       struct page *page, enum migrate_mode mode)
16091f862989SVitaly Wool {
16101f862989SVitaly Wool 	struct z3fold_header *zhdr, *new_zhdr;
16111f862989SVitaly Wool 	struct z3fold_pool *pool;
16121f862989SVitaly Wool 	struct address_space *new_mapping;
16131f862989SVitaly Wool 
16141f862989SVitaly Wool 	VM_BUG_ON_PAGE(!PageMovable(page), page);
16151f862989SVitaly Wool 	VM_BUG_ON_PAGE(!PageIsolated(page), page);
1616810481a2SHenry Burns 	VM_BUG_ON_PAGE(!PageLocked(newpage), newpage);
16171f862989SVitaly Wool 
16181f862989SVitaly Wool 	zhdr = page_address(page);
16191f862989SVitaly Wool 	pool = zhdr_to_pool(zhdr);
16201f862989SVitaly Wool 
16211f862989SVitaly Wool 	if (!z3fold_page_trylock(zhdr)) {
16221f862989SVitaly Wool 		return -EAGAIN;
16231f862989SVitaly Wool 	}
16244a3ac931SVitaly Wool 	if (zhdr->mapped_count != 0 || zhdr->foreign_handles != 0) {
16251f862989SVitaly Wool 		z3fold_page_unlock(zhdr);
16261f862989SVitaly Wool 		return -EBUSY;
16271f862989SVitaly Wool 	}
1628c92d2f38SHenry Burns 	if (work_pending(&zhdr->work)) {
1629c92d2f38SHenry Burns 		z3fold_page_unlock(zhdr);
1630c92d2f38SHenry Burns 		return -EAGAIN;
1631c92d2f38SHenry Burns 	}
16321f862989SVitaly Wool 	new_zhdr = page_address(newpage);
16331f862989SVitaly Wool 	memcpy(new_zhdr, zhdr, PAGE_SIZE);
16341f862989SVitaly Wool 	newpage->private = page->private;
16351f862989SVitaly Wool 	page->private = 0;
16361f862989SVitaly Wool 	z3fold_page_unlock(zhdr);
16371f862989SVitaly Wool 	spin_lock_init(&new_zhdr->page_lock);
1638c92d2f38SHenry Burns 	INIT_WORK(&new_zhdr->work, compact_page_work);
1639c92d2f38SHenry Burns 	/*
1640c92d2f38SHenry Burns 	 * z3fold_page_isolate() ensures that new_zhdr->buddy is empty,
1641c92d2f38SHenry Burns 	 * so we only have to reinitialize it.
1642c92d2f38SHenry Burns 	 */
1643c92d2f38SHenry Burns 	INIT_LIST_HEAD(&new_zhdr->buddy);
16441f862989SVitaly Wool 	new_mapping = page_mapping(page);
16451f862989SVitaly Wool 	__ClearPageMovable(page);
16461f862989SVitaly Wool 	ClearPagePrivate(page);
16471f862989SVitaly Wool 
16481f862989SVitaly Wool 	get_page(newpage);
16491f862989SVitaly Wool 	z3fold_page_lock(new_zhdr);
16501f862989SVitaly Wool 	if (new_zhdr->first_chunks)
16511f862989SVitaly Wool 		encode_handle(new_zhdr, FIRST);
16521f862989SVitaly Wool 	if (new_zhdr->last_chunks)
16531f862989SVitaly Wool 		encode_handle(new_zhdr, LAST);
16541f862989SVitaly Wool 	if (new_zhdr->middle_chunks)
16551f862989SVitaly Wool 		encode_handle(new_zhdr, MIDDLE);
16561f862989SVitaly Wool 	set_bit(NEEDS_COMPACTING, &newpage->private);
16571f862989SVitaly Wool 	new_zhdr->cpu = smp_processor_id();
16581f862989SVitaly Wool 	spin_lock(&pool->lock);
16591f862989SVitaly Wool 	list_add(&newpage->lru, &pool->lru);
16601f862989SVitaly Wool 	spin_unlock(&pool->lock);
16611f862989SVitaly Wool 	__SetPageMovable(newpage, new_mapping);
16621f862989SVitaly Wool 	z3fold_page_unlock(new_zhdr);
16631f862989SVitaly Wool 
16641f862989SVitaly Wool 	queue_work_on(new_zhdr->cpu, pool->compact_wq, &new_zhdr->work);
16651f862989SVitaly Wool 
16661f862989SVitaly Wool 	page_mapcount_reset(page);
16671f862989SVitaly Wool 	put_page(page);
16681f862989SVitaly Wool 	return 0;
16691f862989SVitaly Wool }
16701f862989SVitaly Wool 
16711f862989SVitaly Wool static void z3fold_page_putback(struct page *page)
16721f862989SVitaly Wool {
16731f862989SVitaly Wool 	struct z3fold_header *zhdr;
16741f862989SVitaly Wool 	struct z3fold_pool *pool;
16751f862989SVitaly Wool 
16761f862989SVitaly Wool 	zhdr = page_address(page);
16771f862989SVitaly Wool 	pool = zhdr_to_pool(zhdr);
16781f862989SVitaly Wool 
16791f862989SVitaly Wool 	z3fold_page_lock(zhdr);
16801f862989SVitaly Wool 	if (!list_empty(&zhdr->buddy))
16811f862989SVitaly Wool 		list_del_init(&zhdr->buddy);
16821f862989SVitaly Wool 	INIT_LIST_HEAD(&page->lru);
16831f862989SVitaly Wool 	if (kref_put(&zhdr->refcount, release_z3fold_page_locked)) {
16841f862989SVitaly Wool 		atomic64_dec(&pool->pages_nr);
16851f862989SVitaly Wool 		return;
16861f862989SVitaly Wool 	}
16871f862989SVitaly Wool 	spin_lock(&pool->lock);
16881f862989SVitaly Wool 	list_add(&page->lru, &pool->lru);
16891f862989SVitaly Wool 	spin_unlock(&pool->lock);
16901f862989SVitaly Wool 	z3fold_page_unlock(zhdr);
16911f862989SVitaly Wool }
16921f862989SVitaly Wool 
16931f862989SVitaly Wool static const struct address_space_operations z3fold_aops = {
16941f862989SVitaly Wool 	.isolate_page = z3fold_page_isolate,
16951f862989SVitaly Wool 	.migratepage = z3fold_page_migrate,
16961f862989SVitaly Wool 	.putback_page = z3fold_page_putback,
16971f862989SVitaly Wool };
16981f862989SVitaly Wool 
16999a001fc1SVitaly Wool /*****************
17009a001fc1SVitaly Wool  * zpool
17019a001fc1SVitaly Wool  ****************/
17029a001fc1SVitaly Wool 
17039a001fc1SVitaly Wool static int z3fold_zpool_evict(struct z3fold_pool *pool, unsigned long handle)
17049a001fc1SVitaly Wool {
17059a001fc1SVitaly Wool 	if (pool->zpool && pool->zpool_ops && pool->zpool_ops->evict)
17069a001fc1SVitaly Wool 		return pool->zpool_ops->evict(pool->zpool, handle);
17079a001fc1SVitaly Wool 	else
17089a001fc1SVitaly Wool 		return -ENOENT;
17099a001fc1SVitaly Wool }
17109a001fc1SVitaly Wool 
17119a001fc1SVitaly Wool static const struct z3fold_ops z3fold_zpool_ops = {
17129a001fc1SVitaly Wool 	.evict =	z3fold_zpool_evict
17139a001fc1SVitaly Wool };
17149a001fc1SVitaly Wool 
17159a001fc1SVitaly Wool static void *z3fold_zpool_create(const char *name, gfp_t gfp,
17169a001fc1SVitaly Wool 			       const struct zpool_ops *zpool_ops,
17179a001fc1SVitaly Wool 			       struct zpool *zpool)
17189a001fc1SVitaly Wool {
17199a001fc1SVitaly Wool 	struct z3fold_pool *pool;
17209a001fc1SVitaly Wool 
1721d30561c5SVitaly Wool 	pool = z3fold_create_pool(name, gfp,
1722d30561c5SVitaly Wool 				zpool_ops ? &z3fold_zpool_ops : NULL);
17239a001fc1SVitaly Wool 	if (pool) {
17249a001fc1SVitaly Wool 		pool->zpool = zpool;
17259a001fc1SVitaly Wool 		pool->zpool_ops = zpool_ops;
17269a001fc1SVitaly Wool 	}
17279a001fc1SVitaly Wool 	return pool;
17289a001fc1SVitaly Wool }
17299a001fc1SVitaly Wool 
17309a001fc1SVitaly Wool static void z3fold_zpool_destroy(void *pool)
17319a001fc1SVitaly Wool {
17329a001fc1SVitaly Wool 	z3fold_destroy_pool(pool);
17339a001fc1SVitaly Wool }
17349a001fc1SVitaly Wool 
17359a001fc1SVitaly Wool static int z3fold_zpool_malloc(void *pool, size_t size, gfp_t gfp,
17369a001fc1SVitaly Wool 			unsigned long *handle)
17379a001fc1SVitaly Wool {
17389a001fc1SVitaly Wool 	return z3fold_alloc(pool, size, gfp, handle);
17399a001fc1SVitaly Wool }
17409a001fc1SVitaly Wool static void z3fold_zpool_free(void *pool, unsigned long handle)
17419a001fc1SVitaly Wool {
17429a001fc1SVitaly Wool 	z3fold_free(pool, handle);
17439a001fc1SVitaly Wool }
17449a001fc1SVitaly Wool 
17459a001fc1SVitaly Wool static int z3fold_zpool_shrink(void *pool, unsigned int pages,
17469a001fc1SVitaly Wool 			unsigned int *reclaimed)
17479a001fc1SVitaly Wool {
17489a001fc1SVitaly Wool 	unsigned int total = 0;
17499a001fc1SVitaly Wool 	int ret = -EINVAL;
17509a001fc1SVitaly Wool 
17519a001fc1SVitaly Wool 	while (total < pages) {
17529a001fc1SVitaly Wool 		ret = z3fold_reclaim_page(pool, 8);
17539a001fc1SVitaly Wool 		if (ret < 0)
17549a001fc1SVitaly Wool 			break;
17559a001fc1SVitaly Wool 		total++;
17569a001fc1SVitaly Wool 	}
17579a001fc1SVitaly Wool 
17589a001fc1SVitaly Wool 	if (reclaimed)
17599a001fc1SVitaly Wool 		*reclaimed = total;
17609a001fc1SVitaly Wool 
17619a001fc1SVitaly Wool 	return ret;
17629a001fc1SVitaly Wool }
17639a001fc1SVitaly Wool 
17649a001fc1SVitaly Wool static void *z3fold_zpool_map(void *pool, unsigned long handle,
17659a001fc1SVitaly Wool 			enum zpool_mapmode mm)
17669a001fc1SVitaly Wool {
17679a001fc1SVitaly Wool 	return z3fold_map(pool, handle);
17689a001fc1SVitaly Wool }
17699a001fc1SVitaly Wool static void z3fold_zpool_unmap(void *pool, unsigned long handle)
17709a001fc1SVitaly Wool {
17719a001fc1SVitaly Wool 	z3fold_unmap(pool, handle);
17729a001fc1SVitaly Wool }
17739a001fc1SVitaly Wool 
17749a001fc1SVitaly Wool static u64 z3fold_zpool_total_size(void *pool)
17759a001fc1SVitaly Wool {
17769a001fc1SVitaly Wool 	return z3fold_get_pool_size(pool) * PAGE_SIZE;
17779a001fc1SVitaly Wool }
17789a001fc1SVitaly Wool 
17799a001fc1SVitaly Wool static struct zpool_driver z3fold_zpool_driver = {
17809a001fc1SVitaly Wool 	.type =		"z3fold",
17819a001fc1SVitaly Wool 	.owner =	THIS_MODULE,
17829a001fc1SVitaly Wool 	.create =	z3fold_zpool_create,
17839a001fc1SVitaly Wool 	.destroy =	z3fold_zpool_destroy,
17849a001fc1SVitaly Wool 	.malloc =	z3fold_zpool_malloc,
17859a001fc1SVitaly Wool 	.free =		z3fold_zpool_free,
17869a001fc1SVitaly Wool 	.shrink =	z3fold_zpool_shrink,
17879a001fc1SVitaly Wool 	.map =		z3fold_zpool_map,
17889a001fc1SVitaly Wool 	.unmap =	z3fold_zpool_unmap,
17899a001fc1SVitaly Wool 	.total_size =	z3fold_zpool_total_size,
17909a001fc1SVitaly Wool };
17919a001fc1SVitaly Wool 
17929a001fc1SVitaly Wool MODULE_ALIAS("zpool-z3fold");
17939a001fc1SVitaly Wool 
17949a001fc1SVitaly Wool static int __init init_z3fold(void)
17959a001fc1SVitaly Wool {
17961f862989SVitaly Wool 	int ret;
17971f862989SVitaly Wool 
1798ede93213SVitaly Wool 	/* Make sure the z3fold header is not larger than the page size */
1799ede93213SVitaly Wool 	BUILD_BUG_ON(ZHDR_SIZE_ALIGNED > PAGE_SIZE);
18001f862989SVitaly Wool 	ret = z3fold_mount();
18011f862989SVitaly Wool 	if (ret)
18021f862989SVitaly Wool 		return ret;
18031f862989SVitaly Wool 
18049a001fc1SVitaly Wool 	zpool_register_driver(&z3fold_zpool_driver);
18059a001fc1SVitaly Wool 
18069a001fc1SVitaly Wool 	return 0;
18079a001fc1SVitaly Wool }
18089a001fc1SVitaly Wool 
18099a001fc1SVitaly Wool static void __exit exit_z3fold(void)
18109a001fc1SVitaly Wool {
18111f862989SVitaly Wool 	z3fold_unmount();
18129a001fc1SVitaly Wool 	zpool_unregister_driver(&z3fold_zpool_driver);
18139a001fc1SVitaly Wool }
18149a001fc1SVitaly Wool 
18159a001fc1SVitaly Wool module_init(init_z3fold);
18169a001fc1SVitaly Wool module_exit(exit_z3fold);
18179a001fc1SVitaly Wool 
18189a001fc1SVitaly Wool MODULE_LICENSE("GPL");
18199a001fc1SVitaly Wool MODULE_AUTHOR("Vitaly Wool <vitalywool@gmail.com>");
18209a001fc1SVitaly Wool MODULE_DESCRIPTION("3-Fold Allocator for Compressed Pages");
1821