mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-11-01 09:13:37 +00:00 
			
		
		
		
	mm/zbud: don't export any zbud API
The zbud doesn't need to export any API and it is meant to be used via
zpool API since the commit 12d79d64bf ("mm/zpool: update zswap to use
zpool").  So we can remove the unneeded zbud.h and move down zpool API to
avoid any forward declaration.
[linmiaohe@huawei.com: fix unused function warnings when CONFIG_ZPOOL is disabled]
  Link: https://lkml.kernel.org/r/20210619025508.1239386-1-linmiaohe@huawei.com
Link: https://lkml.kernel.org/r/20210608114515.206992-3-linmiaohe@huawei.com
Signed-off-by: Miaohe Lin <linmiaohe@huawei.com>
Cc: Dan Streetman <ddstreet@ieee.org>
Cc: Seth Jennings <sjenning@redhat.com>
Cc: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
			
			
This commit is contained in:
		
							parent
							
								
									f356aeacf7
								
							
						
					
					
						commit
						2a03085ce8
					
				
					 4 changed files with 396 additions and 424 deletions
				
			
		| 
						 | 
				
			
			@ -20172,7 +20172,6 @@ M:	Seth Jennings <sjenning@redhat.com>
 | 
			
		|||
M:	Dan Streetman <ddstreet@ieee.org>
 | 
			
		||||
L:	linux-mm@kvack.org
 | 
			
		||||
S:	Maintained
 | 
			
		||||
F:	include/linux/zbud.h
 | 
			
		||||
F:	mm/zbud.c
 | 
			
		||||
 | 
			
		||||
ZD1211RW WIRELESS DRIVER
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,23 +0,0 @@
 | 
			
		|||
/* SPDX-License-Identifier: GPL-2.0 */
 | 
			
		||||
#ifndef _ZBUD_H_
 | 
			
		||||
#define _ZBUD_H_
 | 
			
		||||
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
 | 
			
		||||
struct zbud_pool;
 | 
			
		||||
 | 
			
		||||
struct zbud_ops {
 | 
			
		||||
	int (*evict)(struct zbud_pool *pool, unsigned long handle);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct zbud_pool *zbud_create_pool(gfp_t gfp, const struct zbud_ops *ops);
 | 
			
		||||
void zbud_destroy_pool(struct zbud_pool *pool);
 | 
			
		||||
int zbud_alloc(struct zbud_pool *pool, size_t size, gfp_t gfp,
 | 
			
		||||
	unsigned long *handle);
 | 
			
		||||
void zbud_free(struct zbud_pool *pool, unsigned long handle);
 | 
			
		||||
int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries);
 | 
			
		||||
void *zbud_map(struct zbud_pool *pool, unsigned long handle);
 | 
			
		||||
void zbud_unmap(struct zbud_pool *pool, unsigned long handle);
 | 
			
		||||
u64 zbud_get_pool_size(struct zbud_pool *pool);
 | 
			
		||||
 | 
			
		||||
#endif /* _ZBUD_H_ */
 | 
			
		||||
| 
						 | 
				
			
			@ -674,6 +674,7 @@ config ZPOOL
 | 
			
		|||
 | 
			
		||||
config ZBUD
 | 
			
		||||
	tristate "Low (Up to 2x) density storage for compressed pages"
 | 
			
		||||
	depends on ZPOOL
 | 
			
		||||
	help
 | 
			
		||||
	  A special purpose allocator for storing compressed pages.
 | 
			
		||||
	  It is designed to store up to two compressed pages per physical
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										795
									
								
								mm/zbud.c
									
										
									
									
									
								
							
							
						
						
									
										795
									
								
								mm/zbud.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -51,7 +51,6 @@
 | 
			
		|||
#include <linux/preempt.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/spinlock.h>
 | 
			
		||||
#include <linux/zbud.h>
 | 
			
		||||
#include <linux/zpool.h>
 | 
			
		||||
 | 
			
		||||
/*****************
 | 
			
		||||
| 
						 | 
				
			
			@ -73,6 +72,12 @@
 | 
			
		|||
#define ZHDR_SIZE_ALIGNED CHUNK_SIZE
 | 
			
		||||
#define NCHUNKS		((PAGE_SIZE - ZHDR_SIZE_ALIGNED) >> CHUNK_SHIFT)
 | 
			
		||||
 | 
			
		||||
struct zbud_pool;
 | 
			
		||||
 | 
			
		||||
struct zbud_ops {
 | 
			
		||||
	int (*evict)(struct zbud_pool *pool, unsigned long handle);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * struct zbud_pool - stores metadata for each zbud pool
 | 
			
		||||
 * @lock:	protects all pool fields and first|last_chunk fields of any
 | 
			
		||||
| 
						 | 
				
			
			@ -104,10 +109,8 @@ struct zbud_pool {
 | 
			
		|||
	struct list_head lru;
 | 
			
		||||
	u64 pages_nr;
 | 
			
		||||
	const struct zbud_ops *ops;
 | 
			
		||||
#ifdef CONFIG_ZPOOL
 | 
			
		||||
	struct zpool *zpool;
 | 
			
		||||
	const struct zpool_ops *zpool_ops;
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -126,12 +129,399 @@ struct zbud_header {
 | 
			
		|||
	bool under_reclaim;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*****************
 | 
			
		||||
 * Helpers
 | 
			
		||||
*****************/
 | 
			
		||||
/* Just to make the code easier to read */
 | 
			
		||||
enum buddy {
 | 
			
		||||
	FIRST,
 | 
			
		||||
	LAST
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Converts an allocation size in bytes to size in zbud chunks */
 | 
			
		||||
static int size_to_chunks(size_t size)
 | 
			
		||||
{
 | 
			
		||||
	return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define for_each_unbuddied_list(_iter, _begin) \
 | 
			
		||||
	for ((_iter) = (_begin); (_iter) < NCHUNKS; (_iter)++)
 | 
			
		||||
 | 
			
		||||
/* Initializes the zbud header of a newly allocated zbud page */
 | 
			
		||||
static struct zbud_header *init_zbud_page(struct page *page)
 | 
			
		||||
{
 | 
			
		||||
	struct zbud_header *zhdr = page_address(page);
 | 
			
		||||
	zhdr->first_chunks = 0;
 | 
			
		||||
	zhdr->last_chunks = 0;
 | 
			
		||||
	INIT_LIST_HEAD(&zhdr->buddy);
 | 
			
		||||
	INIT_LIST_HEAD(&zhdr->lru);
 | 
			
		||||
	zhdr->under_reclaim = false;
 | 
			
		||||
	return zhdr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Resets the struct page fields and frees the page */
 | 
			
		||||
static void free_zbud_page(struct zbud_header *zhdr)
 | 
			
		||||
{
 | 
			
		||||
	__free_page(virt_to_page(zhdr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Encodes the handle of a particular buddy within a zbud page
 | 
			
		||||
 * Pool lock should be held as this function accesses first|last_chunks
 | 
			
		||||
 */
 | 
			
		||||
static unsigned long encode_handle(struct zbud_header *zhdr, enum buddy bud)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long handle;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * For now, the encoded handle is actually just the pointer to the data
 | 
			
		||||
	 * but this might not always be the case.  A little information hiding.
 | 
			
		||||
	 * Add CHUNK_SIZE to the handle if it is the first allocation to jump
 | 
			
		||||
	 * over the zbud header in the first chunk.
 | 
			
		||||
	 */
 | 
			
		||||
	handle = (unsigned long)zhdr;
 | 
			
		||||
	if (bud == FIRST)
 | 
			
		||||
		/* skip over zbud header */
 | 
			
		||||
		handle += ZHDR_SIZE_ALIGNED;
 | 
			
		||||
	else /* bud == LAST */
 | 
			
		||||
		handle += PAGE_SIZE - (zhdr->last_chunks  << CHUNK_SHIFT);
 | 
			
		||||
	return handle;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns the zbud page where a given handle is stored */
 | 
			
		||||
static struct zbud_header *handle_to_zbud_header(unsigned long handle)
 | 
			
		||||
{
 | 
			
		||||
	return (struct zbud_header *)(handle & PAGE_MASK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns the number of free chunks in a zbud page */
 | 
			
		||||
static int num_free_chunks(struct zbud_header *zhdr)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * Rather than branch for different situations, just use the fact that
 | 
			
		||||
	 * free buddies have a length of zero to simplify everything.
 | 
			
		||||
	 */
 | 
			
		||||
	return NCHUNKS - zhdr->first_chunks - zhdr->last_chunks;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*****************
 | 
			
		||||
 * API Functions
 | 
			
		||||
*****************/
 | 
			
		||||
/**
 | 
			
		||||
 * zbud_create_pool() - create a new zbud pool
 | 
			
		||||
 * @gfp:	gfp flags when allocating the zbud pool structure
 | 
			
		||||
 * @ops:	user-defined operations for the zbud pool
 | 
			
		||||
 *
 | 
			
		||||
 * Return: pointer to the new zbud pool or NULL if the metadata allocation
 | 
			
		||||
 * failed.
 | 
			
		||||
 */
 | 
			
		||||
static struct zbud_pool *zbud_create_pool(gfp_t gfp, const struct zbud_ops *ops)
 | 
			
		||||
{
 | 
			
		||||
	struct zbud_pool *pool;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	pool = kzalloc(sizeof(struct zbud_pool), gfp);
 | 
			
		||||
	if (!pool)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	spin_lock_init(&pool->lock);
 | 
			
		||||
	for_each_unbuddied_list(i, 0)
 | 
			
		||||
		INIT_LIST_HEAD(&pool->unbuddied[i]);
 | 
			
		||||
	INIT_LIST_HEAD(&pool->buddied);
 | 
			
		||||
	INIT_LIST_HEAD(&pool->lru);
 | 
			
		||||
	pool->pages_nr = 0;
 | 
			
		||||
	pool->ops = ops;
 | 
			
		||||
	return pool;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * zbud_destroy_pool() - destroys an existing zbud pool
 | 
			
		||||
 * @pool:	the zbud pool to be destroyed
 | 
			
		||||
 *
 | 
			
		||||
 * The pool should be emptied before this function is called.
 | 
			
		||||
 */
 | 
			
		||||
static void zbud_destroy_pool(struct zbud_pool *pool)
 | 
			
		||||
{
 | 
			
		||||
	kfree(pool);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * zbud_alloc() - allocates a region of a given size
 | 
			
		||||
 * @pool:	zbud pool from which to allocate
 | 
			
		||||
 * @size:	size in bytes of the desired allocation
 | 
			
		||||
 * @gfp:	gfp flags used if the pool needs to grow
 | 
			
		||||
 * @handle:	handle of the new allocation
 | 
			
		||||
 *
 | 
			
		||||
 * This function will attempt to find a free region in the pool large enough to
 | 
			
		||||
 * satisfy the allocation request.  A search of the unbuddied lists is
 | 
			
		||||
 * performed first. If no suitable free region is found, then a new page is
 | 
			
		||||
 * allocated and added to the pool to satisfy the request.
 | 
			
		||||
 *
 | 
			
		||||
 * gfp should not set __GFP_HIGHMEM as highmem pages cannot be used
 | 
			
		||||
 * as zbud pool pages.
 | 
			
		||||
 *
 | 
			
		||||
 * Return: 0 if success and handle is set, otherwise -EINVAL if the size or
 | 
			
		||||
 * gfp arguments are invalid or -ENOMEM if the pool was unable to allocate
 | 
			
		||||
 * a new page.
 | 
			
		||||
 */
 | 
			
		||||
static int zbud_alloc(struct zbud_pool *pool, size_t size, gfp_t gfp,
 | 
			
		||||
			unsigned long *handle)
 | 
			
		||||
{
 | 
			
		||||
	int chunks, i, freechunks;
 | 
			
		||||
	struct zbud_header *zhdr = NULL;
 | 
			
		||||
	enum buddy bud;
 | 
			
		||||
	struct page *page;
 | 
			
		||||
 | 
			
		||||
	if (!size || (gfp & __GFP_HIGHMEM))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	if (size > PAGE_SIZE - ZHDR_SIZE_ALIGNED - CHUNK_SIZE)
 | 
			
		||||
		return -ENOSPC;
 | 
			
		||||
	chunks = size_to_chunks(size);
 | 
			
		||||
	spin_lock(&pool->lock);
 | 
			
		||||
 | 
			
		||||
	/* First, try to find an unbuddied zbud page. */
 | 
			
		||||
	for_each_unbuddied_list(i, chunks) {
 | 
			
		||||
		if (!list_empty(&pool->unbuddied[i])) {
 | 
			
		||||
			zhdr = list_first_entry(&pool->unbuddied[i],
 | 
			
		||||
					struct zbud_header, buddy);
 | 
			
		||||
			list_del(&zhdr->buddy);
 | 
			
		||||
			if (zhdr->first_chunks == 0)
 | 
			
		||||
				bud = FIRST;
 | 
			
		||||
			else
 | 
			
		||||
				bud = LAST;
 | 
			
		||||
			goto found;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Couldn't find unbuddied zbud page, create new one */
 | 
			
		||||
	spin_unlock(&pool->lock);
 | 
			
		||||
	page = alloc_page(gfp);
 | 
			
		||||
	if (!page)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	spin_lock(&pool->lock);
 | 
			
		||||
	pool->pages_nr++;
 | 
			
		||||
	zhdr = init_zbud_page(page);
 | 
			
		||||
	bud = FIRST;
 | 
			
		||||
 | 
			
		||||
found:
 | 
			
		||||
	if (bud == FIRST)
 | 
			
		||||
		zhdr->first_chunks = chunks;
 | 
			
		||||
	else
 | 
			
		||||
		zhdr->last_chunks = chunks;
 | 
			
		||||
 | 
			
		||||
	if (zhdr->first_chunks == 0 || zhdr->last_chunks == 0) {
 | 
			
		||||
		/* Add to unbuddied list */
 | 
			
		||||
		freechunks = num_free_chunks(zhdr);
 | 
			
		||||
		list_add(&zhdr->buddy, &pool->unbuddied[freechunks]);
 | 
			
		||||
	} else {
 | 
			
		||||
		/* Add to buddied list */
 | 
			
		||||
		list_add(&zhdr->buddy, &pool->buddied);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Add/move zbud page to beginning of LRU */
 | 
			
		||||
	if (!list_empty(&zhdr->lru))
 | 
			
		||||
		list_del(&zhdr->lru);
 | 
			
		||||
	list_add(&zhdr->lru, &pool->lru);
 | 
			
		||||
 | 
			
		||||
	*handle = encode_handle(zhdr, bud);
 | 
			
		||||
	spin_unlock(&pool->lock);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * zbud_free() - frees the allocation associated with the given handle
 | 
			
		||||
 * @pool:	pool in which the allocation resided
 | 
			
		||||
 * @handle:	handle associated with the allocation returned by zbud_alloc()
 | 
			
		||||
 *
 | 
			
		||||
 * In the case that the zbud page in which the allocation resides is under
 | 
			
		||||
 * reclaim, as indicated by the PG_reclaim flag being set, this function
 | 
			
		||||
 * only sets the first|last_chunks to 0.  The page is actually freed
 | 
			
		||||
 * once both buddies are evicted (see zbud_reclaim_page() below).
 | 
			
		||||
 */
 | 
			
		||||
static void zbud_free(struct zbud_pool *pool, unsigned long handle)
 | 
			
		||||
{
 | 
			
		||||
	struct zbud_header *zhdr;
 | 
			
		||||
	int freechunks;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&pool->lock);
 | 
			
		||||
	zhdr = handle_to_zbud_header(handle);
 | 
			
		||||
 | 
			
		||||
	/* If first buddy, handle will be page aligned */
 | 
			
		||||
	if ((handle - ZHDR_SIZE_ALIGNED) & ~PAGE_MASK)
 | 
			
		||||
		zhdr->last_chunks = 0;
 | 
			
		||||
	else
 | 
			
		||||
		zhdr->first_chunks = 0;
 | 
			
		||||
 | 
			
		||||
	if (zhdr->under_reclaim) {
 | 
			
		||||
		/* zbud page is under reclaim, reclaim will free */
 | 
			
		||||
		spin_unlock(&pool->lock);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Remove from existing buddy list */
 | 
			
		||||
	list_del(&zhdr->buddy);
 | 
			
		||||
 | 
			
		||||
	if (zhdr->first_chunks == 0 && zhdr->last_chunks == 0) {
 | 
			
		||||
		/* zbud page is empty, free */
 | 
			
		||||
		list_del(&zhdr->lru);
 | 
			
		||||
		free_zbud_page(zhdr);
 | 
			
		||||
		pool->pages_nr--;
 | 
			
		||||
	} else {
 | 
			
		||||
		/* Add to unbuddied list */
 | 
			
		||||
		freechunks = num_free_chunks(zhdr);
 | 
			
		||||
		list_add(&zhdr->buddy, &pool->unbuddied[freechunks]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	spin_unlock(&pool->lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * zbud_reclaim_page() - evicts allocations from a pool page and frees it
 | 
			
		||||
 * @pool:	pool from which a page will attempt to be evicted
 | 
			
		||||
 * @retries:	number of pages on the LRU list for which eviction will
 | 
			
		||||
 *		be attempted before failing
 | 
			
		||||
 *
 | 
			
		||||
 * zbud reclaim is different from normal system reclaim in that the reclaim is
 | 
			
		||||
 * done from the bottom, up.  This is because only the bottom layer, zbud, has
 | 
			
		||||
 * information on how the allocations are organized within each zbud page. This
 | 
			
		||||
 * has the potential to create interesting locking situations between zbud and
 | 
			
		||||
 * the user, however.
 | 
			
		||||
 *
 | 
			
		||||
 * To avoid these, this is how zbud_reclaim_page() should be called:
 | 
			
		||||
 *
 | 
			
		||||
 * The user detects a page should be reclaimed and calls zbud_reclaim_page().
 | 
			
		||||
 * zbud_reclaim_page() will remove a zbud page from the pool LRU list and call
 | 
			
		||||
 * the user-defined eviction handler with the pool and handle as arguments.
 | 
			
		||||
 *
 | 
			
		||||
 * If the handle can not be evicted, the eviction handler should return
 | 
			
		||||
 * non-zero. zbud_reclaim_page() will add the zbud page back to the
 | 
			
		||||
 * appropriate list and try the next zbud page on the LRU up to
 | 
			
		||||
 * a user defined number of retries.
 | 
			
		||||
 *
 | 
			
		||||
 * If the handle is successfully evicted, the eviction handler should
 | 
			
		||||
 * return 0 _and_ should have called zbud_free() on the handle. zbud_free()
 | 
			
		||||
 * contains logic to delay freeing the page if the page is under reclaim,
 | 
			
		||||
 * as indicated by the setting of the PG_reclaim flag on the underlying page.
 | 
			
		||||
 *
 | 
			
		||||
 * If all buddies in the zbud page are successfully evicted, then the
 | 
			
		||||
 * zbud page can be freed.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: 0 if page is successfully freed, otherwise -EINVAL if there are
 | 
			
		||||
 * no pages to evict or an eviction handler is not registered, -EAGAIN if
 | 
			
		||||
 * the retry limit was hit.
 | 
			
		||||
 */
 | 
			
		||||
static int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries)
 | 
			
		||||
{
 | 
			
		||||
	int i, ret, freechunks;
 | 
			
		||||
	struct zbud_header *zhdr;
 | 
			
		||||
	unsigned long first_handle = 0, last_handle = 0;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&pool->lock);
 | 
			
		||||
	if (!pool->ops || !pool->ops->evict || list_empty(&pool->lru) ||
 | 
			
		||||
			retries == 0) {
 | 
			
		||||
		spin_unlock(&pool->lock);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	for (i = 0; i < retries; i++) {
 | 
			
		||||
		zhdr = list_last_entry(&pool->lru, struct zbud_header, lru);
 | 
			
		||||
		list_del(&zhdr->lru);
 | 
			
		||||
		list_del(&zhdr->buddy);
 | 
			
		||||
		/* Protect zbud page against free */
 | 
			
		||||
		zhdr->under_reclaim = true;
 | 
			
		||||
		/*
 | 
			
		||||
		 * We need encode the handles before unlocking, since we can
 | 
			
		||||
		 * race with free that will set (first|last)_chunks to 0
 | 
			
		||||
		 */
 | 
			
		||||
		first_handle = 0;
 | 
			
		||||
		last_handle = 0;
 | 
			
		||||
		if (zhdr->first_chunks)
 | 
			
		||||
			first_handle = encode_handle(zhdr, FIRST);
 | 
			
		||||
		if (zhdr->last_chunks)
 | 
			
		||||
			last_handle = encode_handle(zhdr, LAST);
 | 
			
		||||
		spin_unlock(&pool->lock);
 | 
			
		||||
 | 
			
		||||
		/* Issue the eviction callback(s) */
 | 
			
		||||
		if (first_handle) {
 | 
			
		||||
			ret = pool->ops->evict(pool, first_handle);
 | 
			
		||||
			if (ret)
 | 
			
		||||
				goto next;
 | 
			
		||||
		}
 | 
			
		||||
		if (last_handle) {
 | 
			
		||||
			ret = pool->ops->evict(pool, last_handle);
 | 
			
		||||
			if (ret)
 | 
			
		||||
				goto next;
 | 
			
		||||
		}
 | 
			
		||||
next:
 | 
			
		||||
		spin_lock(&pool->lock);
 | 
			
		||||
		zhdr->under_reclaim = false;
 | 
			
		||||
		if (zhdr->first_chunks == 0 && zhdr->last_chunks == 0) {
 | 
			
		||||
			/*
 | 
			
		||||
			 * Both buddies are now free, free the zbud page and
 | 
			
		||||
			 * return success.
 | 
			
		||||
			 */
 | 
			
		||||
			free_zbud_page(zhdr);
 | 
			
		||||
			pool->pages_nr--;
 | 
			
		||||
			spin_unlock(&pool->lock);
 | 
			
		||||
			return 0;
 | 
			
		||||
		} else if (zhdr->first_chunks == 0 ||
 | 
			
		||||
				zhdr->last_chunks == 0) {
 | 
			
		||||
			/* add to unbuddied list */
 | 
			
		||||
			freechunks = num_free_chunks(zhdr);
 | 
			
		||||
			list_add(&zhdr->buddy, &pool->unbuddied[freechunks]);
 | 
			
		||||
		} else {
 | 
			
		||||
			/* add to buddied list */
 | 
			
		||||
			list_add(&zhdr->buddy, &pool->buddied);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* add to beginning of LRU */
 | 
			
		||||
		list_add(&zhdr->lru, &pool->lru);
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock(&pool->lock);
 | 
			
		||||
	return -EAGAIN;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * zbud_map() - maps the allocation associated with the given handle
 | 
			
		||||
 * @pool:	pool in which the allocation resides
 | 
			
		||||
 * @handle:	handle associated with the allocation to be mapped
 | 
			
		||||
 *
 | 
			
		||||
 * While trivial for zbud, the mapping functions for others allocators
 | 
			
		||||
 * implementing this allocation API could have more complex information encoded
 | 
			
		||||
 * in the handle and could create temporary mappings to make the data
 | 
			
		||||
 * accessible to the user.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: a pointer to the mapped allocation
 | 
			
		||||
 */
 | 
			
		||||
static void *zbud_map(struct zbud_pool *pool, unsigned long handle)
 | 
			
		||||
{
 | 
			
		||||
	return (void *)(handle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * zbud_unmap() - maps the allocation associated with the given handle
 | 
			
		||||
 * @pool:	pool in which the allocation resides
 | 
			
		||||
 * @handle:	handle associated with the allocation to be unmapped
 | 
			
		||||
 */
 | 
			
		||||
static void zbud_unmap(struct zbud_pool *pool, unsigned long handle)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * zbud_get_pool_size() - gets the zbud pool size in pages
 | 
			
		||||
 * @pool:	pool whose size is being queried
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: size in pages of the given pool.  The pool lock need not be
 | 
			
		||||
 * taken to access pages_nr.
 | 
			
		||||
 */
 | 
			
		||||
static u64 zbud_get_pool_size(struct zbud_pool *pool)
 | 
			
		||||
{
 | 
			
		||||
	return pool->pages_nr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*****************
 | 
			
		||||
 * zpool
 | 
			
		||||
 ****************/
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ZPOOL
 | 
			
		||||
 | 
			
		||||
static int zbud_zpool_evict(struct zbud_pool *pool, unsigned long handle)
 | 
			
		||||
{
 | 
			
		||||
	if (pool->zpool && pool->zpool_ops && pool->zpool_ops->evict)
 | 
			
		||||
| 
						 | 
				
			
			@ -222,396 +612,6 @@ static struct zpool_driver zbud_zpool_driver = {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
MODULE_ALIAS("zpool-zbud");
 | 
			
		||||
#endif /* CONFIG_ZPOOL */
 | 
			
		||||
 | 
			
		||||
/*****************
 | 
			
		||||
 * Helpers
 | 
			
		||||
*****************/
 | 
			
		||||
/* Just to make the code easier to read */
 | 
			
		||||
enum buddy {
 | 
			
		||||
	FIRST,
 | 
			
		||||
	LAST
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Converts an allocation size in bytes to size in zbud chunks */
 | 
			
		||||
static int size_to_chunks(size_t size)
 | 
			
		||||
{
 | 
			
		||||
	return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define for_each_unbuddied_list(_iter, _begin) \
 | 
			
		||||
	for ((_iter) = (_begin); (_iter) < NCHUNKS; (_iter)++)
 | 
			
		||||
 | 
			
		||||
/* Initializes the zbud header of a newly allocated zbud page */
 | 
			
		||||
static struct zbud_header *init_zbud_page(struct page *page)
 | 
			
		||||
{
 | 
			
		||||
	struct zbud_header *zhdr = page_address(page);
 | 
			
		||||
	zhdr->first_chunks = 0;
 | 
			
		||||
	zhdr->last_chunks = 0;
 | 
			
		||||
	INIT_LIST_HEAD(&zhdr->buddy);
 | 
			
		||||
	INIT_LIST_HEAD(&zhdr->lru);
 | 
			
		||||
	zhdr->under_reclaim = false;
 | 
			
		||||
	return zhdr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Resets the struct page fields and frees the page */
 | 
			
		||||
static void free_zbud_page(struct zbud_header *zhdr)
 | 
			
		||||
{
 | 
			
		||||
	__free_page(virt_to_page(zhdr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Encodes the handle of a particular buddy within a zbud page
 | 
			
		||||
 * Pool lock should be held as this function accesses first|last_chunks
 | 
			
		||||
 */
 | 
			
		||||
static unsigned long encode_handle(struct zbud_header *zhdr, enum buddy bud)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long handle;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * For now, the encoded handle is actually just the pointer to the data
 | 
			
		||||
	 * but this might not always be the case.  A little information hiding.
 | 
			
		||||
	 * Add CHUNK_SIZE to the handle if it is the first allocation to jump
 | 
			
		||||
	 * over the zbud header in the first chunk.
 | 
			
		||||
	 */
 | 
			
		||||
	handle = (unsigned long)zhdr;
 | 
			
		||||
	if (bud == FIRST)
 | 
			
		||||
		/* skip over zbud header */
 | 
			
		||||
		handle += ZHDR_SIZE_ALIGNED;
 | 
			
		||||
	else /* bud == LAST */
 | 
			
		||||
		handle += PAGE_SIZE - (zhdr->last_chunks  << CHUNK_SHIFT);
 | 
			
		||||
	return handle;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns the zbud page where a given handle is stored */
 | 
			
		||||
static struct zbud_header *handle_to_zbud_header(unsigned long handle)
 | 
			
		||||
{
 | 
			
		||||
	return (struct zbud_header *)(handle & PAGE_MASK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns the number of free chunks in a zbud page */
 | 
			
		||||
static int num_free_chunks(struct zbud_header *zhdr)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * Rather than branch for different situations, just use the fact that
 | 
			
		||||
	 * free buddies have a length of zero to simplify everything.
 | 
			
		||||
	 */
 | 
			
		||||
	return NCHUNKS - zhdr->first_chunks - zhdr->last_chunks;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*****************
 | 
			
		||||
 * API Functions
 | 
			
		||||
*****************/
 | 
			
		||||
/**
 | 
			
		||||
 * zbud_create_pool() - create a new zbud pool
 | 
			
		||||
 * @gfp:	gfp flags when allocating the zbud pool structure
 | 
			
		||||
 * @ops:	user-defined operations for the zbud pool
 | 
			
		||||
 *
 | 
			
		||||
 * Return: pointer to the new zbud pool or NULL if the metadata allocation
 | 
			
		||||
 * failed.
 | 
			
		||||
 */
 | 
			
		||||
struct zbud_pool *zbud_create_pool(gfp_t gfp, const struct zbud_ops *ops)
 | 
			
		||||
{
 | 
			
		||||
	struct zbud_pool *pool;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	pool = kzalloc(sizeof(struct zbud_pool), gfp);
 | 
			
		||||
	if (!pool)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	spin_lock_init(&pool->lock);
 | 
			
		||||
	for_each_unbuddied_list(i, 0)
 | 
			
		||||
		INIT_LIST_HEAD(&pool->unbuddied[i]);
 | 
			
		||||
	INIT_LIST_HEAD(&pool->buddied);
 | 
			
		||||
	INIT_LIST_HEAD(&pool->lru);
 | 
			
		||||
	pool->pages_nr = 0;
 | 
			
		||||
	pool->ops = ops;
 | 
			
		||||
	return pool;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * zbud_destroy_pool() - destroys an existing zbud pool
 | 
			
		||||
 * @pool:	the zbud pool to be destroyed
 | 
			
		||||
 *
 | 
			
		||||
 * The pool should be emptied before this function is called.
 | 
			
		||||
 */
 | 
			
		||||
void zbud_destroy_pool(struct zbud_pool *pool)
 | 
			
		||||
{
 | 
			
		||||
	kfree(pool);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * zbud_alloc() - allocates a region of a given size
 | 
			
		||||
 * @pool:	zbud pool from which to allocate
 | 
			
		||||
 * @size:	size in bytes of the desired allocation
 | 
			
		||||
 * @gfp:	gfp flags used if the pool needs to grow
 | 
			
		||||
 * @handle:	handle of the new allocation
 | 
			
		||||
 *
 | 
			
		||||
 * This function will attempt to find a free region in the pool large enough to
 | 
			
		||||
 * satisfy the allocation request.  A search of the unbuddied lists is
 | 
			
		||||
 * performed first. If no suitable free region is found, then a new page is
 | 
			
		||||
 * allocated and added to the pool to satisfy the request.
 | 
			
		||||
 *
 | 
			
		||||
 * gfp should not set __GFP_HIGHMEM as highmem pages cannot be used
 | 
			
		||||
 * as zbud pool pages.
 | 
			
		||||
 *
 | 
			
		||||
 * Return: 0 if success and handle is set, otherwise -EINVAL if the size or
 | 
			
		||||
 * gfp arguments are invalid or -ENOMEM if the pool was unable to allocate
 | 
			
		||||
 * a new page.
 | 
			
		||||
 */
 | 
			
		||||
int zbud_alloc(struct zbud_pool *pool, size_t size, gfp_t gfp,
 | 
			
		||||
			unsigned long *handle)
 | 
			
		||||
{
 | 
			
		||||
	int chunks, i, freechunks;
 | 
			
		||||
	struct zbud_header *zhdr = NULL;
 | 
			
		||||
	enum buddy bud;
 | 
			
		||||
	struct page *page;
 | 
			
		||||
 | 
			
		||||
	if (!size || (gfp & __GFP_HIGHMEM))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	if (size > PAGE_SIZE - ZHDR_SIZE_ALIGNED - CHUNK_SIZE)
 | 
			
		||||
		return -ENOSPC;
 | 
			
		||||
	chunks = size_to_chunks(size);
 | 
			
		||||
	spin_lock(&pool->lock);
 | 
			
		||||
 | 
			
		||||
	/* First, try to find an unbuddied zbud page. */
 | 
			
		||||
	for_each_unbuddied_list(i, chunks) {
 | 
			
		||||
		if (!list_empty(&pool->unbuddied[i])) {
 | 
			
		||||
			zhdr = list_first_entry(&pool->unbuddied[i],
 | 
			
		||||
					struct zbud_header, buddy);
 | 
			
		||||
			list_del(&zhdr->buddy);
 | 
			
		||||
			if (zhdr->first_chunks == 0)
 | 
			
		||||
				bud = FIRST;
 | 
			
		||||
			else
 | 
			
		||||
				bud = LAST;
 | 
			
		||||
			goto found;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Couldn't find unbuddied zbud page, create new one */
 | 
			
		||||
	spin_unlock(&pool->lock);
 | 
			
		||||
	page = alloc_page(gfp);
 | 
			
		||||
	if (!page)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	spin_lock(&pool->lock);
 | 
			
		||||
	pool->pages_nr++;
 | 
			
		||||
	zhdr = init_zbud_page(page);
 | 
			
		||||
	bud = FIRST;
 | 
			
		||||
 | 
			
		||||
found:
 | 
			
		||||
	if (bud == FIRST)
 | 
			
		||||
		zhdr->first_chunks = chunks;
 | 
			
		||||
	else
 | 
			
		||||
		zhdr->last_chunks = chunks;
 | 
			
		||||
 | 
			
		||||
	if (zhdr->first_chunks == 0 || zhdr->last_chunks == 0) {
 | 
			
		||||
		/* Add to unbuddied list */
 | 
			
		||||
		freechunks = num_free_chunks(zhdr);
 | 
			
		||||
		list_add(&zhdr->buddy, &pool->unbuddied[freechunks]);
 | 
			
		||||
	} else {
 | 
			
		||||
		/* Add to buddied list */
 | 
			
		||||
		list_add(&zhdr->buddy, &pool->buddied);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Add/move zbud page to beginning of LRU */
 | 
			
		||||
	if (!list_empty(&zhdr->lru))
 | 
			
		||||
		list_del(&zhdr->lru);
 | 
			
		||||
	list_add(&zhdr->lru, &pool->lru);
 | 
			
		||||
 | 
			
		||||
	*handle = encode_handle(zhdr, bud);
 | 
			
		||||
	spin_unlock(&pool->lock);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * zbud_free() - frees the allocation associated with the given handle
 | 
			
		||||
 * @pool:	pool in which the allocation resided
 | 
			
		||||
 * @handle:	handle associated with the allocation returned by zbud_alloc()
 | 
			
		||||
 *
 | 
			
		||||
 * In the case that the zbud page in which the allocation resides is under
 | 
			
		||||
 * reclaim, as indicated by the PG_reclaim flag being set, this function
 | 
			
		||||
 * only sets the first|last_chunks to 0.  The page is actually freed
 | 
			
		||||
 * once both buddies are evicted (see zbud_reclaim_page() below).
 | 
			
		||||
 */
 | 
			
		||||
void zbud_free(struct zbud_pool *pool, unsigned long handle)
 | 
			
		||||
{
 | 
			
		||||
	struct zbud_header *zhdr;
 | 
			
		||||
	int freechunks;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&pool->lock);
 | 
			
		||||
	zhdr = handle_to_zbud_header(handle);
 | 
			
		||||
 | 
			
		||||
	/* If first buddy, handle will be page aligned */
 | 
			
		||||
	if ((handle - ZHDR_SIZE_ALIGNED) & ~PAGE_MASK)
 | 
			
		||||
		zhdr->last_chunks = 0;
 | 
			
		||||
	else
 | 
			
		||||
		zhdr->first_chunks = 0;
 | 
			
		||||
 | 
			
		||||
	if (zhdr->under_reclaim) {
 | 
			
		||||
		/* zbud page is under reclaim, reclaim will free */
 | 
			
		||||
		spin_unlock(&pool->lock);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Remove from existing buddy list */
 | 
			
		||||
	list_del(&zhdr->buddy);
 | 
			
		||||
 | 
			
		||||
	if (zhdr->first_chunks == 0 && zhdr->last_chunks == 0) {
 | 
			
		||||
		/* zbud page is empty, free */
 | 
			
		||||
		list_del(&zhdr->lru);
 | 
			
		||||
		free_zbud_page(zhdr);
 | 
			
		||||
		pool->pages_nr--;
 | 
			
		||||
	} else {
 | 
			
		||||
		/* Add to unbuddied list */
 | 
			
		||||
		freechunks = num_free_chunks(zhdr);
 | 
			
		||||
		list_add(&zhdr->buddy, &pool->unbuddied[freechunks]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	spin_unlock(&pool->lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * zbud_reclaim_page() - evicts allocations from a pool page and frees it
 | 
			
		||||
 * @pool:	pool from which a page will attempt to be evicted
 | 
			
		||||
 * @retries:	number of pages on the LRU list for which eviction will
 | 
			
		||||
 *		be attempted before failing
 | 
			
		||||
 *
 | 
			
		||||
 * zbud reclaim is different from normal system reclaim in that the reclaim is
 | 
			
		||||
 * done from the bottom, up.  This is because only the bottom layer, zbud, has
 | 
			
		||||
 * information on how the allocations are organized within each zbud page. This
 | 
			
		||||
 * has the potential to create interesting locking situations between zbud and
 | 
			
		||||
 * the user, however.
 | 
			
		||||
 *
 | 
			
		||||
 * To avoid these, this is how zbud_reclaim_page() should be called:
 | 
			
		||||
 *
 | 
			
		||||
 * The user detects a page should be reclaimed and calls zbud_reclaim_page().
 | 
			
		||||
 * zbud_reclaim_page() will remove a zbud page from the pool LRU list and call
 | 
			
		||||
 * the user-defined eviction handler with the pool and handle as arguments.
 | 
			
		||||
 *
 | 
			
		||||
 * If the handle can not be evicted, the eviction handler should return
 | 
			
		||||
 * non-zero. zbud_reclaim_page() will add the zbud page back to the
 | 
			
		||||
 * appropriate list and try the next zbud page on the LRU up to
 | 
			
		||||
 * a user defined number of retries.
 | 
			
		||||
 *
 | 
			
		||||
 * If the handle is successfully evicted, the eviction handler should
 | 
			
		||||
 * return 0 _and_ should have called zbud_free() on the handle. zbud_free()
 | 
			
		||||
 * contains logic to delay freeing the page if the page is under reclaim,
 | 
			
		||||
 * as indicated by the setting of the PG_reclaim flag on the underlying page.
 | 
			
		||||
 *
 | 
			
		||||
 * If all buddies in the zbud page are successfully evicted, then the
 | 
			
		||||
 * zbud page can be freed.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: 0 if page is successfully freed, otherwise -EINVAL if there are
 | 
			
		||||
 * no pages to evict or an eviction handler is not registered, -EAGAIN if
 | 
			
		||||
 * the retry limit was hit.
 | 
			
		||||
 */
 | 
			
		||||
int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries)
 | 
			
		||||
{
 | 
			
		||||
	int i, ret, freechunks;
 | 
			
		||||
	struct zbud_header *zhdr;
 | 
			
		||||
	unsigned long first_handle = 0, last_handle = 0;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&pool->lock);
 | 
			
		||||
	if (!pool->ops || !pool->ops->evict || list_empty(&pool->lru) ||
 | 
			
		||||
			retries == 0) {
 | 
			
		||||
		spin_unlock(&pool->lock);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	for (i = 0; i < retries; i++) {
 | 
			
		||||
		zhdr = list_last_entry(&pool->lru, struct zbud_header, lru);
 | 
			
		||||
		list_del(&zhdr->lru);
 | 
			
		||||
		list_del(&zhdr->buddy);
 | 
			
		||||
		/* Protect zbud page against free */
 | 
			
		||||
		zhdr->under_reclaim = true;
 | 
			
		||||
		/*
 | 
			
		||||
		 * We need encode the handles before unlocking, since we can
 | 
			
		||||
		 * race with free that will set (first|last)_chunks to 0
 | 
			
		||||
		 */
 | 
			
		||||
		first_handle = 0;
 | 
			
		||||
		last_handle = 0;
 | 
			
		||||
		if (zhdr->first_chunks)
 | 
			
		||||
			first_handle = encode_handle(zhdr, FIRST);
 | 
			
		||||
		if (zhdr->last_chunks)
 | 
			
		||||
			last_handle = encode_handle(zhdr, LAST);
 | 
			
		||||
		spin_unlock(&pool->lock);
 | 
			
		||||
 | 
			
		||||
		/* Issue the eviction callback(s) */
 | 
			
		||||
		if (first_handle) {
 | 
			
		||||
			ret = pool->ops->evict(pool, first_handle);
 | 
			
		||||
			if (ret)
 | 
			
		||||
				goto next;
 | 
			
		||||
		}
 | 
			
		||||
		if (last_handle) {
 | 
			
		||||
			ret = pool->ops->evict(pool, last_handle);
 | 
			
		||||
			if (ret)
 | 
			
		||||
				goto next;
 | 
			
		||||
		}
 | 
			
		||||
next:
 | 
			
		||||
		spin_lock(&pool->lock);
 | 
			
		||||
		zhdr->under_reclaim = false;
 | 
			
		||||
		if (zhdr->first_chunks == 0 && zhdr->last_chunks == 0) {
 | 
			
		||||
			/*
 | 
			
		||||
			 * Both buddies are now free, free the zbud page and
 | 
			
		||||
			 * return success.
 | 
			
		||||
			 */
 | 
			
		||||
			free_zbud_page(zhdr);
 | 
			
		||||
			pool->pages_nr--;
 | 
			
		||||
			spin_unlock(&pool->lock);
 | 
			
		||||
			return 0;
 | 
			
		||||
		} else if (zhdr->first_chunks == 0 ||
 | 
			
		||||
				zhdr->last_chunks == 0) {
 | 
			
		||||
			/* add to unbuddied list */
 | 
			
		||||
			freechunks = num_free_chunks(zhdr);
 | 
			
		||||
			list_add(&zhdr->buddy, &pool->unbuddied[freechunks]);
 | 
			
		||||
		} else {
 | 
			
		||||
			/* add to buddied list */
 | 
			
		||||
			list_add(&zhdr->buddy, &pool->buddied);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* add to beginning of LRU */
 | 
			
		||||
		list_add(&zhdr->lru, &pool->lru);
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock(&pool->lock);
 | 
			
		||||
	return -EAGAIN;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * zbud_map() - maps the allocation associated with the given handle
 | 
			
		||||
 * @pool:	pool in which the allocation resides
 | 
			
		||||
 * @handle:	handle associated with the allocation to be mapped
 | 
			
		||||
 *
 | 
			
		||||
 * While trivial for zbud, the mapping functions for others allocators
 | 
			
		||||
 * implementing this allocation API could have more complex information encoded
 | 
			
		||||
 * in the handle and could create temporary mappings to make the data
 | 
			
		||||
 * accessible to the user.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: a pointer to the mapped allocation
 | 
			
		||||
 */
 | 
			
		||||
void *zbud_map(struct zbud_pool *pool, unsigned long handle)
 | 
			
		||||
{
 | 
			
		||||
	return (void *)(handle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * zbud_unmap() - maps the allocation associated with the given handle
 | 
			
		||||
 * @pool:	pool in which the allocation resides
 | 
			
		||||
 * @handle:	handle associated with the allocation to be unmapped
 | 
			
		||||
 */
 | 
			
		||||
void zbud_unmap(struct zbud_pool *pool, unsigned long handle)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * zbud_get_pool_size() - gets the zbud pool size in pages
 | 
			
		||||
 * @pool:	pool whose size is being queried
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: size in pages of the given pool.  The pool lock need not be
 | 
			
		||||
 * taken to access pages_nr.
 | 
			
		||||
 */
 | 
			
		||||
u64 zbud_get_pool_size(struct zbud_pool *pool)
 | 
			
		||||
{
 | 
			
		||||
	return pool->pages_nr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __init init_zbud(void)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -619,19 +619,14 @@ static int __init init_zbud(void)
 | 
			
		|||
	BUILD_BUG_ON(sizeof(struct zbud_header) > ZHDR_SIZE_ALIGNED);
 | 
			
		||||
	pr_info("loaded\n");
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ZPOOL
 | 
			
		||||
	zpool_register_driver(&zbud_zpool_driver);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __exit exit_zbud(void)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_ZPOOL
 | 
			
		||||
	zpool_unregister_driver(&zbud_zpool_driver);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	pr_info("unloaded\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue