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