162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * zbud.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2013, Seth Jennings, IBM
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Concepts based on zcache internal zbud allocator by Dan Magenheimer.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * zbud is an special purpose allocator for storing compressed pages.  Contrary
1062306a36Sopenharmony_ci * to what its name may suggest, zbud is not a buddy allocator, but rather an
1162306a36Sopenharmony_ci * allocator that "buddies" two compressed pages together in a single memory
1262306a36Sopenharmony_ci * page.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * While this design limits storage density, it has simple and deterministic
1562306a36Sopenharmony_ci * reclaim properties that make it preferable to a higher density approach when
1662306a36Sopenharmony_ci * reclaim will be used.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * zbud works by storing compressed pages, or "zpages", together in pairs in a
1962306a36Sopenharmony_ci * single memory page called a "zbud page".  The first buddy is "left
2062306a36Sopenharmony_ci * justified" at the beginning of the zbud page, and the last buddy is "right
2162306a36Sopenharmony_ci * justified" at the end of the zbud page.  The benefit is that if either
2262306a36Sopenharmony_ci * buddy is freed, the freed buddy space, coalesced with whatever slack space
2362306a36Sopenharmony_ci * that existed between the buddies, results in the largest possible free region
2462306a36Sopenharmony_ci * within the zbud page.
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * zbud also provides an attractive lower bound on density. The ratio of zpages
2762306a36Sopenharmony_ci * to zbud pages can not be less than 1.  This ensures that zbud can never "do
2862306a36Sopenharmony_ci * harm" by using more pages to store zpages than the uncompressed zpages would
2962306a36Sopenharmony_ci * have used on their own.
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci * zbud pages are divided into "chunks".  The size of the chunks is fixed at
3262306a36Sopenharmony_ci * compile time and determined by NCHUNKS_ORDER below.  Dividing zbud pages
3362306a36Sopenharmony_ci * into chunks allows organizing unbuddied zbud pages into a manageable number
3462306a36Sopenharmony_ci * of unbuddied lists according to the number of free chunks available in the
3562306a36Sopenharmony_ci * zbud page.
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * The zbud API differs from that of conventional allocators in that the
3862306a36Sopenharmony_ci * allocation function, zbud_alloc(), returns an opaque handle to the user,
3962306a36Sopenharmony_ci * not a dereferenceable pointer.  The user must map the handle using
4062306a36Sopenharmony_ci * zbud_map() in order to get a usable pointer by which to access the
4162306a36Sopenharmony_ci * allocation data and unmap the handle with zbud_unmap() when operations
4262306a36Sopenharmony_ci * on the allocation data are complete.
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#include <linux/atomic.h>
4862306a36Sopenharmony_ci#include <linux/list.h>
4962306a36Sopenharmony_ci#include <linux/mm.h>
5062306a36Sopenharmony_ci#include <linux/module.h>
5162306a36Sopenharmony_ci#include <linux/preempt.h>
5262306a36Sopenharmony_ci#include <linux/slab.h>
5362306a36Sopenharmony_ci#include <linux/spinlock.h>
5462306a36Sopenharmony_ci#include <linux/zpool.h>
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/*****************
5762306a36Sopenharmony_ci * Structures
5862306a36Sopenharmony_ci*****************/
5962306a36Sopenharmony_ci/*
6062306a36Sopenharmony_ci * NCHUNKS_ORDER determines the internal allocation granularity, effectively
6162306a36Sopenharmony_ci * adjusting internal fragmentation.  It also determines the number of
6262306a36Sopenharmony_ci * freelists maintained in each pool. NCHUNKS_ORDER of 6 means that the
6362306a36Sopenharmony_ci * allocation granularity will be in chunks of size PAGE_SIZE/64. As one chunk
6462306a36Sopenharmony_ci * in allocated page is occupied by zbud header, NCHUNKS will be calculated to
6562306a36Sopenharmony_ci * 63 which shows the max number of free chunks in zbud page, also there will be
6662306a36Sopenharmony_ci * 63 freelists per pool.
6762306a36Sopenharmony_ci */
6862306a36Sopenharmony_ci#define NCHUNKS_ORDER	6
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define CHUNK_SHIFT	(PAGE_SHIFT - NCHUNKS_ORDER)
7162306a36Sopenharmony_ci#define CHUNK_SIZE	(1 << CHUNK_SHIFT)
7262306a36Sopenharmony_ci#define ZHDR_SIZE_ALIGNED CHUNK_SIZE
7362306a36Sopenharmony_ci#define NCHUNKS		((PAGE_SIZE - ZHDR_SIZE_ALIGNED) >> CHUNK_SHIFT)
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistruct zbud_pool;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/**
7862306a36Sopenharmony_ci * struct zbud_pool - stores metadata for each zbud pool
7962306a36Sopenharmony_ci * @lock:	protects all pool fields and first|last_chunk fields of any
8062306a36Sopenharmony_ci *		zbud page in the pool
8162306a36Sopenharmony_ci * @unbuddied:	array of lists tracking zbud pages that only contain one buddy;
8262306a36Sopenharmony_ci *		the lists each zbud page is added to depends on the size of
8362306a36Sopenharmony_ci *		its free region.
8462306a36Sopenharmony_ci * @buddied:	list tracking the zbud pages that contain two buddies;
8562306a36Sopenharmony_ci *		these zbud pages are full
8662306a36Sopenharmony_ci * @pages_nr:	number of zbud pages in the pool.
8762306a36Sopenharmony_ci *
8862306a36Sopenharmony_ci * This structure is allocated at pool creation time and maintains metadata
8962306a36Sopenharmony_ci * pertaining to a particular zbud pool.
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_cistruct zbud_pool {
9262306a36Sopenharmony_ci	spinlock_t lock;
9362306a36Sopenharmony_ci	union {
9462306a36Sopenharmony_ci		/*
9562306a36Sopenharmony_ci		 * Reuse unbuddied[0] as buddied on the ground that
9662306a36Sopenharmony_ci		 * unbuddied[0] is unused.
9762306a36Sopenharmony_ci		 */
9862306a36Sopenharmony_ci		struct list_head buddied;
9962306a36Sopenharmony_ci		struct list_head unbuddied[NCHUNKS];
10062306a36Sopenharmony_ci	};
10162306a36Sopenharmony_ci	u64 pages_nr;
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/*
10562306a36Sopenharmony_ci * struct zbud_header - zbud page metadata occupying the first chunk of each
10662306a36Sopenharmony_ci *			zbud page.
10762306a36Sopenharmony_ci * @buddy:	links the zbud page into the unbuddied/buddied lists in the pool
10862306a36Sopenharmony_ci * @first_chunks:	the size of the first buddy in chunks, 0 if free
10962306a36Sopenharmony_ci * @last_chunks:	the size of the last buddy in chunks, 0 if free
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_cistruct zbud_header {
11262306a36Sopenharmony_ci	struct list_head buddy;
11362306a36Sopenharmony_ci	unsigned int first_chunks;
11462306a36Sopenharmony_ci	unsigned int last_chunks;
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/*****************
11862306a36Sopenharmony_ci * Helpers
11962306a36Sopenharmony_ci*****************/
12062306a36Sopenharmony_ci/* Just to make the code easier to read */
12162306a36Sopenharmony_cienum buddy {
12262306a36Sopenharmony_ci	FIRST,
12362306a36Sopenharmony_ci	LAST
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/* Converts an allocation size in bytes to size in zbud chunks */
12762306a36Sopenharmony_cistatic int size_to_chunks(size_t size)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci#define for_each_unbuddied_list(_iter, _begin) \
13362306a36Sopenharmony_ci	for ((_iter) = (_begin); (_iter) < NCHUNKS; (_iter)++)
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/* Initializes the zbud header of a newly allocated zbud page */
13662306a36Sopenharmony_cistatic struct zbud_header *init_zbud_page(struct page *page)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct zbud_header *zhdr = page_address(page);
13962306a36Sopenharmony_ci	zhdr->first_chunks = 0;
14062306a36Sopenharmony_ci	zhdr->last_chunks = 0;
14162306a36Sopenharmony_ci	INIT_LIST_HEAD(&zhdr->buddy);
14262306a36Sopenharmony_ci	return zhdr;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/* Resets the struct page fields and frees the page */
14662306a36Sopenharmony_cistatic void free_zbud_page(struct zbud_header *zhdr)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	__free_page(virt_to_page(zhdr));
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/*
15262306a36Sopenharmony_ci * Encodes the handle of a particular buddy within a zbud page
15362306a36Sopenharmony_ci * Pool lock should be held as this function accesses first|last_chunks
15462306a36Sopenharmony_ci */
15562306a36Sopenharmony_cistatic unsigned long encode_handle(struct zbud_header *zhdr, enum buddy bud)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	unsigned long handle;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	/*
16062306a36Sopenharmony_ci	 * For now, the encoded handle is actually just the pointer to the data
16162306a36Sopenharmony_ci	 * but this might not always be the case.  A little information hiding.
16262306a36Sopenharmony_ci	 * Add CHUNK_SIZE to the handle if it is the first allocation to jump
16362306a36Sopenharmony_ci	 * over the zbud header in the first chunk.
16462306a36Sopenharmony_ci	 */
16562306a36Sopenharmony_ci	handle = (unsigned long)zhdr;
16662306a36Sopenharmony_ci	if (bud == FIRST)
16762306a36Sopenharmony_ci		/* skip over zbud header */
16862306a36Sopenharmony_ci		handle += ZHDR_SIZE_ALIGNED;
16962306a36Sopenharmony_ci	else /* bud == LAST */
17062306a36Sopenharmony_ci		handle += PAGE_SIZE - (zhdr->last_chunks  << CHUNK_SHIFT);
17162306a36Sopenharmony_ci	return handle;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci/* Returns the zbud page where a given handle is stored */
17562306a36Sopenharmony_cistatic struct zbud_header *handle_to_zbud_header(unsigned long handle)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	return (struct zbud_header *)(handle & PAGE_MASK);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci/* Returns the number of free chunks in a zbud page */
18162306a36Sopenharmony_cistatic int num_free_chunks(struct zbud_header *zhdr)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	/*
18462306a36Sopenharmony_ci	 * Rather than branch for different situations, just use the fact that
18562306a36Sopenharmony_ci	 * free buddies have a length of zero to simplify everything.
18662306a36Sopenharmony_ci	 */
18762306a36Sopenharmony_ci	return NCHUNKS - zhdr->first_chunks - zhdr->last_chunks;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci/*****************
19162306a36Sopenharmony_ci * API Functions
19262306a36Sopenharmony_ci*****************/
19362306a36Sopenharmony_ci/**
19462306a36Sopenharmony_ci * zbud_create_pool() - create a new zbud pool
19562306a36Sopenharmony_ci * @gfp:	gfp flags when allocating the zbud pool structure
19662306a36Sopenharmony_ci *
19762306a36Sopenharmony_ci * Return: pointer to the new zbud pool or NULL if the metadata allocation
19862306a36Sopenharmony_ci * failed.
19962306a36Sopenharmony_ci */
20062306a36Sopenharmony_cistatic struct zbud_pool *zbud_create_pool(gfp_t gfp)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct zbud_pool *pool;
20362306a36Sopenharmony_ci	int i;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	pool = kzalloc(sizeof(struct zbud_pool), gfp);
20662306a36Sopenharmony_ci	if (!pool)
20762306a36Sopenharmony_ci		return NULL;
20862306a36Sopenharmony_ci	spin_lock_init(&pool->lock);
20962306a36Sopenharmony_ci	for_each_unbuddied_list(i, 0)
21062306a36Sopenharmony_ci		INIT_LIST_HEAD(&pool->unbuddied[i]);
21162306a36Sopenharmony_ci	INIT_LIST_HEAD(&pool->buddied);
21262306a36Sopenharmony_ci	pool->pages_nr = 0;
21362306a36Sopenharmony_ci	return pool;
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci/**
21762306a36Sopenharmony_ci * zbud_destroy_pool() - destroys an existing zbud pool
21862306a36Sopenharmony_ci * @pool:	the zbud pool to be destroyed
21962306a36Sopenharmony_ci *
22062306a36Sopenharmony_ci * The pool should be emptied before this function is called.
22162306a36Sopenharmony_ci */
22262306a36Sopenharmony_cistatic void zbud_destroy_pool(struct zbud_pool *pool)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	kfree(pool);
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci/**
22862306a36Sopenharmony_ci * zbud_alloc() - allocates a region of a given size
22962306a36Sopenharmony_ci * @pool:	zbud pool from which to allocate
23062306a36Sopenharmony_ci * @size:	size in bytes of the desired allocation
23162306a36Sopenharmony_ci * @gfp:	gfp flags used if the pool needs to grow
23262306a36Sopenharmony_ci * @handle:	handle of the new allocation
23362306a36Sopenharmony_ci *
23462306a36Sopenharmony_ci * This function will attempt to find a free region in the pool large enough to
23562306a36Sopenharmony_ci * satisfy the allocation request.  A search of the unbuddied lists is
23662306a36Sopenharmony_ci * performed first. If no suitable free region is found, then a new page is
23762306a36Sopenharmony_ci * allocated and added to the pool to satisfy the request.
23862306a36Sopenharmony_ci *
23962306a36Sopenharmony_ci * gfp should not set __GFP_HIGHMEM as highmem pages cannot be used
24062306a36Sopenharmony_ci * as zbud pool pages.
24162306a36Sopenharmony_ci *
24262306a36Sopenharmony_ci * Return: 0 if success and handle is set, otherwise -EINVAL if the size or
24362306a36Sopenharmony_ci * gfp arguments are invalid or -ENOMEM if the pool was unable to allocate
24462306a36Sopenharmony_ci * a new page.
24562306a36Sopenharmony_ci */
24662306a36Sopenharmony_cistatic int zbud_alloc(struct zbud_pool *pool, size_t size, gfp_t gfp,
24762306a36Sopenharmony_ci			unsigned long *handle)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	int chunks, i, freechunks;
25062306a36Sopenharmony_ci	struct zbud_header *zhdr = NULL;
25162306a36Sopenharmony_ci	enum buddy bud;
25262306a36Sopenharmony_ci	struct page *page;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (!size || (gfp & __GFP_HIGHMEM))
25562306a36Sopenharmony_ci		return -EINVAL;
25662306a36Sopenharmony_ci	if (size > PAGE_SIZE - ZHDR_SIZE_ALIGNED - CHUNK_SIZE)
25762306a36Sopenharmony_ci		return -ENOSPC;
25862306a36Sopenharmony_ci	chunks = size_to_chunks(size);
25962306a36Sopenharmony_ci	spin_lock(&pool->lock);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	/* First, try to find an unbuddied zbud page. */
26262306a36Sopenharmony_ci	for_each_unbuddied_list(i, chunks) {
26362306a36Sopenharmony_ci		if (!list_empty(&pool->unbuddied[i])) {
26462306a36Sopenharmony_ci			zhdr = list_first_entry(&pool->unbuddied[i],
26562306a36Sopenharmony_ci					struct zbud_header, buddy);
26662306a36Sopenharmony_ci			list_del(&zhdr->buddy);
26762306a36Sopenharmony_ci			if (zhdr->first_chunks == 0)
26862306a36Sopenharmony_ci				bud = FIRST;
26962306a36Sopenharmony_ci			else
27062306a36Sopenharmony_ci				bud = LAST;
27162306a36Sopenharmony_ci			goto found;
27262306a36Sopenharmony_ci		}
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/* Couldn't find unbuddied zbud page, create new one */
27662306a36Sopenharmony_ci	spin_unlock(&pool->lock);
27762306a36Sopenharmony_ci	page = alloc_page(gfp);
27862306a36Sopenharmony_ci	if (!page)
27962306a36Sopenharmony_ci		return -ENOMEM;
28062306a36Sopenharmony_ci	spin_lock(&pool->lock);
28162306a36Sopenharmony_ci	pool->pages_nr++;
28262306a36Sopenharmony_ci	zhdr = init_zbud_page(page);
28362306a36Sopenharmony_ci	bud = FIRST;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cifound:
28662306a36Sopenharmony_ci	if (bud == FIRST)
28762306a36Sopenharmony_ci		zhdr->first_chunks = chunks;
28862306a36Sopenharmony_ci	else
28962306a36Sopenharmony_ci		zhdr->last_chunks = chunks;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (zhdr->first_chunks == 0 || zhdr->last_chunks == 0) {
29262306a36Sopenharmony_ci		/* Add to unbuddied list */
29362306a36Sopenharmony_ci		freechunks = num_free_chunks(zhdr);
29462306a36Sopenharmony_ci		list_add(&zhdr->buddy, &pool->unbuddied[freechunks]);
29562306a36Sopenharmony_ci	} else {
29662306a36Sopenharmony_ci		/* Add to buddied list */
29762306a36Sopenharmony_ci		list_add(&zhdr->buddy, &pool->buddied);
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	*handle = encode_handle(zhdr, bud);
30162306a36Sopenharmony_ci	spin_unlock(&pool->lock);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	return 0;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci/**
30762306a36Sopenharmony_ci * zbud_free() - frees the allocation associated with the given handle
30862306a36Sopenharmony_ci * @pool:	pool in which the allocation resided
30962306a36Sopenharmony_ci * @handle:	handle associated with the allocation returned by zbud_alloc()
31062306a36Sopenharmony_ci */
31162306a36Sopenharmony_cistatic void zbud_free(struct zbud_pool *pool, unsigned long handle)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	struct zbud_header *zhdr;
31462306a36Sopenharmony_ci	int freechunks;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	spin_lock(&pool->lock);
31762306a36Sopenharmony_ci	zhdr = handle_to_zbud_header(handle);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	/* If first buddy, handle will be page aligned */
32062306a36Sopenharmony_ci	if ((handle - ZHDR_SIZE_ALIGNED) & ~PAGE_MASK)
32162306a36Sopenharmony_ci		zhdr->last_chunks = 0;
32262306a36Sopenharmony_ci	else
32362306a36Sopenharmony_ci		zhdr->first_chunks = 0;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	/* Remove from existing buddy list */
32662306a36Sopenharmony_ci	list_del(&zhdr->buddy);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	if (zhdr->first_chunks == 0 && zhdr->last_chunks == 0) {
32962306a36Sopenharmony_ci		/* zbud page is empty, free */
33062306a36Sopenharmony_ci		free_zbud_page(zhdr);
33162306a36Sopenharmony_ci		pool->pages_nr--;
33262306a36Sopenharmony_ci	} else {
33362306a36Sopenharmony_ci		/* Add to unbuddied list */
33462306a36Sopenharmony_ci		freechunks = num_free_chunks(zhdr);
33562306a36Sopenharmony_ci		list_add(&zhdr->buddy, &pool->unbuddied[freechunks]);
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	spin_unlock(&pool->lock);
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci/**
34262306a36Sopenharmony_ci * zbud_map() - maps the allocation associated with the given handle
34362306a36Sopenharmony_ci * @pool:	pool in which the allocation resides
34462306a36Sopenharmony_ci * @handle:	handle associated with the allocation to be mapped
34562306a36Sopenharmony_ci *
34662306a36Sopenharmony_ci * While trivial for zbud, the mapping functions for others allocators
34762306a36Sopenharmony_ci * implementing this allocation API could have more complex information encoded
34862306a36Sopenharmony_ci * in the handle and could create temporary mappings to make the data
34962306a36Sopenharmony_ci * accessible to the user.
35062306a36Sopenharmony_ci *
35162306a36Sopenharmony_ci * Returns: a pointer to the mapped allocation
35262306a36Sopenharmony_ci */
35362306a36Sopenharmony_cistatic void *zbud_map(struct zbud_pool *pool, unsigned long handle)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	return (void *)(handle);
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci/**
35962306a36Sopenharmony_ci * zbud_unmap() - maps the allocation associated with the given handle
36062306a36Sopenharmony_ci * @pool:	pool in which the allocation resides
36162306a36Sopenharmony_ci * @handle:	handle associated with the allocation to be unmapped
36262306a36Sopenharmony_ci */
36362306a36Sopenharmony_cistatic void zbud_unmap(struct zbud_pool *pool, unsigned long handle)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci/**
36862306a36Sopenharmony_ci * zbud_get_pool_size() - gets the zbud pool size in pages
36962306a36Sopenharmony_ci * @pool:	pool whose size is being queried
37062306a36Sopenharmony_ci *
37162306a36Sopenharmony_ci * Returns: size in pages of the given pool.  The pool lock need not be
37262306a36Sopenharmony_ci * taken to access pages_nr.
37362306a36Sopenharmony_ci */
37462306a36Sopenharmony_cistatic u64 zbud_get_pool_size(struct zbud_pool *pool)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	return pool->pages_nr;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci/*****************
38062306a36Sopenharmony_ci * zpool
38162306a36Sopenharmony_ci ****************/
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic void *zbud_zpool_create(const char *name, gfp_t gfp)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	return zbud_create_pool(gfp);
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic void zbud_zpool_destroy(void *pool)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	zbud_destroy_pool(pool);
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic int zbud_zpool_malloc(void *pool, size_t size, gfp_t gfp,
39462306a36Sopenharmony_ci			unsigned long *handle)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	return zbud_alloc(pool, size, gfp, handle);
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_cistatic void zbud_zpool_free(void *pool, unsigned long handle)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	zbud_free(pool, handle);
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic void *zbud_zpool_map(void *pool, unsigned long handle,
40462306a36Sopenharmony_ci			enum zpool_mapmode mm)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	return zbud_map(pool, handle);
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_cistatic void zbud_zpool_unmap(void *pool, unsigned long handle)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	zbud_unmap(pool, handle);
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic u64 zbud_zpool_total_size(void *pool)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	return zbud_get_pool_size(pool) * PAGE_SIZE;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic struct zpool_driver zbud_zpool_driver = {
41962306a36Sopenharmony_ci	.type =		"zbud",
42062306a36Sopenharmony_ci	.sleep_mapped = true,
42162306a36Sopenharmony_ci	.owner =	THIS_MODULE,
42262306a36Sopenharmony_ci	.create =	zbud_zpool_create,
42362306a36Sopenharmony_ci	.destroy =	zbud_zpool_destroy,
42462306a36Sopenharmony_ci	.malloc =	zbud_zpool_malloc,
42562306a36Sopenharmony_ci	.free =		zbud_zpool_free,
42662306a36Sopenharmony_ci	.map =		zbud_zpool_map,
42762306a36Sopenharmony_ci	.unmap =	zbud_zpool_unmap,
42862306a36Sopenharmony_ci	.total_size =	zbud_zpool_total_size,
42962306a36Sopenharmony_ci};
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ciMODULE_ALIAS("zpool-zbud");
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic int __init init_zbud(void)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	/* Make sure the zbud header will fit in one chunk */
43662306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct zbud_header) > ZHDR_SIZE_ALIGNED);
43762306a36Sopenharmony_ci	pr_info("loaded\n");
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	zpool_register_driver(&zbud_zpool_driver);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	return 0;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic void __exit exit_zbud(void)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	zpool_unregister_driver(&zbud_zpool_driver);
44762306a36Sopenharmony_ci	pr_info("unloaded\n");
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cimodule_init(init_zbud);
45162306a36Sopenharmony_cimodule_exit(exit_zbud);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
45462306a36Sopenharmony_ciMODULE_AUTHOR("Seth Jennings <sjennings@variantweb.net>");
45562306a36Sopenharmony_ciMODULE_DESCRIPTION("Buddy Allocator for Compressed Pages");
456