162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * zsmalloc memory allocator 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2011 Nitin Gupta 562306a36Sopenharmony_ci * Copyright (C) 2012, 2013 Minchan Kim 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This code is released using a dual license strategy: BSD/GPL 862306a36Sopenharmony_ci * You can choose the license that better fits your requirements. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Released under the terms of 3-clause BSD License 1162306a36Sopenharmony_ci * Released under the terms of GNU General Public License Version 2.0 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* 1562306a36Sopenharmony_ci * Following is how we use various fields and flags of underlying 1662306a36Sopenharmony_ci * struct page(s) to form a zspage. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * Usage of struct page fields: 1962306a36Sopenharmony_ci * page->private: points to zspage 2062306a36Sopenharmony_ci * page->index: links together all component pages of a zspage 2162306a36Sopenharmony_ci * For the huge page, this is always 0, so we use this field 2262306a36Sopenharmony_ci * to store handle. 2362306a36Sopenharmony_ci * page->page_type: first object offset in a subpage of zspage 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * Usage of struct page flags: 2662306a36Sopenharmony_ci * PG_private: identifies the first component page 2762306a36Sopenharmony_ci * PG_owner_priv_1: identifies the huge component page 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * lock ordering: 3562306a36Sopenharmony_ci * page_lock 3662306a36Sopenharmony_ci * pool->lock 3762306a36Sopenharmony_ci * zspage->lock 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#include <linux/module.h> 4162306a36Sopenharmony_ci#include <linux/kernel.h> 4262306a36Sopenharmony_ci#include <linux/sched.h> 4362306a36Sopenharmony_ci#include <linux/bitops.h> 4462306a36Sopenharmony_ci#include <linux/errno.h> 4562306a36Sopenharmony_ci#include <linux/highmem.h> 4662306a36Sopenharmony_ci#include <linux/string.h> 4762306a36Sopenharmony_ci#include <linux/slab.h> 4862306a36Sopenharmony_ci#include <linux/pgtable.h> 4962306a36Sopenharmony_ci#include <asm/tlbflush.h> 5062306a36Sopenharmony_ci#include <linux/cpumask.h> 5162306a36Sopenharmony_ci#include <linux/cpu.h> 5262306a36Sopenharmony_ci#include <linux/vmalloc.h> 5362306a36Sopenharmony_ci#include <linux/preempt.h> 5462306a36Sopenharmony_ci#include <linux/spinlock.h> 5562306a36Sopenharmony_ci#include <linux/shrinker.h> 5662306a36Sopenharmony_ci#include <linux/types.h> 5762306a36Sopenharmony_ci#include <linux/debugfs.h> 5862306a36Sopenharmony_ci#include <linux/zsmalloc.h> 5962306a36Sopenharmony_ci#include <linux/zpool.h> 6062306a36Sopenharmony_ci#include <linux/migrate.h> 6162306a36Sopenharmony_ci#include <linux/wait.h> 6262306a36Sopenharmony_ci#include <linux/pagemap.h> 6362306a36Sopenharmony_ci#include <linux/fs.h> 6462306a36Sopenharmony_ci#include <linux/local_lock.h> 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define ZSPAGE_MAGIC 0x58 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* 6962306a36Sopenharmony_ci * This must be power of 2 and greater than or equal to sizeof(link_free). 7062306a36Sopenharmony_ci * These two conditions ensure that any 'struct link_free' itself doesn't 7162306a36Sopenharmony_ci * span more than 1 page which avoids complex case of mapping 2 pages simply 7262306a36Sopenharmony_ci * to restore link_free pointer values. 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_ci#define ZS_ALIGN 8 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define ZS_HANDLE_SIZE (sizeof(unsigned long)) 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* 7962306a36Sopenharmony_ci * Object location (<PFN>, <obj_idx>) is encoded as 8062306a36Sopenharmony_ci * a single (unsigned long) handle value. 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * Note that object index <obj_idx> starts from 0. 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * This is made more complicated by various memory models and PAE. 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#ifndef MAX_POSSIBLE_PHYSMEM_BITS 8862306a36Sopenharmony_ci#ifdef MAX_PHYSMEM_BITS 8962306a36Sopenharmony_ci#define MAX_POSSIBLE_PHYSMEM_BITS MAX_PHYSMEM_BITS 9062306a36Sopenharmony_ci#else 9162306a36Sopenharmony_ci/* 9262306a36Sopenharmony_ci * If this definition of MAX_PHYSMEM_BITS is used, OBJ_INDEX_BITS will just 9362306a36Sopenharmony_ci * be PAGE_SHIFT 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci#define MAX_POSSIBLE_PHYSMEM_BITS BITS_PER_LONG 9662306a36Sopenharmony_ci#endif 9762306a36Sopenharmony_ci#endif 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#define _PFN_BITS (MAX_POSSIBLE_PHYSMEM_BITS - PAGE_SHIFT) 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* 10262306a36Sopenharmony_ci * Head in allocated object should have OBJ_ALLOCATED_TAG 10362306a36Sopenharmony_ci * to identify the object was allocated or not. 10462306a36Sopenharmony_ci * It's okay to add the status bit in the least bit because 10562306a36Sopenharmony_ci * header keeps handle which is 4byte-aligned address so we 10662306a36Sopenharmony_ci * have room for two bit at least. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci#define OBJ_ALLOCATED_TAG 1 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci#define OBJ_TAG_BITS 1 11162306a36Sopenharmony_ci#define OBJ_TAG_MASK OBJ_ALLOCATED_TAG 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#define OBJ_INDEX_BITS (BITS_PER_LONG - _PFN_BITS - OBJ_TAG_BITS) 11462306a36Sopenharmony_ci#define OBJ_INDEX_MASK ((_AC(1, UL) << OBJ_INDEX_BITS) - 1) 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#define HUGE_BITS 1 11762306a36Sopenharmony_ci#define FULLNESS_BITS 4 11862306a36Sopenharmony_ci#define CLASS_BITS 8 11962306a36Sopenharmony_ci#define ISOLATED_BITS 5 12062306a36Sopenharmony_ci#define MAGIC_VAL_BITS 8 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#define MAX(a, b) ((a) >= (b) ? (a) : (b)) 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci#define ZS_MAX_PAGES_PER_ZSPAGE (_AC(CONFIG_ZSMALLOC_CHAIN_SIZE, UL)) 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* ZS_MIN_ALLOC_SIZE must be multiple of ZS_ALIGN */ 12762306a36Sopenharmony_ci#define ZS_MIN_ALLOC_SIZE \ 12862306a36Sopenharmony_ci MAX(32, (ZS_MAX_PAGES_PER_ZSPAGE << PAGE_SHIFT >> OBJ_INDEX_BITS)) 12962306a36Sopenharmony_ci/* each chunk includes extra space to keep handle */ 13062306a36Sopenharmony_ci#define ZS_MAX_ALLOC_SIZE PAGE_SIZE 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* 13362306a36Sopenharmony_ci * On systems with 4K page size, this gives 255 size classes! There is a 13462306a36Sopenharmony_ci * trader-off here: 13562306a36Sopenharmony_ci * - Large number of size classes is potentially wasteful as free page are 13662306a36Sopenharmony_ci * spread across these classes 13762306a36Sopenharmony_ci * - Small number of size classes causes large internal fragmentation 13862306a36Sopenharmony_ci * - Probably its better to use specific size classes (empirically 13962306a36Sopenharmony_ci * determined). NOTE: all those class sizes must be set as multiple of 14062306a36Sopenharmony_ci * ZS_ALIGN to make sure link_free itself never has to span 2 pages. 14162306a36Sopenharmony_ci * 14262306a36Sopenharmony_ci * ZS_MIN_ALLOC_SIZE and ZS_SIZE_CLASS_DELTA must be multiple of ZS_ALIGN 14362306a36Sopenharmony_ci * (reason above) 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci#define ZS_SIZE_CLASS_DELTA (PAGE_SIZE >> CLASS_BITS) 14662306a36Sopenharmony_ci#define ZS_SIZE_CLASSES (DIV_ROUND_UP(ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE, \ 14762306a36Sopenharmony_ci ZS_SIZE_CLASS_DELTA) + 1) 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* 15062306a36Sopenharmony_ci * Pages are distinguished by the ratio of used memory (that is the ratio 15162306a36Sopenharmony_ci * of ->inuse objects to all objects that page can store). For example, 15262306a36Sopenharmony_ci * INUSE_RATIO_10 means that the ratio of used objects is > 0% and <= 10%. 15362306a36Sopenharmony_ci * 15462306a36Sopenharmony_ci * The number of fullness groups is not random. It allows us to keep 15562306a36Sopenharmony_ci * difference between the least busy page in the group (minimum permitted 15662306a36Sopenharmony_ci * number of ->inuse objects) and the most busy page (maximum permitted 15762306a36Sopenharmony_ci * number of ->inuse objects) at a reasonable value. 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_cienum fullness_group { 16062306a36Sopenharmony_ci ZS_INUSE_RATIO_0, 16162306a36Sopenharmony_ci ZS_INUSE_RATIO_10, 16262306a36Sopenharmony_ci /* NOTE: 8 more fullness groups here */ 16362306a36Sopenharmony_ci ZS_INUSE_RATIO_99 = 10, 16462306a36Sopenharmony_ci ZS_INUSE_RATIO_100, 16562306a36Sopenharmony_ci NR_FULLNESS_GROUPS, 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cienum class_stat_type { 16962306a36Sopenharmony_ci /* NOTE: stats for 12 fullness groups here: from inuse 0 to 100 */ 17062306a36Sopenharmony_ci ZS_OBJS_ALLOCATED = NR_FULLNESS_GROUPS, 17162306a36Sopenharmony_ci ZS_OBJS_INUSE, 17262306a36Sopenharmony_ci NR_CLASS_STAT_TYPES, 17362306a36Sopenharmony_ci}; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistruct zs_size_stat { 17662306a36Sopenharmony_ci unsigned long objs[NR_CLASS_STAT_TYPES]; 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci#ifdef CONFIG_ZSMALLOC_STAT 18062306a36Sopenharmony_cistatic struct dentry *zs_stat_root; 18162306a36Sopenharmony_ci#endif 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic size_t huge_class_size; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistruct size_class { 18662306a36Sopenharmony_ci struct list_head fullness_list[NR_FULLNESS_GROUPS]; 18762306a36Sopenharmony_ci /* 18862306a36Sopenharmony_ci * Size of objects stored in this class. Must be multiple 18962306a36Sopenharmony_ci * of ZS_ALIGN. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_ci int size; 19262306a36Sopenharmony_ci int objs_per_zspage; 19362306a36Sopenharmony_ci /* Number of PAGE_SIZE sized pages to combine to form a 'zspage' */ 19462306a36Sopenharmony_ci int pages_per_zspage; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci unsigned int index; 19762306a36Sopenharmony_ci struct zs_size_stat stats; 19862306a36Sopenharmony_ci}; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci/* 20162306a36Sopenharmony_ci * Placed within free objects to form a singly linked list. 20262306a36Sopenharmony_ci * For every zspage, zspage->freeobj gives head of this list. 20362306a36Sopenharmony_ci * 20462306a36Sopenharmony_ci * This must be power of 2 and less than or equal to ZS_ALIGN 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_cistruct link_free { 20762306a36Sopenharmony_ci union { 20862306a36Sopenharmony_ci /* 20962306a36Sopenharmony_ci * Free object index; 21062306a36Sopenharmony_ci * It's valid for non-allocated object 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci unsigned long next; 21362306a36Sopenharmony_ci /* 21462306a36Sopenharmony_ci * Handle of allocated object. 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_ci unsigned long handle; 21762306a36Sopenharmony_ci }; 21862306a36Sopenharmony_ci}; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistruct zs_pool { 22162306a36Sopenharmony_ci const char *name; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci struct size_class *size_class[ZS_SIZE_CLASSES]; 22462306a36Sopenharmony_ci struct kmem_cache *handle_cachep; 22562306a36Sopenharmony_ci struct kmem_cache *zspage_cachep; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci atomic_long_t pages_allocated; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci struct zs_pool_stats stats; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* Compact classes */ 23262306a36Sopenharmony_ci struct shrinker shrinker; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci#ifdef CONFIG_ZSMALLOC_STAT 23562306a36Sopenharmony_ci struct dentry *stat_dentry; 23662306a36Sopenharmony_ci#endif 23762306a36Sopenharmony_ci#ifdef CONFIG_COMPACTION 23862306a36Sopenharmony_ci struct work_struct free_work; 23962306a36Sopenharmony_ci#endif 24062306a36Sopenharmony_ci spinlock_t lock; 24162306a36Sopenharmony_ci atomic_t compaction_in_progress; 24262306a36Sopenharmony_ci}; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistruct zspage { 24562306a36Sopenharmony_ci struct { 24662306a36Sopenharmony_ci unsigned int huge:HUGE_BITS; 24762306a36Sopenharmony_ci unsigned int fullness:FULLNESS_BITS; 24862306a36Sopenharmony_ci unsigned int class:CLASS_BITS + 1; 24962306a36Sopenharmony_ci unsigned int isolated:ISOLATED_BITS; 25062306a36Sopenharmony_ci unsigned int magic:MAGIC_VAL_BITS; 25162306a36Sopenharmony_ci }; 25262306a36Sopenharmony_ci unsigned int inuse; 25362306a36Sopenharmony_ci unsigned int freeobj; 25462306a36Sopenharmony_ci struct page *first_page; 25562306a36Sopenharmony_ci struct list_head list; /* fullness list */ 25662306a36Sopenharmony_ci struct zs_pool *pool; 25762306a36Sopenharmony_ci rwlock_t lock; 25862306a36Sopenharmony_ci}; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistruct mapping_area { 26162306a36Sopenharmony_ci local_lock_t lock; 26262306a36Sopenharmony_ci char *vm_buf; /* copy buffer for objects that span pages */ 26362306a36Sopenharmony_ci char *vm_addr; /* address of kmap_atomic()'ed pages */ 26462306a36Sopenharmony_ci enum zs_mapmode vm_mm; /* mapping mode */ 26562306a36Sopenharmony_ci}; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/* huge object: pages_per_zspage == 1 && maxobj_per_zspage == 1 */ 26862306a36Sopenharmony_cistatic void SetZsHugePage(struct zspage *zspage) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci zspage->huge = 1; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic bool ZsHugePage(struct zspage *zspage) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci return zspage->huge; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic void migrate_lock_init(struct zspage *zspage); 27962306a36Sopenharmony_cistatic void migrate_read_lock(struct zspage *zspage); 28062306a36Sopenharmony_cistatic void migrate_read_unlock(struct zspage *zspage); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci#ifdef CONFIG_COMPACTION 28362306a36Sopenharmony_cistatic void migrate_write_lock(struct zspage *zspage); 28462306a36Sopenharmony_cistatic void migrate_write_lock_nested(struct zspage *zspage); 28562306a36Sopenharmony_cistatic void migrate_write_unlock(struct zspage *zspage); 28662306a36Sopenharmony_cistatic void kick_deferred_free(struct zs_pool *pool); 28762306a36Sopenharmony_cistatic void init_deferred_free(struct zs_pool *pool); 28862306a36Sopenharmony_cistatic void SetZsPageMovable(struct zs_pool *pool, struct zspage *zspage); 28962306a36Sopenharmony_ci#else 29062306a36Sopenharmony_cistatic void migrate_write_lock(struct zspage *zspage) {} 29162306a36Sopenharmony_cistatic void migrate_write_lock_nested(struct zspage *zspage) {} 29262306a36Sopenharmony_cistatic void migrate_write_unlock(struct zspage *zspage) {} 29362306a36Sopenharmony_cistatic void kick_deferred_free(struct zs_pool *pool) {} 29462306a36Sopenharmony_cistatic void init_deferred_free(struct zs_pool *pool) {} 29562306a36Sopenharmony_cistatic void SetZsPageMovable(struct zs_pool *pool, struct zspage *zspage) {} 29662306a36Sopenharmony_ci#endif 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic int create_cache(struct zs_pool *pool) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci pool->handle_cachep = kmem_cache_create("zs_handle", ZS_HANDLE_SIZE, 30162306a36Sopenharmony_ci 0, 0, NULL); 30262306a36Sopenharmony_ci if (!pool->handle_cachep) 30362306a36Sopenharmony_ci return 1; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci pool->zspage_cachep = kmem_cache_create("zspage", sizeof(struct zspage), 30662306a36Sopenharmony_ci 0, 0, NULL); 30762306a36Sopenharmony_ci if (!pool->zspage_cachep) { 30862306a36Sopenharmony_ci kmem_cache_destroy(pool->handle_cachep); 30962306a36Sopenharmony_ci pool->handle_cachep = NULL; 31062306a36Sopenharmony_ci return 1; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return 0; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic void destroy_cache(struct zs_pool *pool) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci kmem_cache_destroy(pool->handle_cachep); 31962306a36Sopenharmony_ci kmem_cache_destroy(pool->zspage_cachep); 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic unsigned long cache_alloc_handle(struct zs_pool *pool, gfp_t gfp) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci return (unsigned long)kmem_cache_alloc(pool->handle_cachep, 32562306a36Sopenharmony_ci gfp & ~(__GFP_HIGHMEM|__GFP_MOVABLE)); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic void cache_free_handle(struct zs_pool *pool, unsigned long handle) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci kmem_cache_free(pool->handle_cachep, (void *)handle); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic struct zspage *cache_alloc_zspage(struct zs_pool *pool, gfp_t flags) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci return kmem_cache_zalloc(pool->zspage_cachep, 33662306a36Sopenharmony_ci flags & ~(__GFP_HIGHMEM|__GFP_MOVABLE)); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic void cache_free_zspage(struct zs_pool *pool, struct zspage *zspage) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci kmem_cache_free(pool->zspage_cachep, zspage); 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci/* pool->lock(which owns the handle) synchronizes races */ 34562306a36Sopenharmony_cistatic void record_obj(unsigned long handle, unsigned long obj) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci *(unsigned long *)handle = obj; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci/* zpool driver */ 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci#ifdef CONFIG_ZPOOL 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic void *zs_zpool_create(const char *name, gfp_t gfp) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci /* 35762306a36Sopenharmony_ci * Ignore global gfp flags: zs_malloc() may be invoked from 35862306a36Sopenharmony_ci * different contexts and its caller must provide a valid 35962306a36Sopenharmony_ci * gfp mask. 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_ci return zs_create_pool(name); 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic void zs_zpool_destroy(void *pool) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci zs_destroy_pool(pool); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic int zs_zpool_malloc(void *pool, size_t size, gfp_t gfp, 37062306a36Sopenharmony_ci unsigned long *handle) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci *handle = zs_malloc(pool, size, gfp); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (IS_ERR_VALUE(*handle)) 37562306a36Sopenharmony_ci return PTR_ERR((void *)*handle); 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_cistatic void zs_zpool_free(void *pool, unsigned long handle) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci zs_free(pool, handle); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic void *zs_zpool_map(void *pool, unsigned long handle, 38462306a36Sopenharmony_ci enum zpool_mapmode mm) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci enum zs_mapmode zs_mm; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci switch (mm) { 38962306a36Sopenharmony_ci case ZPOOL_MM_RO: 39062306a36Sopenharmony_ci zs_mm = ZS_MM_RO; 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci case ZPOOL_MM_WO: 39362306a36Sopenharmony_ci zs_mm = ZS_MM_WO; 39462306a36Sopenharmony_ci break; 39562306a36Sopenharmony_ci case ZPOOL_MM_RW: 39662306a36Sopenharmony_ci default: 39762306a36Sopenharmony_ci zs_mm = ZS_MM_RW; 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci return zs_map_object(pool, handle, zs_mm); 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_cistatic void zs_zpool_unmap(void *pool, unsigned long handle) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci zs_unmap_object(pool, handle); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic u64 zs_zpool_total_size(void *pool) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci return zs_get_total_pages(pool) << PAGE_SHIFT; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic struct zpool_driver zs_zpool_driver = { 41462306a36Sopenharmony_ci .type = "zsmalloc", 41562306a36Sopenharmony_ci .owner = THIS_MODULE, 41662306a36Sopenharmony_ci .create = zs_zpool_create, 41762306a36Sopenharmony_ci .destroy = zs_zpool_destroy, 41862306a36Sopenharmony_ci .malloc_support_movable = true, 41962306a36Sopenharmony_ci .malloc = zs_zpool_malloc, 42062306a36Sopenharmony_ci .free = zs_zpool_free, 42162306a36Sopenharmony_ci .map = zs_zpool_map, 42262306a36Sopenharmony_ci .unmap = zs_zpool_unmap, 42362306a36Sopenharmony_ci .total_size = zs_zpool_total_size, 42462306a36Sopenharmony_ci}; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ciMODULE_ALIAS("zpool-zsmalloc"); 42762306a36Sopenharmony_ci#endif /* CONFIG_ZPOOL */ 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci/* per-cpu VM mapping areas for zspage accesses that cross page boundaries */ 43062306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct mapping_area, zs_map_area) = { 43162306a36Sopenharmony_ci .lock = INIT_LOCAL_LOCK(lock), 43262306a36Sopenharmony_ci}; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic __maybe_unused int is_first_page(struct page *page) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci return PagePrivate(page); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci/* Protected by pool->lock */ 44062306a36Sopenharmony_cistatic inline int get_zspage_inuse(struct zspage *zspage) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci return zspage->inuse; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic inline void mod_zspage_inuse(struct zspage *zspage, int val) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci zspage->inuse += val; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic inline struct page *get_first_page(struct zspage *zspage) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct page *first_page = zspage->first_page; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci VM_BUG_ON_PAGE(!is_first_page(first_page), first_page); 45662306a36Sopenharmony_ci return first_page; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic inline unsigned int get_first_obj_offset(struct page *page) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci return page->page_type; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic inline void set_first_obj_offset(struct page *page, unsigned int offset) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci page->page_type = offset; 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic inline unsigned int get_freeobj(struct zspage *zspage) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci return zspage->freeobj; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic inline void set_freeobj(struct zspage *zspage, unsigned int obj) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci zspage->freeobj = obj; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic void get_zspage_mapping(struct zspage *zspage, 48062306a36Sopenharmony_ci unsigned int *class_idx, 48162306a36Sopenharmony_ci int *fullness) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci BUG_ON(zspage->magic != ZSPAGE_MAGIC); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci *fullness = zspage->fullness; 48662306a36Sopenharmony_ci *class_idx = zspage->class; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic struct size_class *zspage_class(struct zs_pool *pool, 49062306a36Sopenharmony_ci struct zspage *zspage) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci return pool->size_class[zspage->class]; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic void set_zspage_mapping(struct zspage *zspage, 49662306a36Sopenharmony_ci unsigned int class_idx, 49762306a36Sopenharmony_ci int fullness) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci zspage->class = class_idx; 50062306a36Sopenharmony_ci zspage->fullness = fullness; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci/* 50462306a36Sopenharmony_ci * zsmalloc divides the pool into various size classes where each 50562306a36Sopenharmony_ci * class maintains a list of zspages where each zspage is divided 50662306a36Sopenharmony_ci * into equal sized chunks. Each allocation falls into one of these 50762306a36Sopenharmony_ci * classes depending on its size. This function returns index of the 50862306a36Sopenharmony_ci * size class which has chunk size big enough to hold the given size. 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_cistatic int get_size_class_index(int size) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci int idx = 0; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (likely(size > ZS_MIN_ALLOC_SIZE)) 51562306a36Sopenharmony_ci idx = DIV_ROUND_UP(size - ZS_MIN_ALLOC_SIZE, 51662306a36Sopenharmony_ci ZS_SIZE_CLASS_DELTA); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci return min_t(int, ZS_SIZE_CLASSES - 1, idx); 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic inline void class_stat_inc(struct size_class *class, 52262306a36Sopenharmony_ci int type, unsigned long cnt) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci class->stats.objs[type] += cnt; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic inline void class_stat_dec(struct size_class *class, 52862306a36Sopenharmony_ci int type, unsigned long cnt) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci class->stats.objs[type] -= cnt; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic inline unsigned long zs_stat_get(struct size_class *class, int type) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci return class->stats.objs[type]; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci#ifdef CONFIG_ZSMALLOC_STAT 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic void __init zs_stat_init(void) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci if (!debugfs_initialized()) { 54362306a36Sopenharmony_ci pr_warn("debugfs not available, stat dir not created\n"); 54462306a36Sopenharmony_ci return; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci zs_stat_root = debugfs_create_dir("zsmalloc", NULL); 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic void __exit zs_stat_exit(void) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci debugfs_remove_recursive(zs_stat_root); 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic unsigned long zs_can_compact(struct size_class *class); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic int zs_stats_size_show(struct seq_file *s, void *v) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci int i, fg; 56062306a36Sopenharmony_ci struct zs_pool *pool = s->private; 56162306a36Sopenharmony_ci struct size_class *class; 56262306a36Sopenharmony_ci int objs_per_zspage; 56362306a36Sopenharmony_ci unsigned long obj_allocated, obj_used, pages_used, freeable; 56462306a36Sopenharmony_ci unsigned long total_objs = 0, total_used_objs = 0, total_pages = 0; 56562306a36Sopenharmony_ci unsigned long total_freeable = 0; 56662306a36Sopenharmony_ci unsigned long inuse_totals[NR_FULLNESS_GROUPS] = {0, }; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci seq_printf(s, " %5s %5s %9s %9s %9s %9s %9s %9s %9s %9s %9s %9s %9s %13s %10s %10s %16s %8s\n", 56962306a36Sopenharmony_ci "class", "size", "10%", "20%", "30%", "40%", 57062306a36Sopenharmony_ci "50%", "60%", "70%", "80%", "90%", "99%", "100%", 57162306a36Sopenharmony_ci "obj_allocated", "obj_used", "pages_used", 57262306a36Sopenharmony_ci "pages_per_zspage", "freeable"); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci for (i = 0; i < ZS_SIZE_CLASSES; i++) { 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci class = pool->size_class[i]; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (class->index != i) 57962306a36Sopenharmony_ci continue; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci spin_lock(&pool->lock); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci seq_printf(s, " %5u %5u ", i, class->size); 58462306a36Sopenharmony_ci for (fg = ZS_INUSE_RATIO_10; fg < NR_FULLNESS_GROUPS; fg++) { 58562306a36Sopenharmony_ci inuse_totals[fg] += zs_stat_get(class, fg); 58662306a36Sopenharmony_ci seq_printf(s, "%9lu ", zs_stat_get(class, fg)); 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci obj_allocated = zs_stat_get(class, ZS_OBJS_ALLOCATED); 59062306a36Sopenharmony_ci obj_used = zs_stat_get(class, ZS_OBJS_INUSE); 59162306a36Sopenharmony_ci freeable = zs_can_compact(class); 59262306a36Sopenharmony_ci spin_unlock(&pool->lock); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci objs_per_zspage = class->objs_per_zspage; 59562306a36Sopenharmony_ci pages_used = obj_allocated / objs_per_zspage * 59662306a36Sopenharmony_ci class->pages_per_zspage; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci seq_printf(s, "%13lu %10lu %10lu %16d %8lu\n", 59962306a36Sopenharmony_ci obj_allocated, obj_used, pages_used, 60062306a36Sopenharmony_ci class->pages_per_zspage, freeable); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci total_objs += obj_allocated; 60362306a36Sopenharmony_ci total_used_objs += obj_used; 60462306a36Sopenharmony_ci total_pages += pages_used; 60562306a36Sopenharmony_ci total_freeable += freeable; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci seq_puts(s, "\n"); 60962306a36Sopenharmony_ci seq_printf(s, " %5s %5s ", "Total", ""); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci for (fg = ZS_INUSE_RATIO_10; fg < NR_FULLNESS_GROUPS; fg++) 61262306a36Sopenharmony_ci seq_printf(s, "%9lu ", inuse_totals[fg]); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci seq_printf(s, "%13lu %10lu %10lu %16s %8lu\n", 61562306a36Sopenharmony_ci total_objs, total_used_objs, total_pages, "", 61662306a36Sopenharmony_ci total_freeable); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci return 0; 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(zs_stats_size); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic void zs_pool_stat_create(struct zs_pool *pool, const char *name) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci if (!zs_stat_root) { 62562306a36Sopenharmony_ci pr_warn("no root stat dir, not creating <%s> stat dir\n", name); 62662306a36Sopenharmony_ci return; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci pool->stat_dentry = debugfs_create_dir(name, zs_stat_root); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci debugfs_create_file("classes", S_IFREG | 0444, pool->stat_dentry, pool, 63262306a36Sopenharmony_ci &zs_stats_size_fops); 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic void zs_pool_stat_destroy(struct zs_pool *pool) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci debugfs_remove_recursive(pool->stat_dentry); 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci#else /* CONFIG_ZSMALLOC_STAT */ 64162306a36Sopenharmony_cistatic void __init zs_stat_init(void) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic void __exit zs_stat_exit(void) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic inline void zs_pool_stat_create(struct zs_pool *pool, const char *name) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic inline void zs_pool_stat_destroy(struct zs_pool *pool) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci#endif 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci/* 66062306a36Sopenharmony_ci * For each size class, zspages are divided into different groups 66162306a36Sopenharmony_ci * depending on their usage ratio. This function returns fullness 66262306a36Sopenharmony_ci * status of the given page. 66362306a36Sopenharmony_ci */ 66462306a36Sopenharmony_cistatic int get_fullness_group(struct size_class *class, struct zspage *zspage) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci int inuse, objs_per_zspage, ratio; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci inuse = get_zspage_inuse(zspage); 66962306a36Sopenharmony_ci objs_per_zspage = class->objs_per_zspage; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (inuse == 0) 67262306a36Sopenharmony_ci return ZS_INUSE_RATIO_0; 67362306a36Sopenharmony_ci if (inuse == objs_per_zspage) 67462306a36Sopenharmony_ci return ZS_INUSE_RATIO_100; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci ratio = 100 * inuse / objs_per_zspage; 67762306a36Sopenharmony_ci /* 67862306a36Sopenharmony_ci * Take integer division into consideration: a page with one inuse 67962306a36Sopenharmony_ci * object out of 127 possible, will end up having 0 usage ratio, 68062306a36Sopenharmony_ci * which is wrong as it belongs in ZS_INUSE_RATIO_10 fullness group. 68162306a36Sopenharmony_ci */ 68262306a36Sopenharmony_ci return ratio / 10 + 1; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci/* 68662306a36Sopenharmony_ci * Each size class maintains various freelists and zspages are assigned 68762306a36Sopenharmony_ci * to one of these freelists based on the number of live objects they 68862306a36Sopenharmony_ci * have. This functions inserts the given zspage into the freelist 68962306a36Sopenharmony_ci * identified by <class, fullness_group>. 69062306a36Sopenharmony_ci */ 69162306a36Sopenharmony_cistatic void insert_zspage(struct size_class *class, 69262306a36Sopenharmony_ci struct zspage *zspage, 69362306a36Sopenharmony_ci int fullness) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci class_stat_inc(class, fullness, 1); 69662306a36Sopenharmony_ci list_add(&zspage->list, &class->fullness_list[fullness]); 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci/* 70062306a36Sopenharmony_ci * This function removes the given zspage from the freelist identified 70162306a36Sopenharmony_ci * by <class, fullness_group>. 70262306a36Sopenharmony_ci */ 70362306a36Sopenharmony_cistatic void remove_zspage(struct size_class *class, 70462306a36Sopenharmony_ci struct zspage *zspage, 70562306a36Sopenharmony_ci int fullness) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci VM_BUG_ON(list_empty(&class->fullness_list[fullness])); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci list_del_init(&zspage->list); 71062306a36Sopenharmony_ci class_stat_dec(class, fullness, 1); 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci/* 71462306a36Sopenharmony_ci * Each size class maintains zspages in different fullness groups depending 71562306a36Sopenharmony_ci * on the number of live objects they contain. When allocating or freeing 71662306a36Sopenharmony_ci * objects, the fullness status of the page can change, for instance, from 71762306a36Sopenharmony_ci * INUSE_RATIO_80 to INUSE_RATIO_70 when freeing an object. This function 71862306a36Sopenharmony_ci * checks if such a status change has occurred for the given page and 71962306a36Sopenharmony_ci * accordingly moves the page from the list of the old fullness group to that 72062306a36Sopenharmony_ci * of the new fullness group. 72162306a36Sopenharmony_ci */ 72262306a36Sopenharmony_cistatic int fix_fullness_group(struct size_class *class, struct zspage *zspage) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci int class_idx; 72562306a36Sopenharmony_ci int currfg, newfg; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci get_zspage_mapping(zspage, &class_idx, &currfg); 72862306a36Sopenharmony_ci newfg = get_fullness_group(class, zspage); 72962306a36Sopenharmony_ci if (newfg == currfg) 73062306a36Sopenharmony_ci goto out; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci remove_zspage(class, zspage, currfg); 73362306a36Sopenharmony_ci insert_zspage(class, zspage, newfg); 73462306a36Sopenharmony_ci set_zspage_mapping(zspage, class_idx, newfg); 73562306a36Sopenharmony_ciout: 73662306a36Sopenharmony_ci return newfg; 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic struct zspage *get_zspage(struct page *page) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci struct zspage *zspage = (struct zspage *)page_private(page); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci BUG_ON(zspage->magic != ZSPAGE_MAGIC); 74462306a36Sopenharmony_ci return zspage; 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_cistatic struct page *get_next_page(struct page *page) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci struct zspage *zspage = get_zspage(page); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci if (unlikely(ZsHugePage(zspage))) 75262306a36Sopenharmony_ci return NULL; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci return (struct page *)page->index; 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci/** 75862306a36Sopenharmony_ci * obj_to_location - get (<page>, <obj_idx>) from encoded object value 75962306a36Sopenharmony_ci * @obj: the encoded object value 76062306a36Sopenharmony_ci * @page: page object resides in zspage 76162306a36Sopenharmony_ci * @obj_idx: object index 76262306a36Sopenharmony_ci */ 76362306a36Sopenharmony_cistatic void obj_to_location(unsigned long obj, struct page **page, 76462306a36Sopenharmony_ci unsigned int *obj_idx) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci obj >>= OBJ_TAG_BITS; 76762306a36Sopenharmony_ci *page = pfn_to_page(obj >> OBJ_INDEX_BITS); 76862306a36Sopenharmony_ci *obj_idx = (obj & OBJ_INDEX_MASK); 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cistatic void obj_to_page(unsigned long obj, struct page **page) 77262306a36Sopenharmony_ci{ 77362306a36Sopenharmony_ci obj >>= OBJ_TAG_BITS; 77462306a36Sopenharmony_ci *page = pfn_to_page(obj >> OBJ_INDEX_BITS); 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci/** 77862306a36Sopenharmony_ci * location_to_obj - get obj value encoded from (<page>, <obj_idx>) 77962306a36Sopenharmony_ci * @page: page object resides in zspage 78062306a36Sopenharmony_ci * @obj_idx: object index 78162306a36Sopenharmony_ci */ 78262306a36Sopenharmony_cistatic unsigned long location_to_obj(struct page *page, unsigned int obj_idx) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci unsigned long obj; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci obj = page_to_pfn(page) << OBJ_INDEX_BITS; 78762306a36Sopenharmony_ci obj |= obj_idx & OBJ_INDEX_MASK; 78862306a36Sopenharmony_ci obj <<= OBJ_TAG_BITS; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci return obj; 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic unsigned long handle_to_obj(unsigned long handle) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci return *(unsigned long *)handle; 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_cistatic inline bool obj_allocated(struct page *page, void *obj, 79962306a36Sopenharmony_ci unsigned long *phandle) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci unsigned long handle; 80262306a36Sopenharmony_ci struct zspage *zspage = get_zspage(page); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci if (unlikely(ZsHugePage(zspage))) { 80562306a36Sopenharmony_ci VM_BUG_ON_PAGE(!is_first_page(page), page); 80662306a36Sopenharmony_ci handle = page->index; 80762306a36Sopenharmony_ci } else 80862306a36Sopenharmony_ci handle = *(unsigned long *)obj; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (!(handle & OBJ_ALLOCATED_TAG)) 81162306a36Sopenharmony_ci return false; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci /* Clear all tags before returning the handle */ 81462306a36Sopenharmony_ci *phandle = handle & ~OBJ_TAG_MASK; 81562306a36Sopenharmony_ci return true; 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cistatic void reset_page(struct page *page) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci __ClearPageMovable(page); 82162306a36Sopenharmony_ci ClearPagePrivate(page); 82262306a36Sopenharmony_ci set_page_private(page, 0); 82362306a36Sopenharmony_ci page_mapcount_reset(page); 82462306a36Sopenharmony_ci page->index = 0; 82562306a36Sopenharmony_ci} 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_cistatic int trylock_zspage(struct zspage *zspage) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci struct page *cursor, *fail; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci for (cursor = get_first_page(zspage); cursor != NULL; cursor = 83262306a36Sopenharmony_ci get_next_page(cursor)) { 83362306a36Sopenharmony_ci if (!trylock_page(cursor)) { 83462306a36Sopenharmony_ci fail = cursor; 83562306a36Sopenharmony_ci goto unlock; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci return 1; 84062306a36Sopenharmony_ciunlock: 84162306a36Sopenharmony_ci for (cursor = get_first_page(zspage); cursor != fail; cursor = 84262306a36Sopenharmony_ci get_next_page(cursor)) 84362306a36Sopenharmony_ci unlock_page(cursor); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci return 0; 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_cistatic void __free_zspage(struct zs_pool *pool, struct size_class *class, 84962306a36Sopenharmony_ci struct zspage *zspage) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci struct page *page, *next; 85262306a36Sopenharmony_ci int fg; 85362306a36Sopenharmony_ci unsigned int class_idx; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci get_zspage_mapping(zspage, &class_idx, &fg); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci assert_spin_locked(&pool->lock); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci VM_BUG_ON(get_zspage_inuse(zspage)); 86062306a36Sopenharmony_ci VM_BUG_ON(fg != ZS_INUSE_RATIO_0); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci next = page = get_first_page(zspage); 86362306a36Sopenharmony_ci do { 86462306a36Sopenharmony_ci VM_BUG_ON_PAGE(!PageLocked(page), page); 86562306a36Sopenharmony_ci next = get_next_page(page); 86662306a36Sopenharmony_ci reset_page(page); 86762306a36Sopenharmony_ci unlock_page(page); 86862306a36Sopenharmony_ci dec_zone_page_state(page, NR_ZSPAGES); 86962306a36Sopenharmony_ci put_page(page); 87062306a36Sopenharmony_ci page = next; 87162306a36Sopenharmony_ci } while (page != NULL); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci cache_free_zspage(pool, zspage); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci class_stat_dec(class, ZS_OBJS_ALLOCATED, class->objs_per_zspage); 87662306a36Sopenharmony_ci atomic_long_sub(class->pages_per_zspage, &pool->pages_allocated); 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistatic void free_zspage(struct zs_pool *pool, struct size_class *class, 88062306a36Sopenharmony_ci struct zspage *zspage) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci VM_BUG_ON(get_zspage_inuse(zspage)); 88362306a36Sopenharmony_ci VM_BUG_ON(list_empty(&zspage->list)); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci /* 88662306a36Sopenharmony_ci * Since zs_free couldn't be sleepable, this function cannot call 88762306a36Sopenharmony_ci * lock_page. The page locks trylock_zspage got will be released 88862306a36Sopenharmony_ci * by __free_zspage. 88962306a36Sopenharmony_ci */ 89062306a36Sopenharmony_ci if (!trylock_zspage(zspage)) { 89162306a36Sopenharmony_ci kick_deferred_free(pool); 89262306a36Sopenharmony_ci return; 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci remove_zspage(class, zspage, ZS_INUSE_RATIO_0); 89662306a36Sopenharmony_ci __free_zspage(pool, class, zspage); 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci/* Initialize a newly allocated zspage */ 90062306a36Sopenharmony_cistatic void init_zspage(struct size_class *class, struct zspage *zspage) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci unsigned int freeobj = 1; 90362306a36Sopenharmony_ci unsigned long off = 0; 90462306a36Sopenharmony_ci struct page *page = get_first_page(zspage); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci while (page) { 90762306a36Sopenharmony_ci struct page *next_page; 90862306a36Sopenharmony_ci struct link_free *link; 90962306a36Sopenharmony_ci void *vaddr; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci set_first_obj_offset(page, off); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci vaddr = kmap_atomic(page); 91462306a36Sopenharmony_ci link = (struct link_free *)vaddr + off / sizeof(*link); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci while ((off += class->size) < PAGE_SIZE) { 91762306a36Sopenharmony_ci link->next = freeobj++ << OBJ_TAG_BITS; 91862306a36Sopenharmony_ci link += class->size / sizeof(*link); 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci /* 92262306a36Sopenharmony_ci * We now come to the last (full or partial) object on this 92362306a36Sopenharmony_ci * page, which must point to the first object on the next 92462306a36Sopenharmony_ci * page (if present) 92562306a36Sopenharmony_ci */ 92662306a36Sopenharmony_ci next_page = get_next_page(page); 92762306a36Sopenharmony_ci if (next_page) { 92862306a36Sopenharmony_ci link->next = freeobj++ << OBJ_TAG_BITS; 92962306a36Sopenharmony_ci } else { 93062306a36Sopenharmony_ci /* 93162306a36Sopenharmony_ci * Reset OBJ_TAG_BITS bit to last link to tell 93262306a36Sopenharmony_ci * whether it's allocated object or not. 93362306a36Sopenharmony_ci */ 93462306a36Sopenharmony_ci link->next = -1UL << OBJ_TAG_BITS; 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci kunmap_atomic(vaddr); 93762306a36Sopenharmony_ci page = next_page; 93862306a36Sopenharmony_ci off %= PAGE_SIZE; 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci set_freeobj(zspage, 0); 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_cistatic void create_page_chain(struct size_class *class, struct zspage *zspage, 94562306a36Sopenharmony_ci struct page *pages[]) 94662306a36Sopenharmony_ci{ 94762306a36Sopenharmony_ci int i; 94862306a36Sopenharmony_ci struct page *page; 94962306a36Sopenharmony_ci struct page *prev_page = NULL; 95062306a36Sopenharmony_ci int nr_pages = class->pages_per_zspage; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci /* 95362306a36Sopenharmony_ci * Allocate individual pages and link them together as: 95462306a36Sopenharmony_ci * 1. all pages are linked together using page->index 95562306a36Sopenharmony_ci * 2. each sub-page point to zspage using page->private 95662306a36Sopenharmony_ci * 95762306a36Sopenharmony_ci * we set PG_private to identify the first page (i.e. no other sub-page 95862306a36Sopenharmony_ci * has this flag set). 95962306a36Sopenharmony_ci */ 96062306a36Sopenharmony_ci for (i = 0; i < nr_pages; i++) { 96162306a36Sopenharmony_ci page = pages[i]; 96262306a36Sopenharmony_ci set_page_private(page, (unsigned long)zspage); 96362306a36Sopenharmony_ci page->index = 0; 96462306a36Sopenharmony_ci if (i == 0) { 96562306a36Sopenharmony_ci zspage->first_page = page; 96662306a36Sopenharmony_ci SetPagePrivate(page); 96762306a36Sopenharmony_ci if (unlikely(class->objs_per_zspage == 1 && 96862306a36Sopenharmony_ci class->pages_per_zspage == 1)) 96962306a36Sopenharmony_ci SetZsHugePage(zspage); 97062306a36Sopenharmony_ci } else { 97162306a36Sopenharmony_ci prev_page->index = (unsigned long)page; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci prev_page = page; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci/* 97862306a36Sopenharmony_ci * Allocate a zspage for the given size class 97962306a36Sopenharmony_ci */ 98062306a36Sopenharmony_cistatic struct zspage *alloc_zspage(struct zs_pool *pool, 98162306a36Sopenharmony_ci struct size_class *class, 98262306a36Sopenharmony_ci gfp_t gfp) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci int i; 98562306a36Sopenharmony_ci struct page *pages[ZS_MAX_PAGES_PER_ZSPAGE]; 98662306a36Sopenharmony_ci struct zspage *zspage = cache_alloc_zspage(pool, gfp); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci if (!zspage) 98962306a36Sopenharmony_ci return NULL; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci zspage->magic = ZSPAGE_MAGIC; 99262306a36Sopenharmony_ci migrate_lock_init(zspage); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci for (i = 0; i < class->pages_per_zspage; i++) { 99562306a36Sopenharmony_ci struct page *page; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci page = alloc_page(gfp); 99862306a36Sopenharmony_ci if (!page) { 99962306a36Sopenharmony_ci while (--i >= 0) { 100062306a36Sopenharmony_ci dec_zone_page_state(pages[i], NR_ZSPAGES); 100162306a36Sopenharmony_ci __free_page(pages[i]); 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci cache_free_zspage(pool, zspage); 100462306a36Sopenharmony_ci return NULL; 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci inc_zone_page_state(page, NR_ZSPAGES); 100862306a36Sopenharmony_ci pages[i] = page; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci create_page_chain(class, zspage, pages); 101262306a36Sopenharmony_ci init_zspage(class, zspage); 101362306a36Sopenharmony_ci zspage->pool = pool; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci return zspage; 101662306a36Sopenharmony_ci} 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_cistatic struct zspage *find_get_zspage(struct size_class *class) 101962306a36Sopenharmony_ci{ 102062306a36Sopenharmony_ci int i; 102162306a36Sopenharmony_ci struct zspage *zspage; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci for (i = ZS_INUSE_RATIO_99; i >= ZS_INUSE_RATIO_0; i--) { 102462306a36Sopenharmony_ci zspage = list_first_entry_or_null(&class->fullness_list[i], 102562306a36Sopenharmony_ci struct zspage, list); 102662306a36Sopenharmony_ci if (zspage) 102762306a36Sopenharmony_ci break; 102862306a36Sopenharmony_ci } 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci return zspage; 103162306a36Sopenharmony_ci} 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_cistatic inline int __zs_cpu_up(struct mapping_area *area) 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci /* 103662306a36Sopenharmony_ci * Make sure we don't leak memory if a cpu UP notification 103762306a36Sopenharmony_ci * and zs_init() race and both call zs_cpu_up() on the same cpu 103862306a36Sopenharmony_ci */ 103962306a36Sopenharmony_ci if (area->vm_buf) 104062306a36Sopenharmony_ci return 0; 104162306a36Sopenharmony_ci area->vm_buf = kmalloc(ZS_MAX_ALLOC_SIZE, GFP_KERNEL); 104262306a36Sopenharmony_ci if (!area->vm_buf) 104362306a36Sopenharmony_ci return -ENOMEM; 104462306a36Sopenharmony_ci return 0; 104562306a36Sopenharmony_ci} 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_cistatic inline void __zs_cpu_down(struct mapping_area *area) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci kfree(area->vm_buf); 105062306a36Sopenharmony_ci area->vm_buf = NULL; 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_cistatic void *__zs_map_object(struct mapping_area *area, 105462306a36Sopenharmony_ci struct page *pages[2], int off, int size) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci int sizes[2]; 105762306a36Sopenharmony_ci void *addr; 105862306a36Sopenharmony_ci char *buf = area->vm_buf; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci /* disable page faults to match kmap_atomic() return conditions */ 106162306a36Sopenharmony_ci pagefault_disable(); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci /* no read fastpath */ 106462306a36Sopenharmony_ci if (area->vm_mm == ZS_MM_WO) 106562306a36Sopenharmony_ci goto out; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci sizes[0] = PAGE_SIZE - off; 106862306a36Sopenharmony_ci sizes[1] = size - sizes[0]; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci /* copy object to per-cpu buffer */ 107162306a36Sopenharmony_ci addr = kmap_atomic(pages[0]); 107262306a36Sopenharmony_ci memcpy(buf, addr + off, sizes[0]); 107362306a36Sopenharmony_ci kunmap_atomic(addr); 107462306a36Sopenharmony_ci addr = kmap_atomic(pages[1]); 107562306a36Sopenharmony_ci memcpy(buf + sizes[0], addr, sizes[1]); 107662306a36Sopenharmony_ci kunmap_atomic(addr); 107762306a36Sopenharmony_ciout: 107862306a36Sopenharmony_ci return area->vm_buf; 107962306a36Sopenharmony_ci} 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_cistatic void __zs_unmap_object(struct mapping_area *area, 108262306a36Sopenharmony_ci struct page *pages[2], int off, int size) 108362306a36Sopenharmony_ci{ 108462306a36Sopenharmony_ci int sizes[2]; 108562306a36Sopenharmony_ci void *addr; 108662306a36Sopenharmony_ci char *buf; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci /* no write fastpath */ 108962306a36Sopenharmony_ci if (area->vm_mm == ZS_MM_RO) 109062306a36Sopenharmony_ci goto out; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci buf = area->vm_buf; 109362306a36Sopenharmony_ci buf = buf + ZS_HANDLE_SIZE; 109462306a36Sopenharmony_ci size -= ZS_HANDLE_SIZE; 109562306a36Sopenharmony_ci off += ZS_HANDLE_SIZE; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci sizes[0] = PAGE_SIZE - off; 109862306a36Sopenharmony_ci sizes[1] = size - sizes[0]; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci /* copy per-cpu buffer to object */ 110162306a36Sopenharmony_ci addr = kmap_atomic(pages[0]); 110262306a36Sopenharmony_ci memcpy(addr + off, buf, sizes[0]); 110362306a36Sopenharmony_ci kunmap_atomic(addr); 110462306a36Sopenharmony_ci addr = kmap_atomic(pages[1]); 110562306a36Sopenharmony_ci memcpy(addr, buf + sizes[0], sizes[1]); 110662306a36Sopenharmony_ci kunmap_atomic(addr); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ciout: 110962306a36Sopenharmony_ci /* enable page faults to match kunmap_atomic() return conditions */ 111062306a36Sopenharmony_ci pagefault_enable(); 111162306a36Sopenharmony_ci} 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_cistatic int zs_cpu_prepare(unsigned int cpu) 111462306a36Sopenharmony_ci{ 111562306a36Sopenharmony_ci struct mapping_area *area; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci area = &per_cpu(zs_map_area, cpu); 111862306a36Sopenharmony_ci return __zs_cpu_up(area); 111962306a36Sopenharmony_ci} 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_cistatic int zs_cpu_dead(unsigned int cpu) 112262306a36Sopenharmony_ci{ 112362306a36Sopenharmony_ci struct mapping_area *area; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci area = &per_cpu(zs_map_area, cpu); 112662306a36Sopenharmony_ci __zs_cpu_down(area); 112762306a36Sopenharmony_ci return 0; 112862306a36Sopenharmony_ci} 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_cistatic bool can_merge(struct size_class *prev, int pages_per_zspage, 113162306a36Sopenharmony_ci int objs_per_zspage) 113262306a36Sopenharmony_ci{ 113362306a36Sopenharmony_ci if (prev->pages_per_zspage == pages_per_zspage && 113462306a36Sopenharmony_ci prev->objs_per_zspage == objs_per_zspage) 113562306a36Sopenharmony_ci return true; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci return false; 113862306a36Sopenharmony_ci} 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_cistatic bool zspage_full(struct size_class *class, struct zspage *zspage) 114162306a36Sopenharmony_ci{ 114262306a36Sopenharmony_ci return get_zspage_inuse(zspage) == class->objs_per_zspage; 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_cistatic bool zspage_empty(struct zspage *zspage) 114662306a36Sopenharmony_ci{ 114762306a36Sopenharmony_ci return get_zspage_inuse(zspage) == 0; 114862306a36Sopenharmony_ci} 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci/** 115162306a36Sopenharmony_ci * zs_lookup_class_index() - Returns index of the zsmalloc &size_class 115262306a36Sopenharmony_ci * that hold objects of the provided size. 115362306a36Sopenharmony_ci * @pool: zsmalloc pool to use 115462306a36Sopenharmony_ci * @size: object size 115562306a36Sopenharmony_ci * 115662306a36Sopenharmony_ci * Context: Any context. 115762306a36Sopenharmony_ci * 115862306a36Sopenharmony_ci * Return: the index of the zsmalloc &size_class that hold objects of the 115962306a36Sopenharmony_ci * provided size. 116062306a36Sopenharmony_ci */ 116162306a36Sopenharmony_ciunsigned int zs_lookup_class_index(struct zs_pool *pool, unsigned int size) 116262306a36Sopenharmony_ci{ 116362306a36Sopenharmony_ci struct size_class *class; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci class = pool->size_class[get_size_class_index(size)]; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci return class->index; 116862306a36Sopenharmony_ci} 116962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_lookup_class_index); 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ciunsigned long zs_get_total_pages(struct zs_pool *pool) 117262306a36Sopenharmony_ci{ 117362306a36Sopenharmony_ci return atomic_long_read(&pool->pages_allocated); 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_get_total_pages); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci/** 117862306a36Sopenharmony_ci * zs_map_object - get address of allocated object from handle. 117962306a36Sopenharmony_ci * @pool: pool from which the object was allocated 118062306a36Sopenharmony_ci * @handle: handle returned from zs_malloc 118162306a36Sopenharmony_ci * @mm: mapping mode to use 118262306a36Sopenharmony_ci * 118362306a36Sopenharmony_ci * Before using an object allocated from zs_malloc, it must be mapped using 118462306a36Sopenharmony_ci * this function. When done with the object, it must be unmapped using 118562306a36Sopenharmony_ci * zs_unmap_object. 118662306a36Sopenharmony_ci * 118762306a36Sopenharmony_ci * Only one object can be mapped per cpu at a time. There is no protection 118862306a36Sopenharmony_ci * against nested mappings. 118962306a36Sopenharmony_ci * 119062306a36Sopenharmony_ci * This function returns with preemption and page faults disabled. 119162306a36Sopenharmony_ci */ 119262306a36Sopenharmony_civoid *zs_map_object(struct zs_pool *pool, unsigned long handle, 119362306a36Sopenharmony_ci enum zs_mapmode mm) 119462306a36Sopenharmony_ci{ 119562306a36Sopenharmony_ci struct zspage *zspage; 119662306a36Sopenharmony_ci struct page *page; 119762306a36Sopenharmony_ci unsigned long obj, off; 119862306a36Sopenharmony_ci unsigned int obj_idx; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci struct size_class *class; 120162306a36Sopenharmony_ci struct mapping_area *area; 120262306a36Sopenharmony_ci struct page *pages[2]; 120362306a36Sopenharmony_ci void *ret; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci /* 120662306a36Sopenharmony_ci * Because we use per-cpu mapping areas shared among the 120762306a36Sopenharmony_ci * pools/users, we can't allow mapping in interrupt context 120862306a36Sopenharmony_ci * because it can corrupt another users mappings. 120962306a36Sopenharmony_ci */ 121062306a36Sopenharmony_ci BUG_ON(in_interrupt()); 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci /* It guarantees it can get zspage from handle safely */ 121362306a36Sopenharmony_ci spin_lock(&pool->lock); 121462306a36Sopenharmony_ci obj = handle_to_obj(handle); 121562306a36Sopenharmony_ci obj_to_location(obj, &page, &obj_idx); 121662306a36Sopenharmony_ci zspage = get_zspage(page); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci /* 121962306a36Sopenharmony_ci * migration cannot move any zpages in this zspage. Here, pool->lock 122062306a36Sopenharmony_ci * is too heavy since callers would take some time until they calls 122162306a36Sopenharmony_ci * zs_unmap_object API so delegate the locking from class to zspage 122262306a36Sopenharmony_ci * which is smaller granularity. 122362306a36Sopenharmony_ci */ 122462306a36Sopenharmony_ci migrate_read_lock(zspage); 122562306a36Sopenharmony_ci spin_unlock(&pool->lock); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci class = zspage_class(pool, zspage); 122862306a36Sopenharmony_ci off = offset_in_page(class->size * obj_idx); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci local_lock(&zs_map_area.lock); 123162306a36Sopenharmony_ci area = this_cpu_ptr(&zs_map_area); 123262306a36Sopenharmony_ci area->vm_mm = mm; 123362306a36Sopenharmony_ci if (off + class->size <= PAGE_SIZE) { 123462306a36Sopenharmony_ci /* this object is contained entirely within a page */ 123562306a36Sopenharmony_ci area->vm_addr = kmap_atomic(page); 123662306a36Sopenharmony_ci ret = area->vm_addr + off; 123762306a36Sopenharmony_ci goto out; 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci /* this object spans two pages */ 124162306a36Sopenharmony_ci pages[0] = page; 124262306a36Sopenharmony_ci pages[1] = get_next_page(page); 124362306a36Sopenharmony_ci BUG_ON(!pages[1]); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci ret = __zs_map_object(area, pages, off, class->size); 124662306a36Sopenharmony_ciout: 124762306a36Sopenharmony_ci if (likely(!ZsHugePage(zspage))) 124862306a36Sopenharmony_ci ret += ZS_HANDLE_SIZE; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci return ret; 125162306a36Sopenharmony_ci} 125262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_map_object); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_civoid zs_unmap_object(struct zs_pool *pool, unsigned long handle) 125562306a36Sopenharmony_ci{ 125662306a36Sopenharmony_ci struct zspage *zspage; 125762306a36Sopenharmony_ci struct page *page; 125862306a36Sopenharmony_ci unsigned long obj, off; 125962306a36Sopenharmony_ci unsigned int obj_idx; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci struct size_class *class; 126262306a36Sopenharmony_ci struct mapping_area *area; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci obj = handle_to_obj(handle); 126562306a36Sopenharmony_ci obj_to_location(obj, &page, &obj_idx); 126662306a36Sopenharmony_ci zspage = get_zspage(page); 126762306a36Sopenharmony_ci class = zspage_class(pool, zspage); 126862306a36Sopenharmony_ci off = offset_in_page(class->size * obj_idx); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci area = this_cpu_ptr(&zs_map_area); 127162306a36Sopenharmony_ci if (off + class->size <= PAGE_SIZE) 127262306a36Sopenharmony_ci kunmap_atomic(area->vm_addr); 127362306a36Sopenharmony_ci else { 127462306a36Sopenharmony_ci struct page *pages[2]; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci pages[0] = page; 127762306a36Sopenharmony_ci pages[1] = get_next_page(page); 127862306a36Sopenharmony_ci BUG_ON(!pages[1]); 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci __zs_unmap_object(area, pages, off, class->size); 128162306a36Sopenharmony_ci } 128262306a36Sopenharmony_ci local_unlock(&zs_map_area.lock); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci migrate_read_unlock(zspage); 128562306a36Sopenharmony_ci} 128662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_unmap_object); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci/** 128962306a36Sopenharmony_ci * zs_huge_class_size() - Returns the size (in bytes) of the first huge 129062306a36Sopenharmony_ci * zsmalloc &size_class. 129162306a36Sopenharmony_ci * @pool: zsmalloc pool to use 129262306a36Sopenharmony_ci * 129362306a36Sopenharmony_ci * The function returns the size of the first huge class - any object of equal 129462306a36Sopenharmony_ci * or bigger size will be stored in zspage consisting of a single physical 129562306a36Sopenharmony_ci * page. 129662306a36Sopenharmony_ci * 129762306a36Sopenharmony_ci * Context: Any context. 129862306a36Sopenharmony_ci * 129962306a36Sopenharmony_ci * Return: the size (in bytes) of the first huge zsmalloc &size_class. 130062306a36Sopenharmony_ci */ 130162306a36Sopenharmony_cisize_t zs_huge_class_size(struct zs_pool *pool) 130262306a36Sopenharmony_ci{ 130362306a36Sopenharmony_ci return huge_class_size; 130462306a36Sopenharmony_ci} 130562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_huge_class_size); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_cistatic unsigned long obj_malloc(struct zs_pool *pool, 130862306a36Sopenharmony_ci struct zspage *zspage, unsigned long handle) 130962306a36Sopenharmony_ci{ 131062306a36Sopenharmony_ci int i, nr_page, offset; 131162306a36Sopenharmony_ci unsigned long obj; 131262306a36Sopenharmony_ci struct link_free *link; 131362306a36Sopenharmony_ci struct size_class *class; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci struct page *m_page; 131662306a36Sopenharmony_ci unsigned long m_offset; 131762306a36Sopenharmony_ci void *vaddr; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci class = pool->size_class[zspage->class]; 132062306a36Sopenharmony_ci handle |= OBJ_ALLOCATED_TAG; 132162306a36Sopenharmony_ci obj = get_freeobj(zspage); 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci offset = obj * class->size; 132462306a36Sopenharmony_ci nr_page = offset >> PAGE_SHIFT; 132562306a36Sopenharmony_ci m_offset = offset_in_page(offset); 132662306a36Sopenharmony_ci m_page = get_first_page(zspage); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci for (i = 0; i < nr_page; i++) 132962306a36Sopenharmony_ci m_page = get_next_page(m_page); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci vaddr = kmap_atomic(m_page); 133262306a36Sopenharmony_ci link = (struct link_free *)vaddr + m_offset / sizeof(*link); 133362306a36Sopenharmony_ci set_freeobj(zspage, link->next >> OBJ_TAG_BITS); 133462306a36Sopenharmony_ci if (likely(!ZsHugePage(zspage))) 133562306a36Sopenharmony_ci /* record handle in the header of allocated chunk */ 133662306a36Sopenharmony_ci link->handle = handle; 133762306a36Sopenharmony_ci else 133862306a36Sopenharmony_ci /* record handle to page->index */ 133962306a36Sopenharmony_ci zspage->first_page->index = handle; 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci kunmap_atomic(vaddr); 134262306a36Sopenharmony_ci mod_zspage_inuse(zspage, 1); 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci obj = location_to_obj(m_page, obj); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci return obj; 134762306a36Sopenharmony_ci} 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci/** 135162306a36Sopenharmony_ci * zs_malloc - Allocate block of given size from pool. 135262306a36Sopenharmony_ci * @pool: pool to allocate from 135362306a36Sopenharmony_ci * @size: size of block to allocate 135462306a36Sopenharmony_ci * @gfp: gfp flags when allocating object 135562306a36Sopenharmony_ci * 135662306a36Sopenharmony_ci * On success, handle to the allocated object is returned, 135762306a36Sopenharmony_ci * otherwise an ERR_PTR(). 135862306a36Sopenharmony_ci * Allocation requests with size > ZS_MAX_ALLOC_SIZE will fail. 135962306a36Sopenharmony_ci */ 136062306a36Sopenharmony_ciunsigned long zs_malloc(struct zs_pool *pool, size_t size, gfp_t gfp) 136162306a36Sopenharmony_ci{ 136262306a36Sopenharmony_ci unsigned long handle, obj; 136362306a36Sopenharmony_ci struct size_class *class; 136462306a36Sopenharmony_ci int newfg; 136562306a36Sopenharmony_ci struct zspage *zspage; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci if (unlikely(!size || size > ZS_MAX_ALLOC_SIZE)) 136862306a36Sopenharmony_ci return (unsigned long)ERR_PTR(-EINVAL); 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci handle = cache_alloc_handle(pool, gfp); 137162306a36Sopenharmony_ci if (!handle) 137262306a36Sopenharmony_ci return (unsigned long)ERR_PTR(-ENOMEM); 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci /* extra space in chunk to keep the handle */ 137562306a36Sopenharmony_ci size += ZS_HANDLE_SIZE; 137662306a36Sopenharmony_ci class = pool->size_class[get_size_class_index(size)]; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci /* pool->lock effectively protects the zpage migration */ 137962306a36Sopenharmony_ci spin_lock(&pool->lock); 138062306a36Sopenharmony_ci zspage = find_get_zspage(class); 138162306a36Sopenharmony_ci if (likely(zspage)) { 138262306a36Sopenharmony_ci obj = obj_malloc(pool, zspage, handle); 138362306a36Sopenharmony_ci /* Now move the zspage to another fullness group, if required */ 138462306a36Sopenharmony_ci fix_fullness_group(class, zspage); 138562306a36Sopenharmony_ci record_obj(handle, obj); 138662306a36Sopenharmony_ci class_stat_inc(class, ZS_OBJS_INUSE, 1); 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci goto out; 138962306a36Sopenharmony_ci } 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci spin_unlock(&pool->lock); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci zspage = alloc_zspage(pool, class, gfp); 139462306a36Sopenharmony_ci if (!zspage) { 139562306a36Sopenharmony_ci cache_free_handle(pool, handle); 139662306a36Sopenharmony_ci return (unsigned long)ERR_PTR(-ENOMEM); 139762306a36Sopenharmony_ci } 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci spin_lock(&pool->lock); 140062306a36Sopenharmony_ci obj = obj_malloc(pool, zspage, handle); 140162306a36Sopenharmony_ci newfg = get_fullness_group(class, zspage); 140262306a36Sopenharmony_ci insert_zspage(class, zspage, newfg); 140362306a36Sopenharmony_ci set_zspage_mapping(zspage, class->index, newfg); 140462306a36Sopenharmony_ci record_obj(handle, obj); 140562306a36Sopenharmony_ci atomic_long_add(class->pages_per_zspage, &pool->pages_allocated); 140662306a36Sopenharmony_ci class_stat_inc(class, ZS_OBJS_ALLOCATED, class->objs_per_zspage); 140762306a36Sopenharmony_ci class_stat_inc(class, ZS_OBJS_INUSE, 1); 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci /* We completely set up zspage so mark them as movable */ 141062306a36Sopenharmony_ci SetZsPageMovable(pool, zspage); 141162306a36Sopenharmony_ciout: 141262306a36Sopenharmony_ci spin_unlock(&pool->lock); 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci return handle; 141562306a36Sopenharmony_ci} 141662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_malloc); 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_cistatic void obj_free(int class_size, unsigned long obj) 141962306a36Sopenharmony_ci{ 142062306a36Sopenharmony_ci struct link_free *link; 142162306a36Sopenharmony_ci struct zspage *zspage; 142262306a36Sopenharmony_ci struct page *f_page; 142362306a36Sopenharmony_ci unsigned long f_offset; 142462306a36Sopenharmony_ci unsigned int f_objidx; 142562306a36Sopenharmony_ci void *vaddr; 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci obj_to_location(obj, &f_page, &f_objidx); 142862306a36Sopenharmony_ci f_offset = offset_in_page(class_size * f_objidx); 142962306a36Sopenharmony_ci zspage = get_zspage(f_page); 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci vaddr = kmap_atomic(f_page); 143262306a36Sopenharmony_ci link = (struct link_free *)(vaddr + f_offset); 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci /* Insert this object in containing zspage's freelist */ 143562306a36Sopenharmony_ci if (likely(!ZsHugePage(zspage))) 143662306a36Sopenharmony_ci link->next = get_freeobj(zspage) << OBJ_TAG_BITS; 143762306a36Sopenharmony_ci else 143862306a36Sopenharmony_ci f_page->index = 0; 143962306a36Sopenharmony_ci set_freeobj(zspage, f_objidx); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci kunmap_atomic(vaddr); 144262306a36Sopenharmony_ci mod_zspage_inuse(zspage, -1); 144362306a36Sopenharmony_ci} 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_civoid zs_free(struct zs_pool *pool, unsigned long handle) 144662306a36Sopenharmony_ci{ 144762306a36Sopenharmony_ci struct zspage *zspage; 144862306a36Sopenharmony_ci struct page *f_page; 144962306a36Sopenharmony_ci unsigned long obj; 145062306a36Sopenharmony_ci struct size_class *class; 145162306a36Sopenharmony_ci int fullness; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci if (IS_ERR_OR_NULL((void *)handle)) 145462306a36Sopenharmony_ci return; 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci /* 145762306a36Sopenharmony_ci * The pool->lock protects the race with zpage's migration 145862306a36Sopenharmony_ci * so it's safe to get the page from handle. 145962306a36Sopenharmony_ci */ 146062306a36Sopenharmony_ci spin_lock(&pool->lock); 146162306a36Sopenharmony_ci obj = handle_to_obj(handle); 146262306a36Sopenharmony_ci obj_to_page(obj, &f_page); 146362306a36Sopenharmony_ci zspage = get_zspage(f_page); 146462306a36Sopenharmony_ci class = zspage_class(pool, zspage); 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci class_stat_dec(class, ZS_OBJS_INUSE, 1); 146762306a36Sopenharmony_ci obj_free(class->size, obj); 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci fullness = fix_fullness_group(class, zspage); 147062306a36Sopenharmony_ci if (fullness == ZS_INUSE_RATIO_0) 147162306a36Sopenharmony_ci free_zspage(pool, class, zspage); 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci spin_unlock(&pool->lock); 147462306a36Sopenharmony_ci cache_free_handle(pool, handle); 147562306a36Sopenharmony_ci} 147662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_free); 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_cistatic void zs_object_copy(struct size_class *class, unsigned long dst, 147962306a36Sopenharmony_ci unsigned long src) 148062306a36Sopenharmony_ci{ 148162306a36Sopenharmony_ci struct page *s_page, *d_page; 148262306a36Sopenharmony_ci unsigned int s_objidx, d_objidx; 148362306a36Sopenharmony_ci unsigned long s_off, d_off; 148462306a36Sopenharmony_ci void *s_addr, *d_addr; 148562306a36Sopenharmony_ci int s_size, d_size, size; 148662306a36Sopenharmony_ci int written = 0; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci s_size = d_size = class->size; 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci obj_to_location(src, &s_page, &s_objidx); 149162306a36Sopenharmony_ci obj_to_location(dst, &d_page, &d_objidx); 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci s_off = offset_in_page(class->size * s_objidx); 149462306a36Sopenharmony_ci d_off = offset_in_page(class->size * d_objidx); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci if (s_off + class->size > PAGE_SIZE) 149762306a36Sopenharmony_ci s_size = PAGE_SIZE - s_off; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci if (d_off + class->size > PAGE_SIZE) 150062306a36Sopenharmony_ci d_size = PAGE_SIZE - d_off; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci s_addr = kmap_atomic(s_page); 150362306a36Sopenharmony_ci d_addr = kmap_atomic(d_page); 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci while (1) { 150662306a36Sopenharmony_ci size = min(s_size, d_size); 150762306a36Sopenharmony_ci memcpy(d_addr + d_off, s_addr + s_off, size); 150862306a36Sopenharmony_ci written += size; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci if (written == class->size) 151162306a36Sopenharmony_ci break; 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci s_off += size; 151462306a36Sopenharmony_ci s_size -= size; 151562306a36Sopenharmony_ci d_off += size; 151662306a36Sopenharmony_ci d_size -= size; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci /* 151962306a36Sopenharmony_ci * Calling kunmap_atomic(d_addr) is necessary. kunmap_atomic() 152062306a36Sopenharmony_ci * calls must occurs in reverse order of calls to kmap_atomic(). 152162306a36Sopenharmony_ci * So, to call kunmap_atomic(s_addr) we should first call 152262306a36Sopenharmony_ci * kunmap_atomic(d_addr). For more details see 152362306a36Sopenharmony_ci * Documentation/mm/highmem.rst. 152462306a36Sopenharmony_ci */ 152562306a36Sopenharmony_ci if (s_off >= PAGE_SIZE) { 152662306a36Sopenharmony_ci kunmap_atomic(d_addr); 152762306a36Sopenharmony_ci kunmap_atomic(s_addr); 152862306a36Sopenharmony_ci s_page = get_next_page(s_page); 152962306a36Sopenharmony_ci s_addr = kmap_atomic(s_page); 153062306a36Sopenharmony_ci d_addr = kmap_atomic(d_page); 153162306a36Sopenharmony_ci s_size = class->size - written; 153262306a36Sopenharmony_ci s_off = 0; 153362306a36Sopenharmony_ci } 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci if (d_off >= PAGE_SIZE) { 153662306a36Sopenharmony_ci kunmap_atomic(d_addr); 153762306a36Sopenharmony_ci d_page = get_next_page(d_page); 153862306a36Sopenharmony_ci d_addr = kmap_atomic(d_page); 153962306a36Sopenharmony_ci d_size = class->size - written; 154062306a36Sopenharmony_ci d_off = 0; 154162306a36Sopenharmony_ci } 154262306a36Sopenharmony_ci } 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci kunmap_atomic(d_addr); 154562306a36Sopenharmony_ci kunmap_atomic(s_addr); 154662306a36Sopenharmony_ci} 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci/* 154962306a36Sopenharmony_ci * Find alloced object in zspage from index object and 155062306a36Sopenharmony_ci * return handle. 155162306a36Sopenharmony_ci */ 155262306a36Sopenharmony_cistatic unsigned long find_alloced_obj(struct size_class *class, 155362306a36Sopenharmony_ci struct page *page, int *obj_idx) 155462306a36Sopenharmony_ci{ 155562306a36Sopenharmony_ci unsigned int offset; 155662306a36Sopenharmony_ci int index = *obj_idx; 155762306a36Sopenharmony_ci unsigned long handle = 0; 155862306a36Sopenharmony_ci void *addr = kmap_atomic(page); 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci offset = get_first_obj_offset(page); 156162306a36Sopenharmony_ci offset += class->size * index; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci while (offset < PAGE_SIZE) { 156462306a36Sopenharmony_ci if (obj_allocated(page, addr + offset, &handle)) 156562306a36Sopenharmony_ci break; 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci offset += class->size; 156862306a36Sopenharmony_ci index++; 156962306a36Sopenharmony_ci } 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci kunmap_atomic(addr); 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci *obj_idx = index; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci return handle; 157662306a36Sopenharmony_ci} 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_cistatic void migrate_zspage(struct zs_pool *pool, struct zspage *src_zspage, 157962306a36Sopenharmony_ci struct zspage *dst_zspage) 158062306a36Sopenharmony_ci{ 158162306a36Sopenharmony_ci unsigned long used_obj, free_obj; 158262306a36Sopenharmony_ci unsigned long handle; 158362306a36Sopenharmony_ci int obj_idx = 0; 158462306a36Sopenharmony_ci struct page *s_page = get_first_page(src_zspage); 158562306a36Sopenharmony_ci struct size_class *class = pool->size_class[src_zspage->class]; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci while (1) { 158862306a36Sopenharmony_ci handle = find_alloced_obj(class, s_page, &obj_idx); 158962306a36Sopenharmony_ci if (!handle) { 159062306a36Sopenharmony_ci s_page = get_next_page(s_page); 159162306a36Sopenharmony_ci if (!s_page) 159262306a36Sopenharmony_ci break; 159362306a36Sopenharmony_ci obj_idx = 0; 159462306a36Sopenharmony_ci continue; 159562306a36Sopenharmony_ci } 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci used_obj = handle_to_obj(handle); 159862306a36Sopenharmony_ci free_obj = obj_malloc(pool, dst_zspage, handle); 159962306a36Sopenharmony_ci zs_object_copy(class, free_obj, used_obj); 160062306a36Sopenharmony_ci obj_idx++; 160162306a36Sopenharmony_ci record_obj(handle, free_obj); 160262306a36Sopenharmony_ci obj_free(class->size, used_obj); 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci /* Stop if there is no more space */ 160562306a36Sopenharmony_ci if (zspage_full(class, dst_zspage)) 160662306a36Sopenharmony_ci break; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci /* Stop if there are no more objects to migrate */ 160962306a36Sopenharmony_ci if (zspage_empty(src_zspage)) 161062306a36Sopenharmony_ci break; 161162306a36Sopenharmony_ci } 161262306a36Sopenharmony_ci} 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_cistatic struct zspage *isolate_src_zspage(struct size_class *class) 161562306a36Sopenharmony_ci{ 161662306a36Sopenharmony_ci struct zspage *zspage; 161762306a36Sopenharmony_ci int fg; 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci for (fg = ZS_INUSE_RATIO_10; fg <= ZS_INUSE_RATIO_99; fg++) { 162062306a36Sopenharmony_ci zspage = list_first_entry_or_null(&class->fullness_list[fg], 162162306a36Sopenharmony_ci struct zspage, list); 162262306a36Sopenharmony_ci if (zspage) { 162362306a36Sopenharmony_ci remove_zspage(class, zspage, fg); 162462306a36Sopenharmony_ci return zspage; 162562306a36Sopenharmony_ci } 162662306a36Sopenharmony_ci } 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci return zspage; 162962306a36Sopenharmony_ci} 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_cistatic struct zspage *isolate_dst_zspage(struct size_class *class) 163262306a36Sopenharmony_ci{ 163362306a36Sopenharmony_ci struct zspage *zspage; 163462306a36Sopenharmony_ci int fg; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci for (fg = ZS_INUSE_RATIO_99; fg >= ZS_INUSE_RATIO_10; fg--) { 163762306a36Sopenharmony_ci zspage = list_first_entry_or_null(&class->fullness_list[fg], 163862306a36Sopenharmony_ci struct zspage, list); 163962306a36Sopenharmony_ci if (zspage) { 164062306a36Sopenharmony_ci remove_zspage(class, zspage, fg); 164162306a36Sopenharmony_ci return zspage; 164262306a36Sopenharmony_ci } 164362306a36Sopenharmony_ci } 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci return zspage; 164662306a36Sopenharmony_ci} 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci/* 164962306a36Sopenharmony_ci * putback_zspage - add @zspage into right class's fullness list 165062306a36Sopenharmony_ci * @class: destination class 165162306a36Sopenharmony_ci * @zspage: target page 165262306a36Sopenharmony_ci * 165362306a36Sopenharmony_ci * Return @zspage's fullness status 165462306a36Sopenharmony_ci */ 165562306a36Sopenharmony_cistatic int putback_zspage(struct size_class *class, struct zspage *zspage) 165662306a36Sopenharmony_ci{ 165762306a36Sopenharmony_ci int fullness; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci fullness = get_fullness_group(class, zspage); 166062306a36Sopenharmony_ci insert_zspage(class, zspage, fullness); 166162306a36Sopenharmony_ci set_zspage_mapping(zspage, class->index, fullness); 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci return fullness; 166462306a36Sopenharmony_ci} 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci#ifdef CONFIG_COMPACTION 166762306a36Sopenharmony_ci/* 166862306a36Sopenharmony_ci * To prevent zspage destroy during migration, zspage freeing should 166962306a36Sopenharmony_ci * hold locks of all pages in the zspage. 167062306a36Sopenharmony_ci */ 167162306a36Sopenharmony_cistatic void lock_zspage(struct zspage *zspage) 167262306a36Sopenharmony_ci{ 167362306a36Sopenharmony_ci struct page *curr_page, *page; 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci /* 167662306a36Sopenharmony_ci * Pages we haven't locked yet can be migrated off the list while we're 167762306a36Sopenharmony_ci * trying to lock them, so we need to be careful and only attempt to 167862306a36Sopenharmony_ci * lock each page under migrate_read_lock(). Otherwise, the page we lock 167962306a36Sopenharmony_ci * may no longer belong to the zspage. This means that we may wait for 168062306a36Sopenharmony_ci * the wrong page to unlock, so we must take a reference to the page 168162306a36Sopenharmony_ci * prior to waiting for it to unlock outside migrate_read_lock(). 168262306a36Sopenharmony_ci */ 168362306a36Sopenharmony_ci while (1) { 168462306a36Sopenharmony_ci migrate_read_lock(zspage); 168562306a36Sopenharmony_ci page = get_first_page(zspage); 168662306a36Sopenharmony_ci if (trylock_page(page)) 168762306a36Sopenharmony_ci break; 168862306a36Sopenharmony_ci get_page(page); 168962306a36Sopenharmony_ci migrate_read_unlock(zspage); 169062306a36Sopenharmony_ci wait_on_page_locked(page); 169162306a36Sopenharmony_ci put_page(page); 169262306a36Sopenharmony_ci } 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci curr_page = page; 169562306a36Sopenharmony_ci while ((page = get_next_page(curr_page))) { 169662306a36Sopenharmony_ci if (trylock_page(page)) { 169762306a36Sopenharmony_ci curr_page = page; 169862306a36Sopenharmony_ci } else { 169962306a36Sopenharmony_ci get_page(page); 170062306a36Sopenharmony_ci migrate_read_unlock(zspage); 170162306a36Sopenharmony_ci wait_on_page_locked(page); 170262306a36Sopenharmony_ci put_page(page); 170362306a36Sopenharmony_ci migrate_read_lock(zspage); 170462306a36Sopenharmony_ci } 170562306a36Sopenharmony_ci } 170662306a36Sopenharmony_ci migrate_read_unlock(zspage); 170762306a36Sopenharmony_ci} 170862306a36Sopenharmony_ci#endif /* CONFIG_COMPACTION */ 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_cistatic void migrate_lock_init(struct zspage *zspage) 171162306a36Sopenharmony_ci{ 171262306a36Sopenharmony_ci rwlock_init(&zspage->lock); 171362306a36Sopenharmony_ci} 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_cistatic void migrate_read_lock(struct zspage *zspage) __acquires(&zspage->lock) 171662306a36Sopenharmony_ci{ 171762306a36Sopenharmony_ci read_lock(&zspage->lock); 171862306a36Sopenharmony_ci} 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_cistatic void migrate_read_unlock(struct zspage *zspage) __releases(&zspage->lock) 172162306a36Sopenharmony_ci{ 172262306a36Sopenharmony_ci read_unlock(&zspage->lock); 172362306a36Sopenharmony_ci} 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci#ifdef CONFIG_COMPACTION 172662306a36Sopenharmony_cistatic void migrate_write_lock(struct zspage *zspage) 172762306a36Sopenharmony_ci{ 172862306a36Sopenharmony_ci write_lock(&zspage->lock); 172962306a36Sopenharmony_ci} 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_cistatic void migrate_write_lock_nested(struct zspage *zspage) 173262306a36Sopenharmony_ci{ 173362306a36Sopenharmony_ci write_lock_nested(&zspage->lock, SINGLE_DEPTH_NESTING); 173462306a36Sopenharmony_ci} 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_cistatic void migrate_write_unlock(struct zspage *zspage) 173762306a36Sopenharmony_ci{ 173862306a36Sopenharmony_ci write_unlock(&zspage->lock); 173962306a36Sopenharmony_ci} 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci/* Number of isolated subpage for *page migration* in this zspage */ 174262306a36Sopenharmony_cistatic void inc_zspage_isolation(struct zspage *zspage) 174362306a36Sopenharmony_ci{ 174462306a36Sopenharmony_ci zspage->isolated++; 174562306a36Sopenharmony_ci} 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_cistatic void dec_zspage_isolation(struct zspage *zspage) 174862306a36Sopenharmony_ci{ 174962306a36Sopenharmony_ci VM_BUG_ON(zspage->isolated == 0); 175062306a36Sopenharmony_ci zspage->isolated--; 175162306a36Sopenharmony_ci} 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_cistatic const struct movable_operations zsmalloc_mops; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_cistatic void replace_sub_page(struct size_class *class, struct zspage *zspage, 175662306a36Sopenharmony_ci struct page *newpage, struct page *oldpage) 175762306a36Sopenharmony_ci{ 175862306a36Sopenharmony_ci struct page *page; 175962306a36Sopenharmony_ci struct page *pages[ZS_MAX_PAGES_PER_ZSPAGE] = {NULL, }; 176062306a36Sopenharmony_ci int idx = 0; 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci page = get_first_page(zspage); 176362306a36Sopenharmony_ci do { 176462306a36Sopenharmony_ci if (page == oldpage) 176562306a36Sopenharmony_ci pages[idx] = newpage; 176662306a36Sopenharmony_ci else 176762306a36Sopenharmony_ci pages[idx] = page; 176862306a36Sopenharmony_ci idx++; 176962306a36Sopenharmony_ci } while ((page = get_next_page(page)) != NULL); 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci create_page_chain(class, zspage, pages); 177262306a36Sopenharmony_ci set_first_obj_offset(newpage, get_first_obj_offset(oldpage)); 177362306a36Sopenharmony_ci if (unlikely(ZsHugePage(zspage))) 177462306a36Sopenharmony_ci newpage->index = oldpage->index; 177562306a36Sopenharmony_ci __SetPageMovable(newpage, &zsmalloc_mops); 177662306a36Sopenharmony_ci} 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_cistatic bool zs_page_isolate(struct page *page, isolate_mode_t mode) 177962306a36Sopenharmony_ci{ 178062306a36Sopenharmony_ci struct zs_pool *pool; 178162306a36Sopenharmony_ci struct zspage *zspage; 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci /* 178462306a36Sopenharmony_ci * Page is locked so zspage couldn't be destroyed. For detail, look at 178562306a36Sopenharmony_ci * lock_zspage in free_zspage. 178662306a36Sopenharmony_ci */ 178762306a36Sopenharmony_ci VM_BUG_ON_PAGE(PageIsolated(page), page); 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci zspage = get_zspage(page); 179062306a36Sopenharmony_ci pool = zspage->pool; 179162306a36Sopenharmony_ci spin_lock(&pool->lock); 179262306a36Sopenharmony_ci inc_zspage_isolation(zspage); 179362306a36Sopenharmony_ci spin_unlock(&pool->lock); 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci return true; 179662306a36Sopenharmony_ci} 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_cistatic int zs_page_migrate(struct page *newpage, struct page *page, 179962306a36Sopenharmony_ci enum migrate_mode mode) 180062306a36Sopenharmony_ci{ 180162306a36Sopenharmony_ci struct zs_pool *pool; 180262306a36Sopenharmony_ci struct size_class *class; 180362306a36Sopenharmony_ci struct zspage *zspage; 180462306a36Sopenharmony_ci struct page *dummy; 180562306a36Sopenharmony_ci void *s_addr, *d_addr, *addr; 180662306a36Sopenharmony_ci unsigned int offset; 180762306a36Sopenharmony_ci unsigned long handle; 180862306a36Sopenharmony_ci unsigned long old_obj, new_obj; 180962306a36Sopenharmony_ci unsigned int obj_idx; 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci /* 181262306a36Sopenharmony_ci * We cannot support the _NO_COPY case here, because copy needs to 181362306a36Sopenharmony_ci * happen under the zs lock, which does not work with 181462306a36Sopenharmony_ci * MIGRATE_SYNC_NO_COPY workflow. 181562306a36Sopenharmony_ci */ 181662306a36Sopenharmony_ci if (mode == MIGRATE_SYNC_NO_COPY) 181762306a36Sopenharmony_ci return -EINVAL; 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci VM_BUG_ON_PAGE(!PageIsolated(page), page); 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci /* The page is locked, so this pointer must remain valid */ 182262306a36Sopenharmony_ci zspage = get_zspage(page); 182362306a36Sopenharmony_ci pool = zspage->pool; 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci /* 182662306a36Sopenharmony_ci * The pool's lock protects the race between zpage migration 182762306a36Sopenharmony_ci * and zs_free. 182862306a36Sopenharmony_ci */ 182962306a36Sopenharmony_ci spin_lock(&pool->lock); 183062306a36Sopenharmony_ci class = zspage_class(pool, zspage); 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci /* the migrate_write_lock protects zpage access via zs_map_object */ 183362306a36Sopenharmony_ci migrate_write_lock(zspage); 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci offset = get_first_obj_offset(page); 183662306a36Sopenharmony_ci s_addr = kmap_atomic(page); 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci /* 183962306a36Sopenharmony_ci * Here, any user cannot access all objects in the zspage so let's move. 184062306a36Sopenharmony_ci */ 184162306a36Sopenharmony_ci d_addr = kmap_atomic(newpage); 184262306a36Sopenharmony_ci memcpy(d_addr, s_addr, PAGE_SIZE); 184362306a36Sopenharmony_ci kunmap_atomic(d_addr); 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci for (addr = s_addr + offset; addr < s_addr + PAGE_SIZE; 184662306a36Sopenharmony_ci addr += class->size) { 184762306a36Sopenharmony_ci if (obj_allocated(page, addr, &handle)) { 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci old_obj = handle_to_obj(handle); 185062306a36Sopenharmony_ci obj_to_location(old_obj, &dummy, &obj_idx); 185162306a36Sopenharmony_ci new_obj = (unsigned long)location_to_obj(newpage, 185262306a36Sopenharmony_ci obj_idx); 185362306a36Sopenharmony_ci record_obj(handle, new_obj); 185462306a36Sopenharmony_ci } 185562306a36Sopenharmony_ci } 185662306a36Sopenharmony_ci kunmap_atomic(s_addr); 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci replace_sub_page(class, zspage, newpage, page); 185962306a36Sopenharmony_ci dec_zspage_isolation(zspage); 186062306a36Sopenharmony_ci /* 186162306a36Sopenharmony_ci * Since we complete the data copy and set up new zspage structure, 186262306a36Sopenharmony_ci * it's okay to release the pool's lock. 186362306a36Sopenharmony_ci */ 186462306a36Sopenharmony_ci spin_unlock(&pool->lock); 186562306a36Sopenharmony_ci migrate_write_unlock(zspage); 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci get_page(newpage); 186862306a36Sopenharmony_ci if (page_zone(newpage) != page_zone(page)) { 186962306a36Sopenharmony_ci dec_zone_page_state(page, NR_ZSPAGES); 187062306a36Sopenharmony_ci inc_zone_page_state(newpage, NR_ZSPAGES); 187162306a36Sopenharmony_ci } 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci reset_page(page); 187462306a36Sopenharmony_ci put_page(page); 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci return MIGRATEPAGE_SUCCESS; 187762306a36Sopenharmony_ci} 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_cistatic void zs_page_putback(struct page *page) 188062306a36Sopenharmony_ci{ 188162306a36Sopenharmony_ci struct zs_pool *pool; 188262306a36Sopenharmony_ci struct zspage *zspage; 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci VM_BUG_ON_PAGE(!PageIsolated(page), page); 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci zspage = get_zspage(page); 188762306a36Sopenharmony_ci pool = zspage->pool; 188862306a36Sopenharmony_ci spin_lock(&pool->lock); 188962306a36Sopenharmony_ci dec_zspage_isolation(zspage); 189062306a36Sopenharmony_ci spin_unlock(&pool->lock); 189162306a36Sopenharmony_ci} 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_cistatic const struct movable_operations zsmalloc_mops = { 189462306a36Sopenharmony_ci .isolate_page = zs_page_isolate, 189562306a36Sopenharmony_ci .migrate_page = zs_page_migrate, 189662306a36Sopenharmony_ci .putback_page = zs_page_putback, 189762306a36Sopenharmony_ci}; 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci/* 190062306a36Sopenharmony_ci * Caller should hold page_lock of all pages in the zspage 190162306a36Sopenharmony_ci * In here, we cannot use zspage meta data. 190262306a36Sopenharmony_ci */ 190362306a36Sopenharmony_cistatic void async_free_zspage(struct work_struct *work) 190462306a36Sopenharmony_ci{ 190562306a36Sopenharmony_ci int i; 190662306a36Sopenharmony_ci struct size_class *class; 190762306a36Sopenharmony_ci unsigned int class_idx; 190862306a36Sopenharmony_ci int fullness; 190962306a36Sopenharmony_ci struct zspage *zspage, *tmp; 191062306a36Sopenharmony_ci LIST_HEAD(free_pages); 191162306a36Sopenharmony_ci struct zs_pool *pool = container_of(work, struct zs_pool, 191262306a36Sopenharmony_ci free_work); 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci for (i = 0; i < ZS_SIZE_CLASSES; i++) { 191562306a36Sopenharmony_ci class = pool->size_class[i]; 191662306a36Sopenharmony_ci if (class->index != i) 191762306a36Sopenharmony_ci continue; 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci spin_lock(&pool->lock); 192062306a36Sopenharmony_ci list_splice_init(&class->fullness_list[ZS_INUSE_RATIO_0], 192162306a36Sopenharmony_ci &free_pages); 192262306a36Sopenharmony_ci spin_unlock(&pool->lock); 192362306a36Sopenharmony_ci } 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci list_for_each_entry_safe(zspage, tmp, &free_pages, list) { 192662306a36Sopenharmony_ci list_del(&zspage->list); 192762306a36Sopenharmony_ci lock_zspage(zspage); 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci get_zspage_mapping(zspage, &class_idx, &fullness); 193062306a36Sopenharmony_ci VM_BUG_ON(fullness != ZS_INUSE_RATIO_0); 193162306a36Sopenharmony_ci class = pool->size_class[class_idx]; 193262306a36Sopenharmony_ci spin_lock(&pool->lock); 193362306a36Sopenharmony_ci __free_zspage(pool, class, zspage); 193462306a36Sopenharmony_ci spin_unlock(&pool->lock); 193562306a36Sopenharmony_ci } 193662306a36Sopenharmony_ci}; 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_cistatic void kick_deferred_free(struct zs_pool *pool) 193962306a36Sopenharmony_ci{ 194062306a36Sopenharmony_ci schedule_work(&pool->free_work); 194162306a36Sopenharmony_ci} 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_cistatic void zs_flush_migration(struct zs_pool *pool) 194462306a36Sopenharmony_ci{ 194562306a36Sopenharmony_ci flush_work(&pool->free_work); 194662306a36Sopenharmony_ci} 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_cistatic void init_deferred_free(struct zs_pool *pool) 194962306a36Sopenharmony_ci{ 195062306a36Sopenharmony_ci INIT_WORK(&pool->free_work, async_free_zspage); 195162306a36Sopenharmony_ci} 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_cistatic void SetZsPageMovable(struct zs_pool *pool, struct zspage *zspage) 195462306a36Sopenharmony_ci{ 195562306a36Sopenharmony_ci struct page *page = get_first_page(zspage); 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci do { 195862306a36Sopenharmony_ci WARN_ON(!trylock_page(page)); 195962306a36Sopenharmony_ci __SetPageMovable(page, &zsmalloc_mops); 196062306a36Sopenharmony_ci unlock_page(page); 196162306a36Sopenharmony_ci } while ((page = get_next_page(page)) != NULL); 196262306a36Sopenharmony_ci} 196362306a36Sopenharmony_ci#else 196462306a36Sopenharmony_cistatic inline void zs_flush_migration(struct zs_pool *pool) { } 196562306a36Sopenharmony_ci#endif 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci/* 196862306a36Sopenharmony_ci * 196962306a36Sopenharmony_ci * Based on the number of unused allocated objects calculate 197062306a36Sopenharmony_ci * and return the number of pages that we can free. 197162306a36Sopenharmony_ci */ 197262306a36Sopenharmony_cistatic unsigned long zs_can_compact(struct size_class *class) 197362306a36Sopenharmony_ci{ 197462306a36Sopenharmony_ci unsigned long obj_wasted; 197562306a36Sopenharmony_ci unsigned long obj_allocated = zs_stat_get(class, ZS_OBJS_ALLOCATED); 197662306a36Sopenharmony_ci unsigned long obj_used = zs_stat_get(class, ZS_OBJS_INUSE); 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci if (obj_allocated <= obj_used) 197962306a36Sopenharmony_ci return 0; 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci obj_wasted = obj_allocated - obj_used; 198262306a36Sopenharmony_ci obj_wasted /= class->objs_per_zspage; 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci return obj_wasted * class->pages_per_zspage; 198562306a36Sopenharmony_ci} 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_cistatic unsigned long __zs_compact(struct zs_pool *pool, 198862306a36Sopenharmony_ci struct size_class *class) 198962306a36Sopenharmony_ci{ 199062306a36Sopenharmony_ci struct zspage *src_zspage = NULL; 199162306a36Sopenharmony_ci struct zspage *dst_zspage = NULL; 199262306a36Sopenharmony_ci unsigned long pages_freed = 0; 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci /* 199562306a36Sopenharmony_ci * protect the race between zpage migration and zs_free 199662306a36Sopenharmony_ci * as well as zpage allocation/free 199762306a36Sopenharmony_ci */ 199862306a36Sopenharmony_ci spin_lock(&pool->lock); 199962306a36Sopenharmony_ci while (zs_can_compact(class)) { 200062306a36Sopenharmony_ci int fg; 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci if (!dst_zspage) { 200362306a36Sopenharmony_ci dst_zspage = isolate_dst_zspage(class); 200462306a36Sopenharmony_ci if (!dst_zspage) 200562306a36Sopenharmony_ci break; 200662306a36Sopenharmony_ci migrate_write_lock(dst_zspage); 200762306a36Sopenharmony_ci } 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci src_zspage = isolate_src_zspage(class); 201062306a36Sopenharmony_ci if (!src_zspage) 201162306a36Sopenharmony_ci break; 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci migrate_write_lock_nested(src_zspage); 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci migrate_zspage(pool, src_zspage, dst_zspage); 201662306a36Sopenharmony_ci fg = putback_zspage(class, src_zspage); 201762306a36Sopenharmony_ci migrate_write_unlock(src_zspage); 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci if (fg == ZS_INUSE_RATIO_0) { 202062306a36Sopenharmony_ci free_zspage(pool, class, src_zspage); 202162306a36Sopenharmony_ci pages_freed += class->pages_per_zspage; 202262306a36Sopenharmony_ci } 202362306a36Sopenharmony_ci src_zspage = NULL; 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci if (get_fullness_group(class, dst_zspage) == ZS_INUSE_RATIO_100 202662306a36Sopenharmony_ci || spin_is_contended(&pool->lock)) { 202762306a36Sopenharmony_ci putback_zspage(class, dst_zspage); 202862306a36Sopenharmony_ci migrate_write_unlock(dst_zspage); 202962306a36Sopenharmony_ci dst_zspage = NULL; 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci spin_unlock(&pool->lock); 203262306a36Sopenharmony_ci cond_resched(); 203362306a36Sopenharmony_ci spin_lock(&pool->lock); 203462306a36Sopenharmony_ci } 203562306a36Sopenharmony_ci } 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci if (src_zspage) { 203862306a36Sopenharmony_ci putback_zspage(class, src_zspage); 203962306a36Sopenharmony_ci migrate_write_unlock(src_zspage); 204062306a36Sopenharmony_ci } 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci if (dst_zspage) { 204362306a36Sopenharmony_ci putback_zspage(class, dst_zspage); 204462306a36Sopenharmony_ci migrate_write_unlock(dst_zspage); 204562306a36Sopenharmony_ci } 204662306a36Sopenharmony_ci spin_unlock(&pool->lock); 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci return pages_freed; 204962306a36Sopenharmony_ci} 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ciunsigned long zs_compact(struct zs_pool *pool) 205262306a36Sopenharmony_ci{ 205362306a36Sopenharmony_ci int i; 205462306a36Sopenharmony_ci struct size_class *class; 205562306a36Sopenharmony_ci unsigned long pages_freed = 0; 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci /* 205862306a36Sopenharmony_ci * Pool compaction is performed under pool->lock so it is basically 205962306a36Sopenharmony_ci * single-threaded. Having more than one thread in __zs_compact() 206062306a36Sopenharmony_ci * will increase pool->lock contention, which will impact other 206162306a36Sopenharmony_ci * zsmalloc operations that need pool->lock. 206262306a36Sopenharmony_ci */ 206362306a36Sopenharmony_ci if (atomic_xchg(&pool->compaction_in_progress, 1)) 206462306a36Sopenharmony_ci return 0; 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci for (i = ZS_SIZE_CLASSES - 1; i >= 0; i--) { 206762306a36Sopenharmony_ci class = pool->size_class[i]; 206862306a36Sopenharmony_ci if (class->index != i) 206962306a36Sopenharmony_ci continue; 207062306a36Sopenharmony_ci pages_freed += __zs_compact(pool, class); 207162306a36Sopenharmony_ci } 207262306a36Sopenharmony_ci atomic_long_add(pages_freed, &pool->stats.pages_compacted); 207362306a36Sopenharmony_ci atomic_set(&pool->compaction_in_progress, 0); 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_ci return pages_freed; 207662306a36Sopenharmony_ci} 207762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_compact); 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_civoid zs_pool_stats(struct zs_pool *pool, struct zs_pool_stats *stats) 208062306a36Sopenharmony_ci{ 208162306a36Sopenharmony_ci memcpy(stats, &pool->stats, sizeof(struct zs_pool_stats)); 208262306a36Sopenharmony_ci} 208362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_pool_stats); 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_cistatic unsigned long zs_shrinker_scan(struct shrinker *shrinker, 208662306a36Sopenharmony_ci struct shrink_control *sc) 208762306a36Sopenharmony_ci{ 208862306a36Sopenharmony_ci unsigned long pages_freed; 208962306a36Sopenharmony_ci struct zs_pool *pool = container_of(shrinker, struct zs_pool, 209062306a36Sopenharmony_ci shrinker); 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_ci /* 209362306a36Sopenharmony_ci * Compact classes and calculate compaction delta. 209462306a36Sopenharmony_ci * Can run concurrently with a manually triggered 209562306a36Sopenharmony_ci * (by user) compaction. 209662306a36Sopenharmony_ci */ 209762306a36Sopenharmony_ci pages_freed = zs_compact(pool); 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci return pages_freed ? pages_freed : SHRINK_STOP; 210062306a36Sopenharmony_ci} 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_cistatic unsigned long zs_shrinker_count(struct shrinker *shrinker, 210362306a36Sopenharmony_ci struct shrink_control *sc) 210462306a36Sopenharmony_ci{ 210562306a36Sopenharmony_ci int i; 210662306a36Sopenharmony_ci struct size_class *class; 210762306a36Sopenharmony_ci unsigned long pages_to_free = 0; 210862306a36Sopenharmony_ci struct zs_pool *pool = container_of(shrinker, struct zs_pool, 210962306a36Sopenharmony_ci shrinker); 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci for (i = ZS_SIZE_CLASSES - 1; i >= 0; i--) { 211262306a36Sopenharmony_ci class = pool->size_class[i]; 211362306a36Sopenharmony_ci if (class->index != i) 211462306a36Sopenharmony_ci continue; 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci pages_to_free += zs_can_compact(class); 211762306a36Sopenharmony_ci } 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci return pages_to_free; 212062306a36Sopenharmony_ci} 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_cistatic void zs_unregister_shrinker(struct zs_pool *pool) 212362306a36Sopenharmony_ci{ 212462306a36Sopenharmony_ci unregister_shrinker(&pool->shrinker); 212562306a36Sopenharmony_ci} 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_cistatic int zs_register_shrinker(struct zs_pool *pool) 212862306a36Sopenharmony_ci{ 212962306a36Sopenharmony_ci pool->shrinker.scan_objects = zs_shrinker_scan; 213062306a36Sopenharmony_ci pool->shrinker.count_objects = zs_shrinker_count; 213162306a36Sopenharmony_ci pool->shrinker.batch = 0; 213262306a36Sopenharmony_ci pool->shrinker.seeks = DEFAULT_SEEKS; 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci return register_shrinker(&pool->shrinker, "mm-zspool:%s", 213562306a36Sopenharmony_ci pool->name); 213662306a36Sopenharmony_ci} 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_cistatic int calculate_zspage_chain_size(int class_size) 213962306a36Sopenharmony_ci{ 214062306a36Sopenharmony_ci int i, min_waste = INT_MAX; 214162306a36Sopenharmony_ci int chain_size = 1; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci if (is_power_of_2(class_size)) 214462306a36Sopenharmony_ci return chain_size; 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci for (i = 1; i <= ZS_MAX_PAGES_PER_ZSPAGE; i++) { 214762306a36Sopenharmony_ci int waste; 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci waste = (i * PAGE_SIZE) % class_size; 215062306a36Sopenharmony_ci if (waste < min_waste) { 215162306a36Sopenharmony_ci min_waste = waste; 215262306a36Sopenharmony_ci chain_size = i; 215362306a36Sopenharmony_ci } 215462306a36Sopenharmony_ci } 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci return chain_size; 215762306a36Sopenharmony_ci} 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_ci/** 216062306a36Sopenharmony_ci * zs_create_pool - Creates an allocation pool to work from. 216162306a36Sopenharmony_ci * @name: pool name to be created 216262306a36Sopenharmony_ci * 216362306a36Sopenharmony_ci * This function must be called before anything when using 216462306a36Sopenharmony_ci * the zsmalloc allocator. 216562306a36Sopenharmony_ci * 216662306a36Sopenharmony_ci * On success, a pointer to the newly created pool is returned, 216762306a36Sopenharmony_ci * otherwise NULL. 216862306a36Sopenharmony_ci */ 216962306a36Sopenharmony_cistruct zs_pool *zs_create_pool(const char *name) 217062306a36Sopenharmony_ci{ 217162306a36Sopenharmony_ci int i; 217262306a36Sopenharmony_ci struct zs_pool *pool; 217362306a36Sopenharmony_ci struct size_class *prev_class = NULL; 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_ci pool = kzalloc(sizeof(*pool), GFP_KERNEL); 217662306a36Sopenharmony_ci if (!pool) 217762306a36Sopenharmony_ci return NULL; 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci init_deferred_free(pool); 218062306a36Sopenharmony_ci spin_lock_init(&pool->lock); 218162306a36Sopenharmony_ci atomic_set(&pool->compaction_in_progress, 0); 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci pool->name = kstrdup(name, GFP_KERNEL); 218462306a36Sopenharmony_ci if (!pool->name) 218562306a36Sopenharmony_ci goto err; 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci if (create_cache(pool)) 218862306a36Sopenharmony_ci goto err; 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci /* 219162306a36Sopenharmony_ci * Iterate reversely, because, size of size_class that we want to use 219262306a36Sopenharmony_ci * for merging should be larger or equal to current size. 219362306a36Sopenharmony_ci */ 219462306a36Sopenharmony_ci for (i = ZS_SIZE_CLASSES - 1; i >= 0; i--) { 219562306a36Sopenharmony_ci int size; 219662306a36Sopenharmony_ci int pages_per_zspage; 219762306a36Sopenharmony_ci int objs_per_zspage; 219862306a36Sopenharmony_ci struct size_class *class; 219962306a36Sopenharmony_ci int fullness; 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci size = ZS_MIN_ALLOC_SIZE + i * ZS_SIZE_CLASS_DELTA; 220262306a36Sopenharmony_ci if (size > ZS_MAX_ALLOC_SIZE) 220362306a36Sopenharmony_ci size = ZS_MAX_ALLOC_SIZE; 220462306a36Sopenharmony_ci pages_per_zspage = calculate_zspage_chain_size(size); 220562306a36Sopenharmony_ci objs_per_zspage = pages_per_zspage * PAGE_SIZE / size; 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci /* 220862306a36Sopenharmony_ci * We iterate from biggest down to smallest classes, 220962306a36Sopenharmony_ci * so huge_class_size holds the size of the first huge 221062306a36Sopenharmony_ci * class. Any object bigger than or equal to that will 221162306a36Sopenharmony_ci * endup in the huge class. 221262306a36Sopenharmony_ci */ 221362306a36Sopenharmony_ci if (pages_per_zspage != 1 && objs_per_zspage != 1 && 221462306a36Sopenharmony_ci !huge_class_size) { 221562306a36Sopenharmony_ci huge_class_size = size; 221662306a36Sopenharmony_ci /* 221762306a36Sopenharmony_ci * The object uses ZS_HANDLE_SIZE bytes to store the 221862306a36Sopenharmony_ci * handle. We need to subtract it, because zs_malloc() 221962306a36Sopenharmony_ci * unconditionally adds handle size before it performs 222062306a36Sopenharmony_ci * size class search - so object may be smaller than 222162306a36Sopenharmony_ci * huge class size, yet it still can end up in the huge 222262306a36Sopenharmony_ci * class because it grows by ZS_HANDLE_SIZE extra bytes 222362306a36Sopenharmony_ci * right before class lookup. 222462306a36Sopenharmony_ci */ 222562306a36Sopenharmony_ci huge_class_size -= (ZS_HANDLE_SIZE - 1); 222662306a36Sopenharmony_ci } 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci /* 222962306a36Sopenharmony_ci * size_class is used for normal zsmalloc operation such 223062306a36Sopenharmony_ci * as alloc/free for that size. Although it is natural that we 223162306a36Sopenharmony_ci * have one size_class for each size, there is a chance that we 223262306a36Sopenharmony_ci * can get more memory utilization if we use one size_class for 223362306a36Sopenharmony_ci * many different sizes whose size_class have same 223462306a36Sopenharmony_ci * characteristics. So, we makes size_class point to 223562306a36Sopenharmony_ci * previous size_class if possible. 223662306a36Sopenharmony_ci */ 223762306a36Sopenharmony_ci if (prev_class) { 223862306a36Sopenharmony_ci if (can_merge(prev_class, pages_per_zspage, objs_per_zspage)) { 223962306a36Sopenharmony_ci pool->size_class[i] = prev_class; 224062306a36Sopenharmony_ci continue; 224162306a36Sopenharmony_ci } 224262306a36Sopenharmony_ci } 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_ci class = kzalloc(sizeof(struct size_class), GFP_KERNEL); 224562306a36Sopenharmony_ci if (!class) 224662306a36Sopenharmony_ci goto err; 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci class->size = size; 224962306a36Sopenharmony_ci class->index = i; 225062306a36Sopenharmony_ci class->pages_per_zspage = pages_per_zspage; 225162306a36Sopenharmony_ci class->objs_per_zspage = objs_per_zspage; 225262306a36Sopenharmony_ci pool->size_class[i] = class; 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci fullness = ZS_INUSE_RATIO_0; 225562306a36Sopenharmony_ci while (fullness < NR_FULLNESS_GROUPS) { 225662306a36Sopenharmony_ci INIT_LIST_HEAD(&class->fullness_list[fullness]); 225762306a36Sopenharmony_ci fullness++; 225862306a36Sopenharmony_ci } 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_ci prev_class = class; 226162306a36Sopenharmony_ci } 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci /* debug only, don't abort if it fails */ 226462306a36Sopenharmony_ci zs_pool_stat_create(pool, name); 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci /* 226762306a36Sopenharmony_ci * Not critical since shrinker is only used to trigger internal 226862306a36Sopenharmony_ci * defragmentation of the pool which is pretty optional thing. If 226962306a36Sopenharmony_ci * registration fails we still can use the pool normally and user can 227062306a36Sopenharmony_ci * trigger compaction manually. Thus, ignore return code. 227162306a36Sopenharmony_ci */ 227262306a36Sopenharmony_ci zs_register_shrinker(pool); 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_ci return pool; 227562306a36Sopenharmony_ci 227662306a36Sopenharmony_cierr: 227762306a36Sopenharmony_ci zs_destroy_pool(pool); 227862306a36Sopenharmony_ci return NULL; 227962306a36Sopenharmony_ci} 228062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_create_pool); 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_civoid zs_destroy_pool(struct zs_pool *pool) 228362306a36Sopenharmony_ci{ 228462306a36Sopenharmony_ci int i; 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_ci zs_unregister_shrinker(pool); 228762306a36Sopenharmony_ci zs_flush_migration(pool); 228862306a36Sopenharmony_ci zs_pool_stat_destroy(pool); 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci for (i = 0; i < ZS_SIZE_CLASSES; i++) { 229162306a36Sopenharmony_ci int fg; 229262306a36Sopenharmony_ci struct size_class *class = pool->size_class[i]; 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_ci if (!class) 229562306a36Sopenharmony_ci continue; 229662306a36Sopenharmony_ci 229762306a36Sopenharmony_ci if (class->index != i) 229862306a36Sopenharmony_ci continue; 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_ci for (fg = ZS_INUSE_RATIO_0; fg < NR_FULLNESS_GROUPS; fg++) { 230162306a36Sopenharmony_ci if (list_empty(&class->fullness_list[fg])) 230262306a36Sopenharmony_ci continue; 230362306a36Sopenharmony_ci 230462306a36Sopenharmony_ci pr_err("Class-%d fullness group %d is not empty\n", 230562306a36Sopenharmony_ci class->size, fg); 230662306a36Sopenharmony_ci } 230762306a36Sopenharmony_ci kfree(class); 230862306a36Sopenharmony_ci } 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci destroy_cache(pool); 231162306a36Sopenharmony_ci kfree(pool->name); 231262306a36Sopenharmony_ci kfree(pool); 231362306a36Sopenharmony_ci} 231462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_destroy_pool); 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_cistatic int __init zs_init(void) 231762306a36Sopenharmony_ci{ 231862306a36Sopenharmony_ci int ret; 231962306a36Sopenharmony_ci 232062306a36Sopenharmony_ci ret = cpuhp_setup_state(CPUHP_MM_ZS_PREPARE, "mm/zsmalloc:prepare", 232162306a36Sopenharmony_ci zs_cpu_prepare, zs_cpu_dead); 232262306a36Sopenharmony_ci if (ret) 232362306a36Sopenharmony_ci goto out; 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_ci#ifdef CONFIG_ZPOOL 232662306a36Sopenharmony_ci zpool_register_driver(&zs_zpool_driver); 232762306a36Sopenharmony_ci#endif 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci zs_stat_init(); 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_ci return 0; 233262306a36Sopenharmony_ci 233362306a36Sopenharmony_ciout: 233462306a36Sopenharmony_ci return ret; 233562306a36Sopenharmony_ci} 233662306a36Sopenharmony_ci 233762306a36Sopenharmony_cistatic void __exit zs_exit(void) 233862306a36Sopenharmony_ci{ 233962306a36Sopenharmony_ci#ifdef CONFIG_ZPOOL 234062306a36Sopenharmony_ci zpool_unregister_driver(&zs_zpool_driver); 234162306a36Sopenharmony_ci#endif 234262306a36Sopenharmony_ci cpuhp_remove_state(CPUHP_MM_ZS_PREPARE); 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci zs_stat_exit(); 234562306a36Sopenharmony_ci} 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_cimodule_init(zs_init); 234862306a36Sopenharmony_cimodule_exit(zs_exit); 234962306a36Sopenharmony_ci 235062306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 235162306a36Sopenharmony_ciMODULE_AUTHOR("Nitin Gupta <ngupta@vflare.org>"); 2352