18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * zsmalloc memory allocator 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2011 Nitin Gupta 58c2ecf20Sopenharmony_ci * Copyright (C) 2012, 2013 Minchan Kim 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This code is released using a dual license strategy: BSD/GPL 88c2ecf20Sopenharmony_ci * You can choose the license that better fits your requirements. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Released under the terms of 3-clause BSD License 118c2ecf20Sopenharmony_ci * Released under the terms of GNU General Public License Version 2.0 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* 158c2ecf20Sopenharmony_ci * Following is how we use various fields and flags of underlying 168c2ecf20Sopenharmony_ci * struct page(s) to form a zspage. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * Usage of struct page fields: 198c2ecf20Sopenharmony_ci * page->private: points to zspage 208c2ecf20Sopenharmony_ci * page->freelist(index): links together all component pages of a zspage 218c2ecf20Sopenharmony_ci * For the huge page, this is always 0, so we use this field 228c2ecf20Sopenharmony_ci * to store handle. 238c2ecf20Sopenharmony_ci * page->units: first object offset in a subpage of zspage 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Usage of struct page flags: 268c2ecf20Sopenharmony_ci * PG_private: identifies the first component page 278c2ecf20Sopenharmony_ci * PG_owner_priv_1: identifies the huge component page 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <linux/module.h> 348c2ecf20Sopenharmony_ci#include <linux/kernel.h> 358c2ecf20Sopenharmony_ci#include <linux/sched.h> 368c2ecf20Sopenharmony_ci#include <linux/magic.h> 378c2ecf20Sopenharmony_ci#include <linux/bitops.h> 388c2ecf20Sopenharmony_ci#include <linux/errno.h> 398c2ecf20Sopenharmony_ci#include <linux/highmem.h> 408c2ecf20Sopenharmony_ci#include <linux/string.h> 418c2ecf20Sopenharmony_ci#include <linux/slab.h> 428c2ecf20Sopenharmony_ci#include <linux/pgtable.h> 438c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 448c2ecf20Sopenharmony_ci#include <linux/cpumask.h> 458c2ecf20Sopenharmony_ci#include <linux/cpu.h> 468c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 478c2ecf20Sopenharmony_ci#include <linux/preempt.h> 488c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 498c2ecf20Sopenharmony_ci#include <linux/shrinker.h> 508c2ecf20Sopenharmony_ci#include <linux/types.h> 518c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 528c2ecf20Sopenharmony_ci#include <linux/zsmalloc.h> 538c2ecf20Sopenharmony_ci#include <linux/zpool.h> 548c2ecf20Sopenharmony_ci#include <linux/mount.h> 558c2ecf20Sopenharmony_ci#include <linux/pseudo_fs.h> 568c2ecf20Sopenharmony_ci#include <linux/migrate.h> 578c2ecf20Sopenharmony_ci#include <linux/wait.h> 588c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 598c2ecf20Sopenharmony_ci#include <linux/fs.h> 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define ZSPAGE_MAGIC 0x58 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* 648c2ecf20Sopenharmony_ci * This must be power of 2 and greater than of equal to sizeof(link_free). 658c2ecf20Sopenharmony_ci * These two conditions ensure that any 'struct link_free' itself doesn't 668c2ecf20Sopenharmony_ci * span more than 1 page which avoids complex case of mapping 2 pages simply 678c2ecf20Sopenharmony_ci * to restore link_free pointer values. 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci#define ZS_ALIGN 8 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 728c2ecf20Sopenharmony_ci * A single 'zspage' is composed of up to 2^N discontiguous 0-order (single) 738c2ecf20Sopenharmony_ci * pages. ZS_MAX_ZSPAGE_ORDER defines upper limit on N. 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci#define ZS_MAX_ZSPAGE_ORDER 2 768c2ecf20Sopenharmony_ci#define ZS_MAX_PAGES_PER_ZSPAGE (_AC(1, UL) << ZS_MAX_ZSPAGE_ORDER) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define ZS_HANDLE_SIZE (sizeof(unsigned long)) 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* 818c2ecf20Sopenharmony_ci * Object location (<PFN>, <obj_idx>) is encoded as 828c2ecf20Sopenharmony_ci * a single (unsigned long) handle value. 838c2ecf20Sopenharmony_ci * 848c2ecf20Sopenharmony_ci * Note that object index <obj_idx> starts from 0. 858c2ecf20Sopenharmony_ci * 868c2ecf20Sopenharmony_ci * This is made more complicated by various memory models and PAE. 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#ifndef MAX_POSSIBLE_PHYSMEM_BITS 908c2ecf20Sopenharmony_ci#ifdef MAX_PHYSMEM_BITS 918c2ecf20Sopenharmony_ci#define MAX_POSSIBLE_PHYSMEM_BITS MAX_PHYSMEM_BITS 928c2ecf20Sopenharmony_ci#else 938c2ecf20Sopenharmony_ci/* 948c2ecf20Sopenharmony_ci * If this definition of MAX_PHYSMEM_BITS is used, OBJ_INDEX_BITS will just 958c2ecf20Sopenharmony_ci * be PAGE_SHIFT 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci#define MAX_POSSIBLE_PHYSMEM_BITS BITS_PER_LONG 988c2ecf20Sopenharmony_ci#endif 998c2ecf20Sopenharmony_ci#endif 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#define _PFN_BITS (MAX_POSSIBLE_PHYSMEM_BITS - PAGE_SHIFT) 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* 1048c2ecf20Sopenharmony_ci * Memory for allocating for handle keeps object position by 1058c2ecf20Sopenharmony_ci * encoding <page, obj_idx> and the encoded value has a room 1068c2ecf20Sopenharmony_ci * in least bit(ie, look at obj_to_location). 1078c2ecf20Sopenharmony_ci * We use the bit to synchronize between object access by 1088c2ecf20Sopenharmony_ci * user and migration. 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ci#define HANDLE_PIN_BIT 0 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* 1138c2ecf20Sopenharmony_ci * Head in allocated object should have OBJ_ALLOCATED_TAG 1148c2ecf20Sopenharmony_ci * to identify the object was allocated or not. 1158c2ecf20Sopenharmony_ci * It's okay to add the status bit in the least bit because 1168c2ecf20Sopenharmony_ci * header keeps handle which is 4byte-aligned address so we 1178c2ecf20Sopenharmony_ci * have room for two bit at least. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_ci#define OBJ_ALLOCATED_TAG 1 1208c2ecf20Sopenharmony_ci#define OBJ_TAG_BITS 1 1218c2ecf20Sopenharmony_ci#define OBJ_INDEX_BITS (BITS_PER_LONG - _PFN_BITS - OBJ_TAG_BITS) 1228c2ecf20Sopenharmony_ci#define OBJ_INDEX_MASK ((_AC(1, UL) << OBJ_INDEX_BITS) - 1) 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci#define FULLNESS_BITS 2 1258c2ecf20Sopenharmony_ci#define CLASS_BITS 8 1268c2ecf20Sopenharmony_ci#define ISOLATED_BITS 3 1278c2ecf20Sopenharmony_ci#define MAGIC_VAL_BITS 8 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci#define MAX(a, b) ((a) >= (b) ? (a) : (b)) 1308c2ecf20Sopenharmony_ci/* ZS_MIN_ALLOC_SIZE must be multiple of ZS_ALIGN */ 1318c2ecf20Sopenharmony_ci#define ZS_MIN_ALLOC_SIZE \ 1328c2ecf20Sopenharmony_ci MAX(32, (ZS_MAX_PAGES_PER_ZSPAGE << PAGE_SHIFT >> OBJ_INDEX_BITS)) 1338c2ecf20Sopenharmony_ci/* each chunk includes extra space to keep handle */ 1348c2ecf20Sopenharmony_ci#define ZS_MAX_ALLOC_SIZE PAGE_SIZE 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* 1378c2ecf20Sopenharmony_ci * On systems with 4K page size, this gives 255 size classes! There is a 1388c2ecf20Sopenharmony_ci * trader-off here: 1398c2ecf20Sopenharmony_ci * - Large number of size classes is potentially wasteful as free page are 1408c2ecf20Sopenharmony_ci * spread across these classes 1418c2ecf20Sopenharmony_ci * - Small number of size classes causes large internal fragmentation 1428c2ecf20Sopenharmony_ci * - Probably its better to use specific size classes (empirically 1438c2ecf20Sopenharmony_ci * determined). NOTE: all those class sizes must be set as multiple of 1448c2ecf20Sopenharmony_ci * ZS_ALIGN to make sure link_free itself never has to span 2 pages. 1458c2ecf20Sopenharmony_ci * 1468c2ecf20Sopenharmony_ci * ZS_MIN_ALLOC_SIZE and ZS_SIZE_CLASS_DELTA must be multiple of ZS_ALIGN 1478c2ecf20Sopenharmony_ci * (reason above) 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_ci#define ZS_SIZE_CLASS_DELTA (PAGE_SIZE >> CLASS_BITS) 1508c2ecf20Sopenharmony_ci#define ZS_SIZE_CLASSES (DIV_ROUND_UP(ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE, \ 1518c2ecf20Sopenharmony_ci ZS_SIZE_CLASS_DELTA) + 1) 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cienum fullness_group { 1548c2ecf20Sopenharmony_ci ZS_EMPTY, 1558c2ecf20Sopenharmony_ci ZS_ALMOST_EMPTY, 1568c2ecf20Sopenharmony_ci ZS_ALMOST_FULL, 1578c2ecf20Sopenharmony_ci ZS_FULL, 1588c2ecf20Sopenharmony_ci NR_ZS_FULLNESS, 1598c2ecf20Sopenharmony_ci}; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cienum zs_stat_type { 1628c2ecf20Sopenharmony_ci CLASS_EMPTY, 1638c2ecf20Sopenharmony_ci CLASS_ALMOST_EMPTY, 1648c2ecf20Sopenharmony_ci CLASS_ALMOST_FULL, 1658c2ecf20Sopenharmony_ci CLASS_FULL, 1668c2ecf20Sopenharmony_ci OBJ_ALLOCATED, 1678c2ecf20Sopenharmony_ci OBJ_USED, 1688c2ecf20Sopenharmony_ci NR_ZS_STAT_TYPE, 1698c2ecf20Sopenharmony_ci}; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistruct zs_size_stat { 1728c2ecf20Sopenharmony_ci unsigned long objs[NR_ZS_STAT_TYPE]; 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci#ifdef CONFIG_ZSMALLOC_STAT 1768c2ecf20Sopenharmony_cistatic struct dentry *zs_stat_root; 1778c2ecf20Sopenharmony_ci#endif 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPACTION 1808c2ecf20Sopenharmony_cistatic struct vfsmount *zsmalloc_mnt; 1818c2ecf20Sopenharmony_ci#endif 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci/* 1848c2ecf20Sopenharmony_ci * We assign a page to ZS_ALMOST_EMPTY fullness group when: 1858c2ecf20Sopenharmony_ci * n <= N / f, where 1868c2ecf20Sopenharmony_ci * n = number of allocated objects 1878c2ecf20Sopenharmony_ci * N = total number of objects zspage can store 1888c2ecf20Sopenharmony_ci * f = fullness_threshold_frac 1898c2ecf20Sopenharmony_ci * 1908c2ecf20Sopenharmony_ci * Similarly, we assign zspage to: 1918c2ecf20Sopenharmony_ci * ZS_ALMOST_FULL when n > N / f 1928c2ecf20Sopenharmony_ci * ZS_EMPTY when n == 0 1938c2ecf20Sopenharmony_ci * ZS_FULL when n == N 1948c2ecf20Sopenharmony_ci * 1958c2ecf20Sopenharmony_ci * (see: fix_fullness_group()) 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_cistatic const int fullness_threshold_frac = 4; 1988c2ecf20Sopenharmony_cistatic size_t huge_class_size; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistruct size_class { 2018c2ecf20Sopenharmony_ci spinlock_t lock; 2028c2ecf20Sopenharmony_ci struct list_head fullness_list[NR_ZS_FULLNESS]; 2038c2ecf20Sopenharmony_ci /* 2048c2ecf20Sopenharmony_ci * Size of objects stored in this class. Must be multiple 2058c2ecf20Sopenharmony_ci * of ZS_ALIGN. 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_ci int size; 2088c2ecf20Sopenharmony_ci int objs_per_zspage; 2098c2ecf20Sopenharmony_ci /* Number of PAGE_SIZE sized pages to combine to form a 'zspage' */ 2108c2ecf20Sopenharmony_ci int pages_per_zspage; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci unsigned int index; 2138c2ecf20Sopenharmony_ci struct zs_size_stat stats; 2148c2ecf20Sopenharmony_ci}; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* huge object: pages_per_zspage == 1 && maxobj_per_zspage == 1 */ 2178c2ecf20Sopenharmony_cistatic void SetPageHugeObject(struct page *page) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci SetPageOwnerPriv1(page); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic void ClearPageHugeObject(struct page *page) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci ClearPageOwnerPriv1(page); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic int PageHugeObject(struct page *page) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci return PageOwnerPriv1(page); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/* 2338c2ecf20Sopenharmony_ci * Placed within free objects to form a singly linked list. 2348c2ecf20Sopenharmony_ci * For every zspage, zspage->freeobj gives head of this list. 2358c2ecf20Sopenharmony_ci * 2368c2ecf20Sopenharmony_ci * This must be power of 2 and less than or equal to ZS_ALIGN 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_cistruct link_free { 2398c2ecf20Sopenharmony_ci union { 2408c2ecf20Sopenharmony_ci /* 2418c2ecf20Sopenharmony_ci * Free object index; 2428c2ecf20Sopenharmony_ci * It's valid for non-allocated object 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_ci unsigned long next; 2458c2ecf20Sopenharmony_ci /* 2468c2ecf20Sopenharmony_ci * Handle of allocated object. 2478c2ecf20Sopenharmony_ci */ 2488c2ecf20Sopenharmony_ci unsigned long handle; 2498c2ecf20Sopenharmony_ci }; 2508c2ecf20Sopenharmony_ci}; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistruct zs_pool { 2538c2ecf20Sopenharmony_ci const char *name; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci struct size_class *size_class[ZS_SIZE_CLASSES]; 2568c2ecf20Sopenharmony_ci struct kmem_cache *handle_cachep; 2578c2ecf20Sopenharmony_ci struct kmem_cache *zspage_cachep; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci atomic_long_t pages_allocated; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci struct zs_pool_stats stats; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* Compact classes */ 2648c2ecf20Sopenharmony_ci struct shrinker shrinker; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci#ifdef CONFIG_ZSMALLOC_STAT 2678c2ecf20Sopenharmony_ci struct dentry *stat_dentry; 2688c2ecf20Sopenharmony_ci#endif 2698c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPACTION 2708c2ecf20Sopenharmony_ci struct inode *inode; 2718c2ecf20Sopenharmony_ci struct work_struct free_work; 2728c2ecf20Sopenharmony_ci /* A wait queue for when migration races with async_free_zspage() */ 2738c2ecf20Sopenharmony_ci struct wait_queue_head migration_wait; 2748c2ecf20Sopenharmony_ci atomic_long_t isolated_pages; 2758c2ecf20Sopenharmony_ci bool destroying; 2768c2ecf20Sopenharmony_ci#endif 2778c2ecf20Sopenharmony_ci}; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistruct zspage { 2808c2ecf20Sopenharmony_ci struct { 2818c2ecf20Sopenharmony_ci unsigned int fullness:FULLNESS_BITS; 2828c2ecf20Sopenharmony_ci unsigned int class:CLASS_BITS + 1; 2838c2ecf20Sopenharmony_ci unsigned int isolated:ISOLATED_BITS; 2848c2ecf20Sopenharmony_ci unsigned int magic:MAGIC_VAL_BITS; 2858c2ecf20Sopenharmony_ci }; 2868c2ecf20Sopenharmony_ci unsigned int inuse; 2878c2ecf20Sopenharmony_ci unsigned int freeobj; 2888c2ecf20Sopenharmony_ci struct page *first_page; 2898c2ecf20Sopenharmony_ci struct list_head list; /* fullness list */ 2908c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPACTION 2918c2ecf20Sopenharmony_ci rwlock_t lock; 2928c2ecf20Sopenharmony_ci#endif 2938c2ecf20Sopenharmony_ci}; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistruct mapping_area { 2968c2ecf20Sopenharmony_ci char *vm_buf; /* copy buffer for objects that span pages */ 2978c2ecf20Sopenharmony_ci char *vm_addr; /* address of kmap_atomic()'ed pages */ 2988c2ecf20Sopenharmony_ci enum zs_mapmode vm_mm; /* mapping mode */ 2998c2ecf20Sopenharmony_ci}; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPACTION 3028c2ecf20Sopenharmony_cistatic int zs_register_migration(struct zs_pool *pool); 3038c2ecf20Sopenharmony_cistatic void zs_unregister_migration(struct zs_pool *pool); 3048c2ecf20Sopenharmony_cistatic void migrate_lock_init(struct zspage *zspage); 3058c2ecf20Sopenharmony_cistatic void migrate_read_lock(struct zspage *zspage); 3068c2ecf20Sopenharmony_cistatic void migrate_read_unlock(struct zspage *zspage); 3078c2ecf20Sopenharmony_cistatic void kick_deferred_free(struct zs_pool *pool); 3088c2ecf20Sopenharmony_cistatic void init_deferred_free(struct zs_pool *pool); 3098c2ecf20Sopenharmony_cistatic void SetZsPageMovable(struct zs_pool *pool, struct zspage *zspage); 3108c2ecf20Sopenharmony_ci#else 3118c2ecf20Sopenharmony_cistatic int zsmalloc_mount(void) { return 0; } 3128c2ecf20Sopenharmony_cistatic void zsmalloc_unmount(void) {} 3138c2ecf20Sopenharmony_cistatic int zs_register_migration(struct zs_pool *pool) { return 0; } 3148c2ecf20Sopenharmony_cistatic void zs_unregister_migration(struct zs_pool *pool) {} 3158c2ecf20Sopenharmony_cistatic void migrate_lock_init(struct zspage *zspage) {} 3168c2ecf20Sopenharmony_cistatic void migrate_read_lock(struct zspage *zspage) {} 3178c2ecf20Sopenharmony_cistatic void migrate_read_unlock(struct zspage *zspage) {} 3188c2ecf20Sopenharmony_cistatic void kick_deferred_free(struct zs_pool *pool) {} 3198c2ecf20Sopenharmony_cistatic void init_deferred_free(struct zs_pool *pool) {} 3208c2ecf20Sopenharmony_cistatic void SetZsPageMovable(struct zs_pool *pool, struct zspage *zspage) {} 3218c2ecf20Sopenharmony_ci#endif 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic int create_cache(struct zs_pool *pool) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci pool->handle_cachep = kmem_cache_create("zs_handle", ZS_HANDLE_SIZE, 3268c2ecf20Sopenharmony_ci 0, 0, NULL); 3278c2ecf20Sopenharmony_ci if (!pool->handle_cachep) 3288c2ecf20Sopenharmony_ci return 1; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci pool->zspage_cachep = kmem_cache_create("zspage", sizeof(struct zspage), 3318c2ecf20Sopenharmony_ci 0, 0, NULL); 3328c2ecf20Sopenharmony_ci if (!pool->zspage_cachep) { 3338c2ecf20Sopenharmony_ci kmem_cache_destroy(pool->handle_cachep); 3348c2ecf20Sopenharmony_ci pool->handle_cachep = NULL; 3358c2ecf20Sopenharmony_ci return 1; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci return 0; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic void destroy_cache(struct zs_pool *pool) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci kmem_cache_destroy(pool->handle_cachep); 3448c2ecf20Sopenharmony_ci kmem_cache_destroy(pool->zspage_cachep); 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic unsigned long cache_alloc_handle(struct zs_pool *pool, gfp_t gfp) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci return (unsigned long)kmem_cache_alloc(pool->handle_cachep, 3508c2ecf20Sopenharmony_ci gfp & ~(__GFP_HIGHMEM|__GFP_MOVABLE)); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic void cache_free_handle(struct zs_pool *pool, unsigned long handle) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci kmem_cache_free(pool->handle_cachep, (void *)handle); 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic struct zspage *cache_alloc_zspage(struct zs_pool *pool, gfp_t flags) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci return kmem_cache_alloc(pool->zspage_cachep, 3618c2ecf20Sopenharmony_ci flags & ~(__GFP_HIGHMEM|__GFP_MOVABLE)); 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic void cache_free_zspage(struct zs_pool *pool, struct zspage *zspage) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci kmem_cache_free(pool->zspage_cachep, zspage); 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic void record_obj(unsigned long handle, unsigned long obj) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci /* 3728c2ecf20Sopenharmony_ci * lsb of @obj represents handle lock while other bits 3738c2ecf20Sopenharmony_ci * represent object value the handle is pointing so 3748c2ecf20Sopenharmony_ci * updating shouldn't do store tearing. 3758c2ecf20Sopenharmony_ci */ 3768c2ecf20Sopenharmony_ci WRITE_ONCE(*(unsigned long *)handle, obj); 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci/* zpool driver */ 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci#ifdef CONFIG_ZPOOL 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic void *zs_zpool_create(const char *name, gfp_t gfp, 3848c2ecf20Sopenharmony_ci const struct zpool_ops *zpool_ops, 3858c2ecf20Sopenharmony_ci struct zpool *zpool) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci /* 3888c2ecf20Sopenharmony_ci * Ignore global gfp flags: zs_malloc() may be invoked from 3898c2ecf20Sopenharmony_ci * different contexts and its caller must provide a valid 3908c2ecf20Sopenharmony_ci * gfp mask. 3918c2ecf20Sopenharmony_ci */ 3928c2ecf20Sopenharmony_ci return zs_create_pool(name); 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic void zs_zpool_destroy(void *pool) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci zs_destroy_pool(pool); 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic int zs_zpool_malloc(void *pool, size_t size, gfp_t gfp, 4018c2ecf20Sopenharmony_ci unsigned long *handle) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci *handle = zs_malloc(pool, size, gfp); 4048c2ecf20Sopenharmony_ci return *handle ? 0 : -1; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_cistatic void zs_zpool_free(void *pool, unsigned long handle) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci zs_free(pool, handle); 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic void *zs_zpool_map(void *pool, unsigned long handle, 4128c2ecf20Sopenharmony_ci enum zpool_mapmode mm) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci enum zs_mapmode zs_mm; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci switch (mm) { 4178c2ecf20Sopenharmony_ci case ZPOOL_MM_RO: 4188c2ecf20Sopenharmony_ci zs_mm = ZS_MM_RO; 4198c2ecf20Sopenharmony_ci break; 4208c2ecf20Sopenharmony_ci case ZPOOL_MM_WO: 4218c2ecf20Sopenharmony_ci zs_mm = ZS_MM_WO; 4228c2ecf20Sopenharmony_ci break; 4238c2ecf20Sopenharmony_ci case ZPOOL_MM_RW: 4248c2ecf20Sopenharmony_ci default: 4258c2ecf20Sopenharmony_ci zs_mm = ZS_MM_RW; 4268c2ecf20Sopenharmony_ci break; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return zs_map_object(pool, handle, zs_mm); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_cistatic void zs_zpool_unmap(void *pool, unsigned long handle) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci zs_unmap_object(pool, handle); 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic u64 zs_zpool_total_size(void *pool) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci return zs_get_total_pages(pool) << PAGE_SHIFT; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic struct zpool_driver zs_zpool_driver = { 4428c2ecf20Sopenharmony_ci .type = "zsmalloc", 4438c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4448c2ecf20Sopenharmony_ci .create = zs_zpool_create, 4458c2ecf20Sopenharmony_ci .destroy = zs_zpool_destroy, 4468c2ecf20Sopenharmony_ci .malloc_support_movable = true, 4478c2ecf20Sopenharmony_ci .malloc = zs_zpool_malloc, 4488c2ecf20Sopenharmony_ci .free = zs_zpool_free, 4498c2ecf20Sopenharmony_ci .map = zs_zpool_map, 4508c2ecf20Sopenharmony_ci .unmap = zs_zpool_unmap, 4518c2ecf20Sopenharmony_ci .total_size = zs_zpool_total_size, 4528c2ecf20Sopenharmony_ci}; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ciMODULE_ALIAS("zpool-zsmalloc"); 4558c2ecf20Sopenharmony_ci#endif /* CONFIG_ZPOOL */ 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci/* per-cpu VM mapping areas for zspage accesses that cross page boundaries */ 4588c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct mapping_area, zs_map_area); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic bool is_zspage_isolated(struct zspage *zspage) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci return zspage->isolated; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic __maybe_unused int is_first_page(struct page *page) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci return PagePrivate(page); 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci/* Protected by class->lock */ 4718c2ecf20Sopenharmony_cistatic inline int get_zspage_inuse(struct zspage *zspage) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci return zspage->inuse; 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic inline void mod_zspage_inuse(struct zspage *zspage, int val) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci zspage->inuse += val; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic inline struct page *get_first_page(struct zspage *zspage) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct page *first_page = zspage->first_page; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci VM_BUG_ON_PAGE(!is_first_page(first_page), first_page); 4878c2ecf20Sopenharmony_ci return first_page; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic inline int get_first_obj_offset(struct page *page) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci return page->units; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic inline void set_first_obj_offset(struct page *page, int offset) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci page->units = offset; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic inline unsigned int get_freeobj(struct zspage *zspage) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci return zspage->freeobj; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic inline void set_freeobj(struct zspage *zspage, unsigned int obj) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci zspage->freeobj = obj; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic void get_zspage_mapping(struct zspage *zspage, 5118c2ecf20Sopenharmony_ci unsigned int *class_idx, 5128c2ecf20Sopenharmony_ci enum fullness_group *fullness) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci BUG_ON(zspage->magic != ZSPAGE_MAGIC); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci *fullness = zspage->fullness; 5178c2ecf20Sopenharmony_ci *class_idx = zspage->class; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic void set_zspage_mapping(struct zspage *zspage, 5218c2ecf20Sopenharmony_ci unsigned int class_idx, 5228c2ecf20Sopenharmony_ci enum fullness_group fullness) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci zspage->class = class_idx; 5258c2ecf20Sopenharmony_ci zspage->fullness = fullness; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci/* 5298c2ecf20Sopenharmony_ci * zsmalloc divides the pool into various size classes where each 5308c2ecf20Sopenharmony_ci * class maintains a list of zspages where each zspage is divided 5318c2ecf20Sopenharmony_ci * into equal sized chunks. Each allocation falls into one of these 5328c2ecf20Sopenharmony_ci * classes depending on its size. This function returns index of the 5338c2ecf20Sopenharmony_ci * size class which has chunk size big enough to hold the give size. 5348c2ecf20Sopenharmony_ci */ 5358c2ecf20Sopenharmony_cistatic int get_size_class_index(int size) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci int idx = 0; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if (likely(size > ZS_MIN_ALLOC_SIZE)) 5408c2ecf20Sopenharmony_ci idx = DIV_ROUND_UP(size - ZS_MIN_ALLOC_SIZE, 5418c2ecf20Sopenharmony_ci ZS_SIZE_CLASS_DELTA); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci return min_t(int, ZS_SIZE_CLASSES - 1, idx); 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci/* type can be of enum type zs_stat_type or fullness_group */ 5478c2ecf20Sopenharmony_cistatic inline void zs_stat_inc(struct size_class *class, 5488c2ecf20Sopenharmony_ci int type, unsigned long cnt) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci class->stats.objs[type] += cnt; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci/* type can be of enum type zs_stat_type or fullness_group */ 5548c2ecf20Sopenharmony_cistatic inline void zs_stat_dec(struct size_class *class, 5558c2ecf20Sopenharmony_ci int type, unsigned long cnt) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci class->stats.objs[type] -= cnt; 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci/* type can be of enum type zs_stat_type or fullness_group */ 5618c2ecf20Sopenharmony_cistatic inline unsigned long zs_stat_get(struct size_class *class, 5628c2ecf20Sopenharmony_ci int type) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci return class->stats.objs[type]; 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci#ifdef CONFIG_ZSMALLOC_STAT 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic void __init zs_stat_init(void) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci if (!debugfs_initialized()) { 5728c2ecf20Sopenharmony_ci pr_warn("debugfs not available, stat dir not created\n"); 5738c2ecf20Sopenharmony_ci return; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci zs_stat_root = debugfs_create_dir("zsmalloc", NULL); 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic void __exit zs_stat_exit(void) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci debugfs_remove_recursive(zs_stat_root); 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic unsigned long zs_can_compact(struct size_class *class); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_cistatic int zs_stats_size_show(struct seq_file *s, void *v) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci int i; 5898c2ecf20Sopenharmony_ci struct zs_pool *pool = s->private; 5908c2ecf20Sopenharmony_ci struct size_class *class; 5918c2ecf20Sopenharmony_ci int objs_per_zspage; 5928c2ecf20Sopenharmony_ci unsigned long class_almost_full, class_almost_empty; 5938c2ecf20Sopenharmony_ci unsigned long obj_allocated, obj_used, pages_used, freeable; 5948c2ecf20Sopenharmony_ci unsigned long total_class_almost_full = 0, total_class_almost_empty = 0; 5958c2ecf20Sopenharmony_ci unsigned long total_objs = 0, total_used_objs = 0, total_pages = 0; 5968c2ecf20Sopenharmony_ci unsigned long total_freeable = 0; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci seq_printf(s, " %5s %5s %11s %12s %13s %10s %10s %16s %8s\n", 5998c2ecf20Sopenharmony_ci "class", "size", "almost_full", "almost_empty", 6008c2ecf20Sopenharmony_ci "obj_allocated", "obj_used", "pages_used", 6018c2ecf20Sopenharmony_ci "pages_per_zspage", "freeable"); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci for (i = 0; i < ZS_SIZE_CLASSES; i++) { 6048c2ecf20Sopenharmony_ci class = pool->size_class[i]; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci if (class->index != i) 6078c2ecf20Sopenharmony_ci continue; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci spin_lock(&class->lock); 6108c2ecf20Sopenharmony_ci class_almost_full = zs_stat_get(class, CLASS_ALMOST_FULL); 6118c2ecf20Sopenharmony_ci class_almost_empty = zs_stat_get(class, CLASS_ALMOST_EMPTY); 6128c2ecf20Sopenharmony_ci obj_allocated = zs_stat_get(class, OBJ_ALLOCATED); 6138c2ecf20Sopenharmony_ci obj_used = zs_stat_get(class, OBJ_USED); 6148c2ecf20Sopenharmony_ci freeable = zs_can_compact(class); 6158c2ecf20Sopenharmony_ci spin_unlock(&class->lock); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci objs_per_zspage = class->objs_per_zspage; 6188c2ecf20Sopenharmony_ci pages_used = obj_allocated / objs_per_zspage * 6198c2ecf20Sopenharmony_ci class->pages_per_zspage; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci seq_printf(s, " %5u %5u %11lu %12lu %13lu" 6228c2ecf20Sopenharmony_ci " %10lu %10lu %16d %8lu\n", 6238c2ecf20Sopenharmony_ci i, class->size, class_almost_full, class_almost_empty, 6248c2ecf20Sopenharmony_ci obj_allocated, obj_used, pages_used, 6258c2ecf20Sopenharmony_ci class->pages_per_zspage, freeable); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci total_class_almost_full += class_almost_full; 6288c2ecf20Sopenharmony_ci total_class_almost_empty += class_almost_empty; 6298c2ecf20Sopenharmony_ci total_objs += obj_allocated; 6308c2ecf20Sopenharmony_ci total_used_objs += obj_used; 6318c2ecf20Sopenharmony_ci total_pages += pages_used; 6328c2ecf20Sopenharmony_ci total_freeable += freeable; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci seq_puts(s, "\n"); 6368c2ecf20Sopenharmony_ci seq_printf(s, " %5s %5s %11lu %12lu %13lu %10lu %10lu %16s %8lu\n", 6378c2ecf20Sopenharmony_ci "Total", "", total_class_almost_full, 6388c2ecf20Sopenharmony_ci total_class_almost_empty, total_objs, 6398c2ecf20Sopenharmony_ci total_used_objs, total_pages, "", total_freeable); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci return 0; 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(zs_stats_size); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic void zs_pool_stat_create(struct zs_pool *pool, const char *name) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci if (!zs_stat_root) { 6488c2ecf20Sopenharmony_ci pr_warn("no root stat dir, not creating <%s> stat dir\n", name); 6498c2ecf20Sopenharmony_ci return; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci pool->stat_dentry = debugfs_create_dir(name, zs_stat_root); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci debugfs_create_file("classes", S_IFREG | 0444, pool->stat_dentry, pool, 6558c2ecf20Sopenharmony_ci &zs_stats_size_fops); 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic void zs_pool_stat_destroy(struct zs_pool *pool) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci debugfs_remove_recursive(pool->stat_dentry); 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci#else /* CONFIG_ZSMALLOC_STAT */ 6648c2ecf20Sopenharmony_cistatic void __init zs_stat_init(void) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic void __exit zs_stat_exit(void) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic inline void zs_pool_stat_create(struct zs_pool *pool, const char *name) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic inline void zs_pool_stat_destroy(struct zs_pool *pool) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci#endif 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci/* 6838c2ecf20Sopenharmony_ci * For each size class, zspages are divided into different groups 6848c2ecf20Sopenharmony_ci * depending on how "full" they are. This was done so that we could 6858c2ecf20Sopenharmony_ci * easily find empty or nearly empty zspages when we try to shrink 6868c2ecf20Sopenharmony_ci * the pool (not yet implemented). This function returns fullness 6878c2ecf20Sopenharmony_ci * status of the given page. 6888c2ecf20Sopenharmony_ci */ 6898c2ecf20Sopenharmony_cistatic enum fullness_group get_fullness_group(struct size_class *class, 6908c2ecf20Sopenharmony_ci struct zspage *zspage) 6918c2ecf20Sopenharmony_ci{ 6928c2ecf20Sopenharmony_ci int inuse, objs_per_zspage; 6938c2ecf20Sopenharmony_ci enum fullness_group fg; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci inuse = get_zspage_inuse(zspage); 6968c2ecf20Sopenharmony_ci objs_per_zspage = class->objs_per_zspage; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci if (inuse == 0) 6998c2ecf20Sopenharmony_ci fg = ZS_EMPTY; 7008c2ecf20Sopenharmony_ci else if (inuse == objs_per_zspage) 7018c2ecf20Sopenharmony_ci fg = ZS_FULL; 7028c2ecf20Sopenharmony_ci else if (inuse <= 3 * objs_per_zspage / fullness_threshold_frac) 7038c2ecf20Sopenharmony_ci fg = ZS_ALMOST_EMPTY; 7048c2ecf20Sopenharmony_ci else 7058c2ecf20Sopenharmony_ci fg = ZS_ALMOST_FULL; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci return fg; 7088c2ecf20Sopenharmony_ci} 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci/* 7118c2ecf20Sopenharmony_ci * Each size class maintains various freelists and zspages are assigned 7128c2ecf20Sopenharmony_ci * to one of these freelists based on the number of live objects they 7138c2ecf20Sopenharmony_ci * have. This functions inserts the given zspage into the freelist 7148c2ecf20Sopenharmony_ci * identified by <class, fullness_group>. 7158c2ecf20Sopenharmony_ci */ 7168c2ecf20Sopenharmony_cistatic void insert_zspage(struct size_class *class, 7178c2ecf20Sopenharmony_ci struct zspage *zspage, 7188c2ecf20Sopenharmony_ci enum fullness_group fullness) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci struct zspage *head; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci zs_stat_inc(class, fullness, 1); 7238c2ecf20Sopenharmony_ci head = list_first_entry_or_null(&class->fullness_list[fullness], 7248c2ecf20Sopenharmony_ci struct zspage, list); 7258c2ecf20Sopenharmony_ci /* 7268c2ecf20Sopenharmony_ci * We want to see more ZS_FULL pages and less almost empty/full. 7278c2ecf20Sopenharmony_ci * Put pages with higher ->inuse first. 7288c2ecf20Sopenharmony_ci */ 7298c2ecf20Sopenharmony_ci if (head) { 7308c2ecf20Sopenharmony_ci if (get_zspage_inuse(zspage) < get_zspage_inuse(head)) { 7318c2ecf20Sopenharmony_ci list_add(&zspage->list, &head->list); 7328c2ecf20Sopenharmony_ci return; 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci } 7358c2ecf20Sopenharmony_ci list_add(&zspage->list, &class->fullness_list[fullness]); 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci/* 7398c2ecf20Sopenharmony_ci * This function removes the given zspage from the freelist identified 7408c2ecf20Sopenharmony_ci * by <class, fullness_group>. 7418c2ecf20Sopenharmony_ci */ 7428c2ecf20Sopenharmony_cistatic void remove_zspage(struct size_class *class, 7438c2ecf20Sopenharmony_ci struct zspage *zspage, 7448c2ecf20Sopenharmony_ci enum fullness_group fullness) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci VM_BUG_ON(list_empty(&class->fullness_list[fullness])); 7478c2ecf20Sopenharmony_ci VM_BUG_ON(is_zspage_isolated(zspage)); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci list_del_init(&zspage->list); 7508c2ecf20Sopenharmony_ci zs_stat_dec(class, fullness, 1); 7518c2ecf20Sopenharmony_ci} 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci/* 7548c2ecf20Sopenharmony_ci * Each size class maintains zspages in different fullness groups depending 7558c2ecf20Sopenharmony_ci * on the number of live objects they contain. When allocating or freeing 7568c2ecf20Sopenharmony_ci * objects, the fullness status of the page can change, say, from ALMOST_FULL 7578c2ecf20Sopenharmony_ci * to ALMOST_EMPTY when freeing an object. This function checks if such 7588c2ecf20Sopenharmony_ci * a status change has occurred for the given page and accordingly moves the 7598c2ecf20Sopenharmony_ci * page from the freelist of the old fullness group to that of the new 7608c2ecf20Sopenharmony_ci * fullness group. 7618c2ecf20Sopenharmony_ci */ 7628c2ecf20Sopenharmony_cistatic enum fullness_group fix_fullness_group(struct size_class *class, 7638c2ecf20Sopenharmony_ci struct zspage *zspage) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci int class_idx; 7668c2ecf20Sopenharmony_ci enum fullness_group currfg, newfg; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci get_zspage_mapping(zspage, &class_idx, &currfg); 7698c2ecf20Sopenharmony_ci newfg = get_fullness_group(class, zspage); 7708c2ecf20Sopenharmony_ci if (newfg == currfg) 7718c2ecf20Sopenharmony_ci goto out; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci if (!is_zspage_isolated(zspage)) { 7748c2ecf20Sopenharmony_ci remove_zspage(class, zspage, currfg); 7758c2ecf20Sopenharmony_ci insert_zspage(class, zspage, newfg); 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci set_zspage_mapping(zspage, class_idx, newfg); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ciout: 7818c2ecf20Sopenharmony_ci return newfg; 7828c2ecf20Sopenharmony_ci} 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci/* 7858c2ecf20Sopenharmony_ci * We have to decide on how many pages to link together 7868c2ecf20Sopenharmony_ci * to form a zspage for each size class. This is important 7878c2ecf20Sopenharmony_ci * to reduce wastage due to unusable space left at end of 7888c2ecf20Sopenharmony_ci * each zspage which is given as: 7898c2ecf20Sopenharmony_ci * wastage = Zp % class_size 7908c2ecf20Sopenharmony_ci * usage = Zp - wastage 7918c2ecf20Sopenharmony_ci * where Zp = zspage size = k * PAGE_SIZE where k = 1, 2, ... 7928c2ecf20Sopenharmony_ci * 7938c2ecf20Sopenharmony_ci * For example, for size class of 3/8 * PAGE_SIZE, we should 7948c2ecf20Sopenharmony_ci * link together 3 PAGE_SIZE sized pages to form a zspage 7958c2ecf20Sopenharmony_ci * since then we can perfectly fit in 8 such objects. 7968c2ecf20Sopenharmony_ci */ 7978c2ecf20Sopenharmony_cistatic int get_pages_per_zspage(int class_size) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci int i, max_usedpc = 0; 8008c2ecf20Sopenharmony_ci /* zspage order which gives maximum used size per KB */ 8018c2ecf20Sopenharmony_ci int max_usedpc_order = 1; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci for (i = 1; i <= ZS_MAX_PAGES_PER_ZSPAGE; i++) { 8048c2ecf20Sopenharmony_ci int zspage_size; 8058c2ecf20Sopenharmony_ci int waste, usedpc; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci zspage_size = i * PAGE_SIZE; 8088c2ecf20Sopenharmony_ci waste = zspage_size % class_size; 8098c2ecf20Sopenharmony_ci usedpc = (zspage_size - waste) * 100 / zspage_size; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci if (usedpc > max_usedpc) { 8128c2ecf20Sopenharmony_ci max_usedpc = usedpc; 8138c2ecf20Sopenharmony_ci max_usedpc_order = i; 8148c2ecf20Sopenharmony_ci } 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci return max_usedpc_order; 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_cistatic struct zspage *get_zspage(struct page *page) 8218c2ecf20Sopenharmony_ci{ 8228c2ecf20Sopenharmony_ci struct zspage *zspage = (struct zspage *)page->private; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci BUG_ON(zspage->magic != ZSPAGE_MAGIC); 8258c2ecf20Sopenharmony_ci return zspage; 8268c2ecf20Sopenharmony_ci} 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_cistatic struct page *get_next_page(struct page *page) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci if (unlikely(PageHugeObject(page))) 8318c2ecf20Sopenharmony_ci return NULL; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci return page->freelist; 8348c2ecf20Sopenharmony_ci} 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci/** 8378c2ecf20Sopenharmony_ci * obj_to_location - get (<page>, <obj_idx>) from encoded object value 8388c2ecf20Sopenharmony_ci * @obj: the encoded object value 8398c2ecf20Sopenharmony_ci * @page: page object resides in zspage 8408c2ecf20Sopenharmony_ci * @obj_idx: object index 8418c2ecf20Sopenharmony_ci */ 8428c2ecf20Sopenharmony_cistatic void obj_to_location(unsigned long obj, struct page **page, 8438c2ecf20Sopenharmony_ci unsigned int *obj_idx) 8448c2ecf20Sopenharmony_ci{ 8458c2ecf20Sopenharmony_ci obj >>= OBJ_TAG_BITS; 8468c2ecf20Sopenharmony_ci *page = pfn_to_page(obj >> OBJ_INDEX_BITS); 8478c2ecf20Sopenharmony_ci *obj_idx = (obj & OBJ_INDEX_MASK); 8488c2ecf20Sopenharmony_ci} 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci/** 8518c2ecf20Sopenharmony_ci * location_to_obj - get obj value encoded from (<page>, <obj_idx>) 8528c2ecf20Sopenharmony_ci * @page: page object resides in zspage 8538c2ecf20Sopenharmony_ci * @obj_idx: object index 8548c2ecf20Sopenharmony_ci */ 8558c2ecf20Sopenharmony_cistatic unsigned long location_to_obj(struct page *page, unsigned int obj_idx) 8568c2ecf20Sopenharmony_ci{ 8578c2ecf20Sopenharmony_ci unsigned long obj; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci obj = page_to_pfn(page) << OBJ_INDEX_BITS; 8608c2ecf20Sopenharmony_ci obj |= obj_idx & OBJ_INDEX_MASK; 8618c2ecf20Sopenharmony_ci obj <<= OBJ_TAG_BITS; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci return obj; 8648c2ecf20Sopenharmony_ci} 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_cistatic unsigned long handle_to_obj(unsigned long handle) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci return *(unsigned long *)handle; 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic unsigned long obj_to_head(struct page *page, void *obj) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci if (unlikely(PageHugeObject(page))) { 8748c2ecf20Sopenharmony_ci VM_BUG_ON_PAGE(!is_first_page(page), page); 8758c2ecf20Sopenharmony_ci return page->index; 8768c2ecf20Sopenharmony_ci } else 8778c2ecf20Sopenharmony_ci return *(unsigned long *)obj; 8788c2ecf20Sopenharmony_ci} 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_cistatic inline int testpin_tag(unsigned long handle) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci return bit_spin_is_locked(HANDLE_PIN_BIT, (unsigned long *)handle); 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_cistatic inline int trypin_tag(unsigned long handle) 8868c2ecf20Sopenharmony_ci{ 8878c2ecf20Sopenharmony_ci return bit_spin_trylock(HANDLE_PIN_BIT, (unsigned long *)handle); 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic void pin_tag(unsigned long handle) __acquires(bitlock) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci bit_spin_lock(HANDLE_PIN_BIT, (unsigned long *)handle); 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_cistatic void unpin_tag(unsigned long handle) __releases(bitlock) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci bit_spin_unlock(HANDLE_PIN_BIT, (unsigned long *)handle); 8988c2ecf20Sopenharmony_ci} 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cistatic void reset_page(struct page *page) 9018c2ecf20Sopenharmony_ci{ 9028c2ecf20Sopenharmony_ci __ClearPageMovable(page); 9038c2ecf20Sopenharmony_ci ClearPagePrivate(page); 9048c2ecf20Sopenharmony_ci set_page_private(page, 0); 9058c2ecf20Sopenharmony_ci page_mapcount_reset(page); 9068c2ecf20Sopenharmony_ci ClearPageHugeObject(page); 9078c2ecf20Sopenharmony_ci page->freelist = NULL; 9088c2ecf20Sopenharmony_ci} 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_cistatic int trylock_zspage(struct zspage *zspage) 9118c2ecf20Sopenharmony_ci{ 9128c2ecf20Sopenharmony_ci struct page *cursor, *fail; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci for (cursor = get_first_page(zspage); cursor != NULL; cursor = 9158c2ecf20Sopenharmony_ci get_next_page(cursor)) { 9168c2ecf20Sopenharmony_ci if (!trylock_page(cursor)) { 9178c2ecf20Sopenharmony_ci fail = cursor; 9188c2ecf20Sopenharmony_ci goto unlock; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci return 1; 9238c2ecf20Sopenharmony_ciunlock: 9248c2ecf20Sopenharmony_ci for (cursor = get_first_page(zspage); cursor != fail; cursor = 9258c2ecf20Sopenharmony_ci get_next_page(cursor)) 9268c2ecf20Sopenharmony_ci unlock_page(cursor); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci return 0; 9298c2ecf20Sopenharmony_ci} 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_cistatic void __free_zspage(struct zs_pool *pool, struct size_class *class, 9328c2ecf20Sopenharmony_ci struct zspage *zspage) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci struct page *page, *next; 9358c2ecf20Sopenharmony_ci enum fullness_group fg; 9368c2ecf20Sopenharmony_ci unsigned int class_idx; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci get_zspage_mapping(zspage, &class_idx, &fg); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci assert_spin_locked(&class->lock); 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci VM_BUG_ON(get_zspage_inuse(zspage)); 9438c2ecf20Sopenharmony_ci VM_BUG_ON(fg != ZS_EMPTY); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci next = page = get_first_page(zspage); 9468c2ecf20Sopenharmony_ci do { 9478c2ecf20Sopenharmony_ci VM_BUG_ON_PAGE(!PageLocked(page), page); 9488c2ecf20Sopenharmony_ci next = get_next_page(page); 9498c2ecf20Sopenharmony_ci reset_page(page); 9508c2ecf20Sopenharmony_ci unlock_page(page); 9518c2ecf20Sopenharmony_ci dec_zone_page_state(page, NR_ZSPAGES); 9528c2ecf20Sopenharmony_ci put_page(page); 9538c2ecf20Sopenharmony_ci page = next; 9548c2ecf20Sopenharmony_ci } while (page != NULL); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci cache_free_zspage(pool, zspage); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci zs_stat_dec(class, OBJ_ALLOCATED, class->objs_per_zspage); 9598c2ecf20Sopenharmony_ci atomic_long_sub(class->pages_per_zspage, 9608c2ecf20Sopenharmony_ci &pool->pages_allocated); 9618c2ecf20Sopenharmony_ci} 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_cistatic void free_zspage(struct zs_pool *pool, struct size_class *class, 9648c2ecf20Sopenharmony_ci struct zspage *zspage) 9658c2ecf20Sopenharmony_ci{ 9668c2ecf20Sopenharmony_ci VM_BUG_ON(get_zspage_inuse(zspage)); 9678c2ecf20Sopenharmony_ci VM_BUG_ON(list_empty(&zspage->list)); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci if (!trylock_zspage(zspage)) { 9708c2ecf20Sopenharmony_ci kick_deferred_free(pool); 9718c2ecf20Sopenharmony_ci return; 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci remove_zspage(class, zspage, ZS_EMPTY); 9758c2ecf20Sopenharmony_ci __free_zspage(pool, class, zspage); 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci/* Initialize a newly allocated zspage */ 9798c2ecf20Sopenharmony_cistatic void init_zspage(struct size_class *class, struct zspage *zspage) 9808c2ecf20Sopenharmony_ci{ 9818c2ecf20Sopenharmony_ci unsigned int freeobj = 1; 9828c2ecf20Sopenharmony_ci unsigned long off = 0; 9838c2ecf20Sopenharmony_ci struct page *page = get_first_page(zspage); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci while (page) { 9868c2ecf20Sopenharmony_ci struct page *next_page; 9878c2ecf20Sopenharmony_ci struct link_free *link; 9888c2ecf20Sopenharmony_ci void *vaddr; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci set_first_obj_offset(page, off); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci vaddr = kmap_atomic(page); 9938c2ecf20Sopenharmony_ci link = (struct link_free *)vaddr + off / sizeof(*link); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci while ((off += class->size) < PAGE_SIZE) { 9968c2ecf20Sopenharmony_ci link->next = freeobj++ << OBJ_TAG_BITS; 9978c2ecf20Sopenharmony_ci link += class->size / sizeof(*link); 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci /* 10018c2ecf20Sopenharmony_ci * We now come to the last (full or partial) object on this 10028c2ecf20Sopenharmony_ci * page, which must point to the first object on the next 10038c2ecf20Sopenharmony_ci * page (if present) 10048c2ecf20Sopenharmony_ci */ 10058c2ecf20Sopenharmony_ci next_page = get_next_page(page); 10068c2ecf20Sopenharmony_ci if (next_page) { 10078c2ecf20Sopenharmony_ci link->next = freeobj++ << OBJ_TAG_BITS; 10088c2ecf20Sopenharmony_ci } else { 10098c2ecf20Sopenharmony_ci /* 10108c2ecf20Sopenharmony_ci * Reset OBJ_TAG_BITS bit to last link to tell 10118c2ecf20Sopenharmony_ci * whether it's allocated object or not. 10128c2ecf20Sopenharmony_ci */ 10138c2ecf20Sopenharmony_ci link->next = -1UL << OBJ_TAG_BITS; 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci kunmap_atomic(vaddr); 10168c2ecf20Sopenharmony_ci page = next_page; 10178c2ecf20Sopenharmony_ci off %= PAGE_SIZE; 10188c2ecf20Sopenharmony_ci } 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci set_freeobj(zspage, 0); 10218c2ecf20Sopenharmony_ci} 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_cistatic void create_page_chain(struct size_class *class, struct zspage *zspage, 10248c2ecf20Sopenharmony_ci struct page *pages[]) 10258c2ecf20Sopenharmony_ci{ 10268c2ecf20Sopenharmony_ci int i; 10278c2ecf20Sopenharmony_ci struct page *page; 10288c2ecf20Sopenharmony_ci struct page *prev_page = NULL; 10298c2ecf20Sopenharmony_ci int nr_pages = class->pages_per_zspage; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci /* 10328c2ecf20Sopenharmony_ci * Allocate individual pages and link them together as: 10338c2ecf20Sopenharmony_ci * 1. all pages are linked together using page->freelist 10348c2ecf20Sopenharmony_ci * 2. each sub-page point to zspage using page->private 10358c2ecf20Sopenharmony_ci * 10368c2ecf20Sopenharmony_ci * we set PG_private to identify the first page (i.e. no other sub-page 10378c2ecf20Sopenharmony_ci * has this flag set). 10388c2ecf20Sopenharmony_ci */ 10398c2ecf20Sopenharmony_ci for (i = 0; i < nr_pages; i++) { 10408c2ecf20Sopenharmony_ci page = pages[i]; 10418c2ecf20Sopenharmony_ci set_page_private(page, (unsigned long)zspage); 10428c2ecf20Sopenharmony_ci page->freelist = NULL; 10438c2ecf20Sopenharmony_ci if (i == 0) { 10448c2ecf20Sopenharmony_ci zspage->first_page = page; 10458c2ecf20Sopenharmony_ci SetPagePrivate(page); 10468c2ecf20Sopenharmony_ci if (unlikely(class->objs_per_zspage == 1 && 10478c2ecf20Sopenharmony_ci class->pages_per_zspage == 1)) 10488c2ecf20Sopenharmony_ci SetPageHugeObject(page); 10498c2ecf20Sopenharmony_ci } else { 10508c2ecf20Sopenharmony_ci prev_page->freelist = page; 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci prev_page = page; 10538c2ecf20Sopenharmony_ci } 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci/* 10578c2ecf20Sopenharmony_ci * Allocate a zspage for the given size class 10588c2ecf20Sopenharmony_ci */ 10598c2ecf20Sopenharmony_cistatic struct zspage *alloc_zspage(struct zs_pool *pool, 10608c2ecf20Sopenharmony_ci struct size_class *class, 10618c2ecf20Sopenharmony_ci gfp_t gfp) 10628c2ecf20Sopenharmony_ci{ 10638c2ecf20Sopenharmony_ci int i; 10648c2ecf20Sopenharmony_ci struct page *pages[ZS_MAX_PAGES_PER_ZSPAGE]; 10658c2ecf20Sopenharmony_ci struct zspage *zspage = cache_alloc_zspage(pool, gfp); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (!zspage) 10688c2ecf20Sopenharmony_ci return NULL; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci memset(zspage, 0, sizeof(struct zspage)); 10718c2ecf20Sopenharmony_ci zspage->magic = ZSPAGE_MAGIC; 10728c2ecf20Sopenharmony_ci migrate_lock_init(zspage); 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci for (i = 0; i < class->pages_per_zspage; i++) { 10758c2ecf20Sopenharmony_ci struct page *page; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci page = alloc_page(gfp); 10788c2ecf20Sopenharmony_ci if (!page) { 10798c2ecf20Sopenharmony_ci while (--i >= 0) { 10808c2ecf20Sopenharmony_ci dec_zone_page_state(pages[i], NR_ZSPAGES); 10818c2ecf20Sopenharmony_ci __free_page(pages[i]); 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci cache_free_zspage(pool, zspage); 10848c2ecf20Sopenharmony_ci return NULL; 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci inc_zone_page_state(page, NR_ZSPAGES); 10888c2ecf20Sopenharmony_ci#ifdef CONFIG_PAGE_TRACING 10898c2ecf20Sopenharmony_ci SetPageZspage(page); 10908c2ecf20Sopenharmony_ci#endif 10918c2ecf20Sopenharmony_ci pages[i] = page; 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci create_page_chain(class, zspage, pages); 10958c2ecf20Sopenharmony_ci init_zspage(class, zspage); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci return zspage; 10988c2ecf20Sopenharmony_ci} 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_cistatic struct zspage *find_get_zspage(struct size_class *class) 11018c2ecf20Sopenharmony_ci{ 11028c2ecf20Sopenharmony_ci int i; 11038c2ecf20Sopenharmony_ci struct zspage *zspage; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci for (i = ZS_ALMOST_FULL; i >= ZS_EMPTY; i--) { 11068c2ecf20Sopenharmony_ci zspage = list_first_entry_or_null(&class->fullness_list[i], 11078c2ecf20Sopenharmony_ci struct zspage, list); 11088c2ecf20Sopenharmony_ci if (zspage) 11098c2ecf20Sopenharmony_ci break; 11108c2ecf20Sopenharmony_ci } 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci return zspage; 11138c2ecf20Sopenharmony_ci} 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_cistatic inline int __zs_cpu_up(struct mapping_area *area) 11168c2ecf20Sopenharmony_ci{ 11178c2ecf20Sopenharmony_ci /* 11188c2ecf20Sopenharmony_ci * Make sure we don't leak memory if a cpu UP notification 11198c2ecf20Sopenharmony_ci * and zs_init() race and both call zs_cpu_up() on the same cpu 11208c2ecf20Sopenharmony_ci */ 11218c2ecf20Sopenharmony_ci if (area->vm_buf) 11228c2ecf20Sopenharmony_ci return 0; 11238c2ecf20Sopenharmony_ci area->vm_buf = kmalloc(ZS_MAX_ALLOC_SIZE, GFP_KERNEL); 11248c2ecf20Sopenharmony_ci if (!area->vm_buf) 11258c2ecf20Sopenharmony_ci return -ENOMEM; 11268c2ecf20Sopenharmony_ci return 0; 11278c2ecf20Sopenharmony_ci} 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_cistatic inline void __zs_cpu_down(struct mapping_area *area) 11308c2ecf20Sopenharmony_ci{ 11318c2ecf20Sopenharmony_ci kfree(area->vm_buf); 11328c2ecf20Sopenharmony_ci area->vm_buf = NULL; 11338c2ecf20Sopenharmony_ci} 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_cistatic void *__zs_map_object(struct mapping_area *area, 11368c2ecf20Sopenharmony_ci struct page *pages[2], int off, int size) 11378c2ecf20Sopenharmony_ci{ 11388c2ecf20Sopenharmony_ci int sizes[2]; 11398c2ecf20Sopenharmony_ci void *addr; 11408c2ecf20Sopenharmony_ci char *buf = area->vm_buf; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci /* disable page faults to match kmap_atomic() return conditions */ 11438c2ecf20Sopenharmony_ci pagefault_disable(); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci /* no read fastpath */ 11468c2ecf20Sopenharmony_ci if (area->vm_mm == ZS_MM_WO) 11478c2ecf20Sopenharmony_ci goto out; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci sizes[0] = PAGE_SIZE - off; 11508c2ecf20Sopenharmony_ci sizes[1] = size - sizes[0]; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci /* copy object to per-cpu buffer */ 11538c2ecf20Sopenharmony_ci addr = kmap_atomic(pages[0]); 11548c2ecf20Sopenharmony_ci memcpy(buf, addr + off, sizes[0]); 11558c2ecf20Sopenharmony_ci kunmap_atomic(addr); 11568c2ecf20Sopenharmony_ci addr = kmap_atomic(pages[1]); 11578c2ecf20Sopenharmony_ci memcpy(buf + sizes[0], addr, sizes[1]); 11588c2ecf20Sopenharmony_ci kunmap_atomic(addr); 11598c2ecf20Sopenharmony_ciout: 11608c2ecf20Sopenharmony_ci return area->vm_buf; 11618c2ecf20Sopenharmony_ci} 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cistatic void __zs_unmap_object(struct mapping_area *area, 11648c2ecf20Sopenharmony_ci struct page *pages[2], int off, int size) 11658c2ecf20Sopenharmony_ci{ 11668c2ecf20Sopenharmony_ci int sizes[2]; 11678c2ecf20Sopenharmony_ci void *addr; 11688c2ecf20Sopenharmony_ci char *buf; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci /* no write fastpath */ 11718c2ecf20Sopenharmony_ci if (area->vm_mm == ZS_MM_RO) 11728c2ecf20Sopenharmony_ci goto out; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci buf = area->vm_buf; 11758c2ecf20Sopenharmony_ci buf = buf + ZS_HANDLE_SIZE; 11768c2ecf20Sopenharmony_ci size -= ZS_HANDLE_SIZE; 11778c2ecf20Sopenharmony_ci off += ZS_HANDLE_SIZE; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci sizes[0] = PAGE_SIZE - off; 11808c2ecf20Sopenharmony_ci sizes[1] = size - sizes[0]; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci /* copy per-cpu buffer to object */ 11838c2ecf20Sopenharmony_ci addr = kmap_atomic(pages[0]); 11848c2ecf20Sopenharmony_ci memcpy(addr + off, buf, sizes[0]); 11858c2ecf20Sopenharmony_ci kunmap_atomic(addr); 11868c2ecf20Sopenharmony_ci addr = kmap_atomic(pages[1]); 11878c2ecf20Sopenharmony_ci memcpy(addr, buf + sizes[0], sizes[1]); 11888c2ecf20Sopenharmony_ci kunmap_atomic(addr); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ciout: 11918c2ecf20Sopenharmony_ci /* enable page faults to match kunmap_atomic() return conditions */ 11928c2ecf20Sopenharmony_ci pagefault_enable(); 11938c2ecf20Sopenharmony_ci} 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_cistatic int zs_cpu_prepare(unsigned int cpu) 11968c2ecf20Sopenharmony_ci{ 11978c2ecf20Sopenharmony_ci struct mapping_area *area; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci area = &per_cpu(zs_map_area, cpu); 12008c2ecf20Sopenharmony_ci return __zs_cpu_up(area); 12018c2ecf20Sopenharmony_ci} 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_cistatic int zs_cpu_dead(unsigned int cpu) 12048c2ecf20Sopenharmony_ci{ 12058c2ecf20Sopenharmony_ci struct mapping_area *area; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci area = &per_cpu(zs_map_area, cpu); 12088c2ecf20Sopenharmony_ci __zs_cpu_down(area); 12098c2ecf20Sopenharmony_ci return 0; 12108c2ecf20Sopenharmony_ci} 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_cistatic bool can_merge(struct size_class *prev, int pages_per_zspage, 12138c2ecf20Sopenharmony_ci int objs_per_zspage) 12148c2ecf20Sopenharmony_ci{ 12158c2ecf20Sopenharmony_ci if (prev->pages_per_zspage == pages_per_zspage && 12168c2ecf20Sopenharmony_ci prev->objs_per_zspage == objs_per_zspage) 12178c2ecf20Sopenharmony_ci return true; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci return false; 12208c2ecf20Sopenharmony_ci} 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_cistatic bool zspage_full(struct size_class *class, struct zspage *zspage) 12238c2ecf20Sopenharmony_ci{ 12248c2ecf20Sopenharmony_ci return get_zspage_inuse(zspage) == class->objs_per_zspage; 12258c2ecf20Sopenharmony_ci} 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ciunsigned long zs_get_total_pages(struct zs_pool *pool) 12288c2ecf20Sopenharmony_ci{ 12298c2ecf20Sopenharmony_ci return atomic_long_read(&pool->pages_allocated); 12308c2ecf20Sopenharmony_ci} 12318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_get_total_pages); 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci/** 12348c2ecf20Sopenharmony_ci * zs_map_object - get address of allocated object from handle. 12358c2ecf20Sopenharmony_ci * @pool: pool from which the object was allocated 12368c2ecf20Sopenharmony_ci * @handle: handle returned from zs_malloc 12378c2ecf20Sopenharmony_ci * @mm: maping mode to use 12388c2ecf20Sopenharmony_ci * 12398c2ecf20Sopenharmony_ci * Before using an object allocated from zs_malloc, it must be mapped using 12408c2ecf20Sopenharmony_ci * this function. When done with the object, it must be unmapped using 12418c2ecf20Sopenharmony_ci * zs_unmap_object. 12428c2ecf20Sopenharmony_ci * 12438c2ecf20Sopenharmony_ci * Only one object can be mapped per cpu at a time. There is no protection 12448c2ecf20Sopenharmony_ci * against nested mappings. 12458c2ecf20Sopenharmony_ci * 12468c2ecf20Sopenharmony_ci * This function returns with preemption and page faults disabled. 12478c2ecf20Sopenharmony_ci */ 12488c2ecf20Sopenharmony_civoid *zs_map_object(struct zs_pool *pool, unsigned long handle, 12498c2ecf20Sopenharmony_ci enum zs_mapmode mm) 12508c2ecf20Sopenharmony_ci{ 12518c2ecf20Sopenharmony_ci struct zspage *zspage; 12528c2ecf20Sopenharmony_ci struct page *page; 12538c2ecf20Sopenharmony_ci unsigned long obj, off; 12548c2ecf20Sopenharmony_ci unsigned int obj_idx; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci unsigned int class_idx; 12578c2ecf20Sopenharmony_ci enum fullness_group fg; 12588c2ecf20Sopenharmony_ci struct size_class *class; 12598c2ecf20Sopenharmony_ci struct mapping_area *area; 12608c2ecf20Sopenharmony_ci struct page *pages[2]; 12618c2ecf20Sopenharmony_ci void *ret; 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci /* 12648c2ecf20Sopenharmony_ci * Because we use per-cpu mapping areas shared among the 12658c2ecf20Sopenharmony_ci * pools/users, we can't allow mapping in interrupt context 12668c2ecf20Sopenharmony_ci * because it can corrupt another users mappings. 12678c2ecf20Sopenharmony_ci */ 12688c2ecf20Sopenharmony_ci BUG_ON(in_interrupt()); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci /* From now on, migration cannot move the object */ 12718c2ecf20Sopenharmony_ci pin_tag(handle); 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci obj = handle_to_obj(handle); 12748c2ecf20Sopenharmony_ci obj_to_location(obj, &page, &obj_idx); 12758c2ecf20Sopenharmony_ci zspage = get_zspage(page); 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci /* migration cannot move any subpage in this zspage */ 12788c2ecf20Sopenharmony_ci migrate_read_lock(zspage); 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci get_zspage_mapping(zspage, &class_idx, &fg); 12818c2ecf20Sopenharmony_ci class = pool->size_class[class_idx]; 12828c2ecf20Sopenharmony_ci off = (class->size * obj_idx) & ~PAGE_MASK; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci area = &get_cpu_var(zs_map_area); 12858c2ecf20Sopenharmony_ci area->vm_mm = mm; 12868c2ecf20Sopenharmony_ci if (off + class->size <= PAGE_SIZE) { 12878c2ecf20Sopenharmony_ci /* this object is contained entirely within a page */ 12888c2ecf20Sopenharmony_ci area->vm_addr = kmap_atomic(page); 12898c2ecf20Sopenharmony_ci ret = area->vm_addr + off; 12908c2ecf20Sopenharmony_ci goto out; 12918c2ecf20Sopenharmony_ci } 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci /* this object spans two pages */ 12948c2ecf20Sopenharmony_ci pages[0] = page; 12958c2ecf20Sopenharmony_ci pages[1] = get_next_page(page); 12968c2ecf20Sopenharmony_ci BUG_ON(!pages[1]); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci ret = __zs_map_object(area, pages, off, class->size); 12998c2ecf20Sopenharmony_ciout: 13008c2ecf20Sopenharmony_ci if (likely(!PageHugeObject(page))) 13018c2ecf20Sopenharmony_ci ret += ZS_HANDLE_SIZE; 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci return ret; 13048c2ecf20Sopenharmony_ci} 13058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_map_object); 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_civoid zs_unmap_object(struct zs_pool *pool, unsigned long handle) 13088c2ecf20Sopenharmony_ci{ 13098c2ecf20Sopenharmony_ci struct zspage *zspage; 13108c2ecf20Sopenharmony_ci struct page *page; 13118c2ecf20Sopenharmony_ci unsigned long obj, off; 13128c2ecf20Sopenharmony_ci unsigned int obj_idx; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci unsigned int class_idx; 13158c2ecf20Sopenharmony_ci enum fullness_group fg; 13168c2ecf20Sopenharmony_ci struct size_class *class; 13178c2ecf20Sopenharmony_ci struct mapping_area *area; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci obj = handle_to_obj(handle); 13208c2ecf20Sopenharmony_ci obj_to_location(obj, &page, &obj_idx); 13218c2ecf20Sopenharmony_ci zspage = get_zspage(page); 13228c2ecf20Sopenharmony_ci get_zspage_mapping(zspage, &class_idx, &fg); 13238c2ecf20Sopenharmony_ci class = pool->size_class[class_idx]; 13248c2ecf20Sopenharmony_ci off = (class->size * obj_idx) & ~PAGE_MASK; 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci area = this_cpu_ptr(&zs_map_area); 13278c2ecf20Sopenharmony_ci if (off + class->size <= PAGE_SIZE) 13288c2ecf20Sopenharmony_ci kunmap_atomic(area->vm_addr); 13298c2ecf20Sopenharmony_ci else { 13308c2ecf20Sopenharmony_ci struct page *pages[2]; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci pages[0] = page; 13338c2ecf20Sopenharmony_ci pages[1] = get_next_page(page); 13348c2ecf20Sopenharmony_ci BUG_ON(!pages[1]); 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci __zs_unmap_object(area, pages, off, class->size); 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci put_cpu_var(zs_map_area); 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci migrate_read_unlock(zspage); 13418c2ecf20Sopenharmony_ci unpin_tag(handle); 13428c2ecf20Sopenharmony_ci} 13438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_unmap_object); 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci/** 13468c2ecf20Sopenharmony_ci * zs_huge_class_size() - Returns the size (in bytes) of the first huge 13478c2ecf20Sopenharmony_ci * zsmalloc &size_class. 13488c2ecf20Sopenharmony_ci * @pool: zsmalloc pool to use 13498c2ecf20Sopenharmony_ci * 13508c2ecf20Sopenharmony_ci * The function returns the size of the first huge class - any object of equal 13518c2ecf20Sopenharmony_ci * or bigger size will be stored in zspage consisting of a single physical 13528c2ecf20Sopenharmony_ci * page. 13538c2ecf20Sopenharmony_ci * 13548c2ecf20Sopenharmony_ci * Context: Any context. 13558c2ecf20Sopenharmony_ci * 13568c2ecf20Sopenharmony_ci * Return: the size (in bytes) of the first huge zsmalloc &size_class. 13578c2ecf20Sopenharmony_ci */ 13588c2ecf20Sopenharmony_cisize_t zs_huge_class_size(struct zs_pool *pool) 13598c2ecf20Sopenharmony_ci{ 13608c2ecf20Sopenharmony_ci return huge_class_size; 13618c2ecf20Sopenharmony_ci} 13628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_huge_class_size); 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_cistatic unsigned long obj_malloc(struct size_class *class, 13658c2ecf20Sopenharmony_ci struct zspage *zspage, unsigned long handle) 13668c2ecf20Sopenharmony_ci{ 13678c2ecf20Sopenharmony_ci int i, nr_page, offset; 13688c2ecf20Sopenharmony_ci unsigned long obj; 13698c2ecf20Sopenharmony_ci struct link_free *link; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci struct page *m_page; 13728c2ecf20Sopenharmony_ci unsigned long m_offset; 13738c2ecf20Sopenharmony_ci void *vaddr; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci handle |= OBJ_ALLOCATED_TAG; 13768c2ecf20Sopenharmony_ci obj = get_freeobj(zspage); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci offset = obj * class->size; 13798c2ecf20Sopenharmony_ci nr_page = offset >> PAGE_SHIFT; 13808c2ecf20Sopenharmony_ci m_offset = offset & ~PAGE_MASK; 13818c2ecf20Sopenharmony_ci m_page = get_first_page(zspage); 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci for (i = 0; i < nr_page; i++) 13848c2ecf20Sopenharmony_ci m_page = get_next_page(m_page); 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci vaddr = kmap_atomic(m_page); 13878c2ecf20Sopenharmony_ci link = (struct link_free *)vaddr + m_offset / sizeof(*link); 13888c2ecf20Sopenharmony_ci set_freeobj(zspage, link->next >> OBJ_TAG_BITS); 13898c2ecf20Sopenharmony_ci if (likely(!PageHugeObject(m_page))) 13908c2ecf20Sopenharmony_ci /* record handle in the header of allocated chunk */ 13918c2ecf20Sopenharmony_ci link->handle = handle; 13928c2ecf20Sopenharmony_ci else 13938c2ecf20Sopenharmony_ci /* record handle to page->index */ 13948c2ecf20Sopenharmony_ci zspage->first_page->index = handle; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci kunmap_atomic(vaddr); 13978c2ecf20Sopenharmony_ci mod_zspage_inuse(zspage, 1); 13988c2ecf20Sopenharmony_ci zs_stat_inc(class, OBJ_USED, 1); 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci obj = location_to_obj(m_page, obj); 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci return obj; 14038c2ecf20Sopenharmony_ci} 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci/** 14078c2ecf20Sopenharmony_ci * zs_malloc - Allocate block of given size from pool. 14088c2ecf20Sopenharmony_ci * @pool: pool to allocate from 14098c2ecf20Sopenharmony_ci * @size: size of block to allocate 14108c2ecf20Sopenharmony_ci * @gfp: gfp flags when allocating object 14118c2ecf20Sopenharmony_ci * 14128c2ecf20Sopenharmony_ci * On success, handle to the allocated object is returned, 14138c2ecf20Sopenharmony_ci * otherwise 0. 14148c2ecf20Sopenharmony_ci * Allocation requests with size > ZS_MAX_ALLOC_SIZE will fail. 14158c2ecf20Sopenharmony_ci */ 14168c2ecf20Sopenharmony_ciunsigned long zs_malloc(struct zs_pool *pool, size_t size, gfp_t gfp) 14178c2ecf20Sopenharmony_ci{ 14188c2ecf20Sopenharmony_ci unsigned long handle, obj; 14198c2ecf20Sopenharmony_ci struct size_class *class; 14208c2ecf20Sopenharmony_ci enum fullness_group newfg; 14218c2ecf20Sopenharmony_ci struct zspage *zspage; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci if (unlikely(!size || size > ZS_MAX_ALLOC_SIZE)) 14248c2ecf20Sopenharmony_ci return 0; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci handle = cache_alloc_handle(pool, gfp); 14278c2ecf20Sopenharmony_ci if (!handle) 14288c2ecf20Sopenharmony_ci return 0; 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci /* extra space in chunk to keep the handle */ 14318c2ecf20Sopenharmony_ci size += ZS_HANDLE_SIZE; 14328c2ecf20Sopenharmony_ci class = pool->size_class[get_size_class_index(size)]; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci spin_lock(&class->lock); 14358c2ecf20Sopenharmony_ci zspage = find_get_zspage(class); 14368c2ecf20Sopenharmony_ci if (likely(zspage)) { 14378c2ecf20Sopenharmony_ci obj = obj_malloc(class, zspage, handle); 14388c2ecf20Sopenharmony_ci /* Now move the zspage to another fullness group, if required */ 14398c2ecf20Sopenharmony_ci fix_fullness_group(class, zspage); 14408c2ecf20Sopenharmony_ci record_obj(handle, obj); 14418c2ecf20Sopenharmony_ci spin_unlock(&class->lock); 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci return handle; 14448c2ecf20Sopenharmony_ci } 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci spin_unlock(&class->lock); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci zspage = alloc_zspage(pool, class, gfp); 14498c2ecf20Sopenharmony_ci if (!zspage) { 14508c2ecf20Sopenharmony_ci cache_free_handle(pool, handle); 14518c2ecf20Sopenharmony_ci return 0; 14528c2ecf20Sopenharmony_ci } 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci spin_lock(&class->lock); 14558c2ecf20Sopenharmony_ci obj = obj_malloc(class, zspage, handle); 14568c2ecf20Sopenharmony_ci newfg = get_fullness_group(class, zspage); 14578c2ecf20Sopenharmony_ci insert_zspage(class, zspage, newfg); 14588c2ecf20Sopenharmony_ci set_zspage_mapping(zspage, class->index, newfg); 14598c2ecf20Sopenharmony_ci record_obj(handle, obj); 14608c2ecf20Sopenharmony_ci atomic_long_add(class->pages_per_zspage, 14618c2ecf20Sopenharmony_ci &pool->pages_allocated); 14628c2ecf20Sopenharmony_ci zs_stat_inc(class, OBJ_ALLOCATED, class->objs_per_zspage); 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci /* We completely set up zspage so mark them as movable */ 14658c2ecf20Sopenharmony_ci SetZsPageMovable(pool, zspage); 14668c2ecf20Sopenharmony_ci spin_unlock(&class->lock); 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci return handle; 14698c2ecf20Sopenharmony_ci} 14708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_malloc); 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_cistatic void obj_free(struct size_class *class, unsigned long obj) 14738c2ecf20Sopenharmony_ci{ 14748c2ecf20Sopenharmony_ci struct link_free *link; 14758c2ecf20Sopenharmony_ci struct zspage *zspage; 14768c2ecf20Sopenharmony_ci struct page *f_page; 14778c2ecf20Sopenharmony_ci unsigned long f_offset; 14788c2ecf20Sopenharmony_ci unsigned int f_objidx; 14798c2ecf20Sopenharmony_ci void *vaddr; 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci obj &= ~OBJ_ALLOCATED_TAG; 14828c2ecf20Sopenharmony_ci obj_to_location(obj, &f_page, &f_objidx); 14838c2ecf20Sopenharmony_ci f_offset = (class->size * f_objidx) & ~PAGE_MASK; 14848c2ecf20Sopenharmony_ci zspage = get_zspage(f_page); 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci vaddr = kmap_atomic(f_page); 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci /* Insert this object in containing zspage's freelist */ 14898c2ecf20Sopenharmony_ci link = (struct link_free *)(vaddr + f_offset); 14908c2ecf20Sopenharmony_ci link->next = get_freeobj(zspage) << OBJ_TAG_BITS; 14918c2ecf20Sopenharmony_ci kunmap_atomic(vaddr); 14928c2ecf20Sopenharmony_ci set_freeobj(zspage, f_objidx); 14938c2ecf20Sopenharmony_ci mod_zspage_inuse(zspage, -1); 14948c2ecf20Sopenharmony_ci zs_stat_dec(class, OBJ_USED, 1); 14958c2ecf20Sopenharmony_ci} 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_civoid zs_free(struct zs_pool *pool, unsigned long handle) 14988c2ecf20Sopenharmony_ci{ 14998c2ecf20Sopenharmony_ci struct zspage *zspage; 15008c2ecf20Sopenharmony_ci struct page *f_page; 15018c2ecf20Sopenharmony_ci unsigned long obj; 15028c2ecf20Sopenharmony_ci unsigned int f_objidx; 15038c2ecf20Sopenharmony_ci int class_idx; 15048c2ecf20Sopenharmony_ci struct size_class *class; 15058c2ecf20Sopenharmony_ci enum fullness_group fullness; 15068c2ecf20Sopenharmony_ci bool isolated; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci if (unlikely(!handle)) 15098c2ecf20Sopenharmony_ci return; 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci pin_tag(handle); 15128c2ecf20Sopenharmony_ci obj = handle_to_obj(handle); 15138c2ecf20Sopenharmony_ci obj_to_location(obj, &f_page, &f_objidx); 15148c2ecf20Sopenharmony_ci zspage = get_zspage(f_page); 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci migrate_read_lock(zspage); 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci get_zspage_mapping(zspage, &class_idx, &fullness); 15198c2ecf20Sopenharmony_ci class = pool->size_class[class_idx]; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci spin_lock(&class->lock); 15228c2ecf20Sopenharmony_ci obj_free(class, obj); 15238c2ecf20Sopenharmony_ci fullness = fix_fullness_group(class, zspage); 15248c2ecf20Sopenharmony_ci if (fullness != ZS_EMPTY) { 15258c2ecf20Sopenharmony_ci migrate_read_unlock(zspage); 15268c2ecf20Sopenharmony_ci goto out; 15278c2ecf20Sopenharmony_ci } 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci isolated = is_zspage_isolated(zspage); 15308c2ecf20Sopenharmony_ci migrate_read_unlock(zspage); 15318c2ecf20Sopenharmony_ci /* If zspage is isolated, zs_page_putback will free the zspage */ 15328c2ecf20Sopenharmony_ci if (likely(!isolated)) 15338c2ecf20Sopenharmony_ci free_zspage(pool, class, zspage); 15348c2ecf20Sopenharmony_ciout: 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci spin_unlock(&class->lock); 15378c2ecf20Sopenharmony_ci unpin_tag(handle); 15388c2ecf20Sopenharmony_ci cache_free_handle(pool, handle); 15398c2ecf20Sopenharmony_ci} 15408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_free); 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_cistatic void zs_object_copy(struct size_class *class, unsigned long dst, 15438c2ecf20Sopenharmony_ci unsigned long src) 15448c2ecf20Sopenharmony_ci{ 15458c2ecf20Sopenharmony_ci struct page *s_page, *d_page; 15468c2ecf20Sopenharmony_ci unsigned int s_objidx, d_objidx; 15478c2ecf20Sopenharmony_ci unsigned long s_off, d_off; 15488c2ecf20Sopenharmony_ci void *s_addr, *d_addr; 15498c2ecf20Sopenharmony_ci int s_size, d_size, size; 15508c2ecf20Sopenharmony_ci int written = 0; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci s_size = d_size = class->size; 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci obj_to_location(src, &s_page, &s_objidx); 15558c2ecf20Sopenharmony_ci obj_to_location(dst, &d_page, &d_objidx); 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci s_off = (class->size * s_objidx) & ~PAGE_MASK; 15588c2ecf20Sopenharmony_ci d_off = (class->size * d_objidx) & ~PAGE_MASK; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci if (s_off + class->size > PAGE_SIZE) 15618c2ecf20Sopenharmony_ci s_size = PAGE_SIZE - s_off; 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci if (d_off + class->size > PAGE_SIZE) 15648c2ecf20Sopenharmony_ci d_size = PAGE_SIZE - d_off; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci s_addr = kmap_atomic(s_page); 15678c2ecf20Sopenharmony_ci d_addr = kmap_atomic(d_page); 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci while (1) { 15708c2ecf20Sopenharmony_ci size = min(s_size, d_size); 15718c2ecf20Sopenharmony_ci memcpy(d_addr + d_off, s_addr + s_off, size); 15728c2ecf20Sopenharmony_ci written += size; 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci if (written == class->size) 15758c2ecf20Sopenharmony_ci break; 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci s_off += size; 15788c2ecf20Sopenharmony_ci s_size -= size; 15798c2ecf20Sopenharmony_ci d_off += size; 15808c2ecf20Sopenharmony_ci d_size -= size; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci if (s_off >= PAGE_SIZE) { 15838c2ecf20Sopenharmony_ci kunmap_atomic(d_addr); 15848c2ecf20Sopenharmony_ci kunmap_atomic(s_addr); 15858c2ecf20Sopenharmony_ci s_page = get_next_page(s_page); 15868c2ecf20Sopenharmony_ci s_addr = kmap_atomic(s_page); 15878c2ecf20Sopenharmony_ci d_addr = kmap_atomic(d_page); 15888c2ecf20Sopenharmony_ci s_size = class->size - written; 15898c2ecf20Sopenharmony_ci s_off = 0; 15908c2ecf20Sopenharmony_ci } 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci if (d_off >= PAGE_SIZE) { 15938c2ecf20Sopenharmony_ci kunmap_atomic(d_addr); 15948c2ecf20Sopenharmony_ci d_page = get_next_page(d_page); 15958c2ecf20Sopenharmony_ci d_addr = kmap_atomic(d_page); 15968c2ecf20Sopenharmony_ci d_size = class->size - written; 15978c2ecf20Sopenharmony_ci d_off = 0; 15988c2ecf20Sopenharmony_ci } 15998c2ecf20Sopenharmony_ci } 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci kunmap_atomic(d_addr); 16028c2ecf20Sopenharmony_ci kunmap_atomic(s_addr); 16038c2ecf20Sopenharmony_ci} 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci/* 16068c2ecf20Sopenharmony_ci * Find alloced object in zspage from index object and 16078c2ecf20Sopenharmony_ci * return handle. 16088c2ecf20Sopenharmony_ci */ 16098c2ecf20Sopenharmony_cistatic unsigned long find_alloced_obj(struct size_class *class, 16108c2ecf20Sopenharmony_ci struct page *page, int *obj_idx) 16118c2ecf20Sopenharmony_ci{ 16128c2ecf20Sopenharmony_ci unsigned long head; 16138c2ecf20Sopenharmony_ci int offset = 0; 16148c2ecf20Sopenharmony_ci int index = *obj_idx; 16158c2ecf20Sopenharmony_ci unsigned long handle = 0; 16168c2ecf20Sopenharmony_ci void *addr = kmap_atomic(page); 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci offset = get_first_obj_offset(page); 16198c2ecf20Sopenharmony_ci offset += class->size * index; 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci while (offset < PAGE_SIZE) { 16228c2ecf20Sopenharmony_ci head = obj_to_head(page, addr + offset); 16238c2ecf20Sopenharmony_ci if (head & OBJ_ALLOCATED_TAG) { 16248c2ecf20Sopenharmony_ci handle = head & ~OBJ_ALLOCATED_TAG; 16258c2ecf20Sopenharmony_ci if (trypin_tag(handle)) 16268c2ecf20Sopenharmony_ci break; 16278c2ecf20Sopenharmony_ci handle = 0; 16288c2ecf20Sopenharmony_ci } 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci offset += class->size; 16318c2ecf20Sopenharmony_ci index++; 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci kunmap_atomic(addr); 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci *obj_idx = index; 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci return handle; 16398c2ecf20Sopenharmony_ci} 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_cistruct zs_compact_control { 16428c2ecf20Sopenharmony_ci /* Source spage for migration which could be a subpage of zspage */ 16438c2ecf20Sopenharmony_ci struct page *s_page; 16448c2ecf20Sopenharmony_ci /* Destination page for migration which should be a first page 16458c2ecf20Sopenharmony_ci * of zspage. */ 16468c2ecf20Sopenharmony_ci struct page *d_page; 16478c2ecf20Sopenharmony_ci /* Starting object index within @s_page which used for live object 16488c2ecf20Sopenharmony_ci * in the subpage. */ 16498c2ecf20Sopenharmony_ci int obj_idx; 16508c2ecf20Sopenharmony_ci}; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_cistatic int migrate_zspage(struct zs_pool *pool, struct size_class *class, 16538c2ecf20Sopenharmony_ci struct zs_compact_control *cc) 16548c2ecf20Sopenharmony_ci{ 16558c2ecf20Sopenharmony_ci unsigned long used_obj, free_obj; 16568c2ecf20Sopenharmony_ci unsigned long handle; 16578c2ecf20Sopenharmony_ci struct page *s_page = cc->s_page; 16588c2ecf20Sopenharmony_ci struct page *d_page = cc->d_page; 16598c2ecf20Sopenharmony_ci int obj_idx = cc->obj_idx; 16608c2ecf20Sopenharmony_ci int ret = 0; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci while (1) { 16638c2ecf20Sopenharmony_ci handle = find_alloced_obj(class, s_page, &obj_idx); 16648c2ecf20Sopenharmony_ci if (!handle) { 16658c2ecf20Sopenharmony_ci s_page = get_next_page(s_page); 16668c2ecf20Sopenharmony_ci if (!s_page) 16678c2ecf20Sopenharmony_ci break; 16688c2ecf20Sopenharmony_ci obj_idx = 0; 16698c2ecf20Sopenharmony_ci continue; 16708c2ecf20Sopenharmony_ci } 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci /* Stop if there is no more space */ 16738c2ecf20Sopenharmony_ci if (zspage_full(class, get_zspage(d_page))) { 16748c2ecf20Sopenharmony_ci unpin_tag(handle); 16758c2ecf20Sopenharmony_ci ret = -ENOMEM; 16768c2ecf20Sopenharmony_ci break; 16778c2ecf20Sopenharmony_ci } 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci used_obj = handle_to_obj(handle); 16808c2ecf20Sopenharmony_ci free_obj = obj_malloc(class, get_zspage(d_page), handle); 16818c2ecf20Sopenharmony_ci zs_object_copy(class, free_obj, used_obj); 16828c2ecf20Sopenharmony_ci obj_idx++; 16838c2ecf20Sopenharmony_ci /* 16848c2ecf20Sopenharmony_ci * record_obj updates handle's value to free_obj and it will 16858c2ecf20Sopenharmony_ci * invalidate lock bit(ie, HANDLE_PIN_BIT) of handle, which 16868c2ecf20Sopenharmony_ci * breaks synchronization using pin_tag(e,g, zs_free) so 16878c2ecf20Sopenharmony_ci * let's keep the lock bit. 16888c2ecf20Sopenharmony_ci */ 16898c2ecf20Sopenharmony_ci free_obj |= BIT(HANDLE_PIN_BIT); 16908c2ecf20Sopenharmony_ci record_obj(handle, free_obj); 16918c2ecf20Sopenharmony_ci unpin_tag(handle); 16928c2ecf20Sopenharmony_ci obj_free(class, used_obj); 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci /* Remember last position in this iteration */ 16968c2ecf20Sopenharmony_ci cc->s_page = s_page; 16978c2ecf20Sopenharmony_ci cc->obj_idx = obj_idx; 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci return ret; 17008c2ecf20Sopenharmony_ci} 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_cistatic struct zspage *isolate_zspage(struct size_class *class, bool source) 17038c2ecf20Sopenharmony_ci{ 17048c2ecf20Sopenharmony_ci int i; 17058c2ecf20Sopenharmony_ci struct zspage *zspage; 17068c2ecf20Sopenharmony_ci enum fullness_group fg[2] = {ZS_ALMOST_EMPTY, ZS_ALMOST_FULL}; 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci if (!source) { 17098c2ecf20Sopenharmony_ci fg[0] = ZS_ALMOST_FULL; 17108c2ecf20Sopenharmony_ci fg[1] = ZS_ALMOST_EMPTY; 17118c2ecf20Sopenharmony_ci } 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 17148c2ecf20Sopenharmony_ci zspage = list_first_entry_or_null(&class->fullness_list[fg[i]], 17158c2ecf20Sopenharmony_ci struct zspage, list); 17168c2ecf20Sopenharmony_ci if (zspage) { 17178c2ecf20Sopenharmony_ci VM_BUG_ON(is_zspage_isolated(zspage)); 17188c2ecf20Sopenharmony_ci remove_zspage(class, zspage, fg[i]); 17198c2ecf20Sopenharmony_ci return zspage; 17208c2ecf20Sopenharmony_ci } 17218c2ecf20Sopenharmony_ci } 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci return zspage; 17248c2ecf20Sopenharmony_ci} 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci/* 17278c2ecf20Sopenharmony_ci * putback_zspage - add @zspage into right class's fullness list 17288c2ecf20Sopenharmony_ci * @class: destination class 17298c2ecf20Sopenharmony_ci * @zspage: target page 17308c2ecf20Sopenharmony_ci * 17318c2ecf20Sopenharmony_ci * Return @zspage's fullness_group 17328c2ecf20Sopenharmony_ci */ 17338c2ecf20Sopenharmony_cistatic enum fullness_group putback_zspage(struct size_class *class, 17348c2ecf20Sopenharmony_ci struct zspage *zspage) 17358c2ecf20Sopenharmony_ci{ 17368c2ecf20Sopenharmony_ci enum fullness_group fullness; 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci VM_BUG_ON(is_zspage_isolated(zspage)); 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci fullness = get_fullness_group(class, zspage); 17418c2ecf20Sopenharmony_ci insert_zspage(class, zspage, fullness); 17428c2ecf20Sopenharmony_ci set_zspage_mapping(zspage, class->index, fullness); 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci return fullness; 17458c2ecf20Sopenharmony_ci} 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPACTION 17488c2ecf20Sopenharmony_ci/* 17498c2ecf20Sopenharmony_ci * To prevent zspage destroy during migration, zspage freeing should 17508c2ecf20Sopenharmony_ci * hold locks of all pages in the zspage. 17518c2ecf20Sopenharmony_ci */ 17528c2ecf20Sopenharmony_cistatic void lock_zspage(struct zspage *zspage) 17538c2ecf20Sopenharmony_ci{ 17548c2ecf20Sopenharmony_ci struct page *curr_page, *page; 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci /* 17578c2ecf20Sopenharmony_ci * Pages we haven't locked yet can be migrated off the list while we're 17588c2ecf20Sopenharmony_ci * trying to lock them, so we need to be careful and only attempt to 17598c2ecf20Sopenharmony_ci * lock each page under migrate_read_lock(). Otherwise, the page we lock 17608c2ecf20Sopenharmony_ci * may no longer belong to the zspage. This means that we may wait for 17618c2ecf20Sopenharmony_ci * the wrong page to unlock, so we must take a reference to the page 17628c2ecf20Sopenharmony_ci * prior to waiting for it to unlock outside migrate_read_lock(). 17638c2ecf20Sopenharmony_ci */ 17648c2ecf20Sopenharmony_ci while (1) { 17658c2ecf20Sopenharmony_ci migrate_read_lock(zspage); 17668c2ecf20Sopenharmony_ci page = get_first_page(zspage); 17678c2ecf20Sopenharmony_ci if (trylock_page(page)) 17688c2ecf20Sopenharmony_ci break; 17698c2ecf20Sopenharmony_ci get_page(page); 17708c2ecf20Sopenharmony_ci migrate_read_unlock(zspage); 17718c2ecf20Sopenharmony_ci wait_on_page_locked(page); 17728c2ecf20Sopenharmony_ci put_page(page); 17738c2ecf20Sopenharmony_ci } 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci curr_page = page; 17768c2ecf20Sopenharmony_ci while ((page = get_next_page(curr_page))) { 17778c2ecf20Sopenharmony_ci if (trylock_page(page)) { 17788c2ecf20Sopenharmony_ci curr_page = page; 17798c2ecf20Sopenharmony_ci } else { 17808c2ecf20Sopenharmony_ci get_page(page); 17818c2ecf20Sopenharmony_ci migrate_read_unlock(zspage); 17828c2ecf20Sopenharmony_ci wait_on_page_locked(page); 17838c2ecf20Sopenharmony_ci put_page(page); 17848c2ecf20Sopenharmony_ci migrate_read_lock(zspage); 17858c2ecf20Sopenharmony_ci } 17868c2ecf20Sopenharmony_ci } 17878c2ecf20Sopenharmony_ci migrate_read_unlock(zspage); 17888c2ecf20Sopenharmony_ci} 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_cistatic int zs_init_fs_context(struct fs_context *fc) 17918c2ecf20Sopenharmony_ci{ 17928c2ecf20Sopenharmony_ci return init_pseudo(fc, ZSMALLOC_MAGIC) ? 0 : -ENOMEM; 17938c2ecf20Sopenharmony_ci} 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_cistatic struct file_system_type zsmalloc_fs = { 17968c2ecf20Sopenharmony_ci .name = "zsmalloc", 17978c2ecf20Sopenharmony_ci .init_fs_context = zs_init_fs_context, 17988c2ecf20Sopenharmony_ci .kill_sb = kill_anon_super, 17998c2ecf20Sopenharmony_ci}; 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_cistatic int zsmalloc_mount(void) 18028c2ecf20Sopenharmony_ci{ 18038c2ecf20Sopenharmony_ci int ret = 0; 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci zsmalloc_mnt = kern_mount(&zsmalloc_fs); 18068c2ecf20Sopenharmony_ci if (IS_ERR(zsmalloc_mnt)) 18078c2ecf20Sopenharmony_ci ret = PTR_ERR(zsmalloc_mnt); 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci return ret; 18108c2ecf20Sopenharmony_ci} 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_cistatic void zsmalloc_unmount(void) 18138c2ecf20Sopenharmony_ci{ 18148c2ecf20Sopenharmony_ci kern_unmount(zsmalloc_mnt); 18158c2ecf20Sopenharmony_ci} 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_cistatic void migrate_lock_init(struct zspage *zspage) 18188c2ecf20Sopenharmony_ci{ 18198c2ecf20Sopenharmony_ci rwlock_init(&zspage->lock); 18208c2ecf20Sopenharmony_ci} 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_cistatic void migrate_read_lock(struct zspage *zspage) __acquires(&zspage->lock) 18238c2ecf20Sopenharmony_ci{ 18248c2ecf20Sopenharmony_ci read_lock(&zspage->lock); 18258c2ecf20Sopenharmony_ci} 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_cistatic void migrate_read_unlock(struct zspage *zspage) __releases(&zspage->lock) 18288c2ecf20Sopenharmony_ci{ 18298c2ecf20Sopenharmony_ci read_unlock(&zspage->lock); 18308c2ecf20Sopenharmony_ci} 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_cistatic void migrate_write_lock(struct zspage *zspage) 18338c2ecf20Sopenharmony_ci{ 18348c2ecf20Sopenharmony_ci write_lock(&zspage->lock); 18358c2ecf20Sopenharmony_ci} 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_cistatic void migrate_write_unlock(struct zspage *zspage) 18388c2ecf20Sopenharmony_ci{ 18398c2ecf20Sopenharmony_ci write_unlock(&zspage->lock); 18408c2ecf20Sopenharmony_ci} 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci/* Number of isolated subpage for *page migration* in this zspage */ 18438c2ecf20Sopenharmony_cistatic void inc_zspage_isolation(struct zspage *zspage) 18448c2ecf20Sopenharmony_ci{ 18458c2ecf20Sopenharmony_ci zspage->isolated++; 18468c2ecf20Sopenharmony_ci} 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_cistatic void dec_zspage_isolation(struct zspage *zspage) 18498c2ecf20Sopenharmony_ci{ 18508c2ecf20Sopenharmony_ci zspage->isolated--; 18518c2ecf20Sopenharmony_ci} 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_cistatic void putback_zspage_deferred(struct zs_pool *pool, 18548c2ecf20Sopenharmony_ci struct size_class *class, 18558c2ecf20Sopenharmony_ci struct zspage *zspage) 18568c2ecf20Sopenharmony_ci{ 18578c2ecf20Sopenharmony_ci enum fullness_group fg; 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci fg = putback_zspage(class, zspage); 18608c2ecf20Sopenharmony_ci if (fg == ZS_EMPTY) 18618c2ecf20Sopenharmony_ci schedule_work(&pool->free_work); 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci} 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_cistatic inline void zs_pool_dec_isolated(struct zs_pool *pool) 18668c2ecf20Sopenharmony_ci{ 18678c2ecf20Sopenharmony_ci VM_BUG_ON(atomic_long_read(&pool->isolated_pages) <= 0); 18688c2ecf20Sopenharmony_ci atomic_long_dec(&pool->isolated_pages); 18698c2ecf20Sopenharmony_ci /* 18708c2ecf20Sopenharmony_ci * Checking pool->destroying must happen after atomic_long_dec() 18718c2ecf20Sopenharmony_ci * for pool->isolated_pages above. Paired with the smp_mb() in 18728c2ecf20Sopenharmony_ci * zs_unregister_migration(). 18738c2ecf20Sopenharmony_ci */ 18748c2ecf20Sopenharmony_ci smp_mb__after_atomic(); 18758c2ecf20Sopenharmony_ci if (atomic_long_read(&pool->isolated_pages) == 0 && pool->destroying) 18768c2ecf20Sopenharmony_ci wake_up_all(&pool->migration_wait); 18778c2ecf20Sopenharmony_ci} 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_cistatic void replace_sub_page(struct size_class *class, struct zspage *zspage, 18808c2ecf20Sopenharmony_ci struct page *newpage, struct page *oldpage) 18818c2ecf20Sopenharmony_ci{ 18828c2ecf20Sopenharmony_ci struct page *page; 18838c2ecf20Sopenharmony_ci struct page *pages[ZS_MAX_PAGES_PER_ZSPAGE] = {NULL, }; 18848c2ecf20Sopenharmony_ci int idx = 0; 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci page = get_first_page(zspage); 18878c2ecf20Sopenharmony_ci do { 18888c2ecf20Sopenharmony_ci if (page == oldpage) 18898c2ecf20Sopenharmony_ci pages[idx] = newpage; 18908c2ecf20Sopenharmony_ci else 18918c2ecf20Sopenharmony_ci pages[idx] = page; 18928c2ecf20Sopenharmony_ci idx++; 18938c2ecf20Sopenharmony_ci } while ((page = get_next_page(page)) != NULL); 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci create_page_chain(class, zspage, pages); 18968c2ecf20Sopenharmony_ci set_first_obj_offset(newpage, get_first_obj_offset(oldpage)); 18978c2ecf20Sopenharmony_ci if (unlikely(PageHugeObject(oldpage))) 18988c2ecf20Sopenharmony_ci newpage->index = oldpage->index; 18998c2ecf20Sopenharmony_ci __SetPageMovable(newpage, page_mapping(oldpage)); 19008c2ecf20Sopenharmony_ci} 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_cistatic bool zs_page_isolate(struct page *page, isolate_mode_t mode) 19038c2ecf20Sopenharmony_ci{ 19048c2ecf20Sopenharmony_ci struct zs_pool *pool; 19058c2ecf20Sopenharmony_ci struct size_class *class; 19068c2ecf20Sopenharmony_ci int class_idx; 19078c2ecf20Sopenharmony_ci enum fullness_group fullness; 19088c2ecf20Sopenharmony_ci struct zspage *zspage; 19098c2ecf20Sopenharmony_ci struct address_space *mapping; 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci /* 19128c2ecf20Sopenharmony_ci * Page is locked so zspage couldn't be destroyed. For detail, look at 19138c2ecf20Sopenharmony_ci * lock_zspage in free_zspage. 19148c2ecf20Sopenharmony_ci */ 19158c2ecf20Sopenharmony_ci VM_BUG_ON_PAGE(!PageMovable(page), page); 19168c2ecf20Sopenharmony_ci VM_BUG_ON_PAGE(PageIsolated(page), page); 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci zspage = get_zspage(page); 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci /* 19218c2ecf20Sopenharmony_ci * Without class lock, fullness could be stale while class_idx is okay 19228c2ecf20Sopenharmony_ci * because class_idx is constant unless page is freed so we should get 19238c2ecf20Sopenharmony_ci * fullness again under class lock. 19248c2ecf20Sopenharmony_ci */ 19258c2ecf20Sopenharmony_ci get_zspage_mapping(zspage, &class_idx, &fullness); 19268c2ecf20Sopenharmony_ci mapping = page_mapping(page); 19278c2ecf20Sopenharmony_ci pool = mapping->private_data; 19288c2ecf20Sopenharmony_ci class = pool->size_class[class_idx]; 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci spin_lock(&class->lock); 19318c2ecf20Sopenharmony_ci if (get_zspage_inuse(zspage) == 0) { 19328c2ecf20Sopenharmony_ci spin_unlock(&class->lock); 19338c2ecf20Sopenharmony_ci return false; 19348c2ecf20Sopenharmony_ci } 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci /* zspage is isolated for object migration */ 19378c2ecf20Sopenharmony_ci if (list_empty(&zspage->list) && !is_zspage_isolated(zspage)) { 19388c2ecf20Sopenharmony_ci spin_unlock(&class->lock); 19398c2ecf20Sopenharmony_ci return false; 19408c2ecf20Sopenharmony_ci } 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci /* 19438c2ecf20Sopenharmony_ci * If this is first time isolation for the zspage, isolate zspage from 19448c2ecf20Sopenharmony_ci * size_class to prevent further object allocation from the zspage. 19458c2ecf20Sopenharmony_ci */ 19468c2ecf20Sopenharmony_ci if (!list_empty(&zspage->list) && !is_zspage_isolated(zspage)) { 19478c2ecf20Sopenharmony_ci get_zspage_mapping(zspage, &class_idx, &fullness); 19488c2ecf20Sopenharmony_ci atomic_long_inc(&pool->isolated_pages); 19498c2ecf20Sopenharmony_ci remove_zspage(class, zspage, fullness); 19508c2ecf20Sopenharmony_ci } 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci inc_zspage_isolation(zspage); 19538c2ecf20Sopenharmony_ci spin_unlock(&class->lock); 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci return true; 19568c2ecf20Sopenharmony_ci} 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_cistatic int zs_page_migrate(struct address_space *mapping, struct page *newpage, 19598c2ecf20Sopenharmony_ci struct page *page, enum migrate_mode mode) 19608c2ecf20Sopenharmony_ci{ 19618c2ecf20Sopenharmony_ci struct zs_pool *pool; 19628c2ecf20Sopenharmony_ci struct size_class *class; 19638c2ecf20Sopenharmony_ci int class_idx; 19648c2ecf20Sopenharmony_ci enum fullness_group fullness; 19658c2ecf20Sopenharmony_ci struct zspage *zspage; 19668c2ecf20Sopenharmony_ci struct page *dummy; 19678c2ecf20Sopenharmony_ci void *s_addr, *d_addr, *addr; 19688c2ecf20Sopenharmony_ci int offset, pos; 19698c2ecf20Sopenharmony_ci unsigned long handle, head; 19708c2ecf20Sopenharmony_ci unsigned long old_obj, new_obj; 19718c2ecf20Sopenharmony_ci unsigned int obj_idx; 19728c2ecf20Sopenharmony_ci int ret = -EAGAIN; 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci /* 19758c2ecf20Sopenharmony_ci * We cannot support the _NO_COPY case here, because copy needs to 19768c2ecf20Sopenharmony_ci * happen under the zs lock, which does not work with 19778c2ecf20Sopenharmony_ci * MIGRATE_SYNC_NO_COPY workflow. 19788c2ecf20Sopenharmony_ci */ 19798c2ecf20Sopenharmony_ci if (mode == MIGRATE_SYNC_NO_COPY) 19808c2ecf20Sopenharmony_ci return -EINVAL; 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci VM_BUG_ON_PAGE(!PageMovable(page), page); 19838c2ecf20Sopenharmony_ci VM_BUG_ON_PAGE(!PageIsolated(page), page); 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci zspage = get_zspage(page); 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci /* Concurrent compactor cannot migrate any subpage in zspage */ 19888c2ecf20Sopenharmony_ci migrate_write_lock(zspage); 19898c2ecf20Sopenharmony_ci get_zspage_mapping(zspage, &class_idx, &fullness); 19908c2ecf20Sopenharmony_ci pool = mapping->private_data; 19918c2ecf20Sopenharmony_ci class = pool->size_class[class_idx]; 19928c2ecf20Sopenharmony_ci offset = get_first_obj_offset(page); 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci spin_lock(&class->lock); 19958c2ecf20Sopenharmony_ci if (!get_zspage_inuse(zspage)) { 19968c2ecf20Sopenharmony_ci /* 19978c2ecf20Sopenharmony_ci * Set "offset" to end of the page so that every loops 19988c2ecf20Sopenharmony_ci * skips unnecessary object scanning. 19998c2ecf20Sopenharmony_ci */ 20008c2ecf20Sopenharmony_ci offset = PAGE_SIZE; 20018c2ecf20Sopenharmony_ci } 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci pos = offset; 20048c2ecf20Sopenharmony_ci s_addr = kmap_atomic(page); 20058c2ecf20Sopenharmony_ci while (pos < PAGE_SIZE) { 20068c2ecf20Sopenharmony_ci head = obj_to_head(page, s_addr + pos); 20078c2ecf20Sopenharmony_ci if (head & OBJ_ALLOCATED_TAG) { 20088c2ecf20Sopenharmony_ci handle = head & ~OBJ_ALLOCATED_TAG; 20098c2ecf20Sopenharmony_ci if (!trypin_tag(handle)) 20108c2ecf20Sopenharmony_ci goto unpin_objects; 20118c2ecf20Sopenharmony_ci } 20128c2ecf20Sopenharmony_ci pos += class->size; 20138c2ecf20Sopenharmony_ci } 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci /* 20168c2ecf20Sopenharmony_ci * Here, any user cannot access all objects in the zspage so let's move. 20178c2ecf20Sopenharmony_ci */ 20188c2ecf20Sopenharmony_ci d_addr = kmap_atomic(newpage); 20198c2ecf20Sopenharmony_ci memcpy(d_addr, s_addr, PAGE_SIZE); 20208c2ecf20Sopenharmony_ci kunmap_atomic(d_addr); 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci for (addr = s_addr + offset; addr < s_addr + pos; 20238c2ecf20Sopenharmony_ci addr += class->size) { 20248c2ecf20Sopenharmony_ci head = obj_to_head(page, addr); 20258c2ecf20Sopenharmony_ci if (head & OBJ_ALLOCATED_TAG) { 20268c2ecf20Sopenharmony_ci handle = head & ~OBJ_ALLOCATED_TAG; 20278c2ecf20Sopenharmony_ci if (!testpin_tag(handle)) 20288c2ecf20Sopenharmony_ci BUG(); 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci old_obj = handle_to_obj(handle); 20318c2ecf20Sopenharmony_ci obj_to_location(old_obj, &dummy, &obj_idx); 20328c2ecf20Sopenharmony_ci new_obj = (unsigned long)location_to_obj(newpage, 20338c2ecf20Sopenharmony_ci obj_idx); 20348c2ecf20Sopenharmony_ci new_obj |= BIT(HANDLE_PIN_BIT); 20358c2ecf20Sopenharmony_ci record_obj(handle, new_obj); 20368c2ecf20Sopenharmony_ci } 20378c2ecf20Sopenharmony_ci } 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci replace_sub_page(class, zspage, newpage, page); 20408c2ecf20Sopenharmony_ci get_page(newpage); 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci dec_zspage_isolation(zspage); 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci /* 20458c2ecf20Sopenharmony_ci * Page migration is done so let's putback isolated zspage to 20468c2ecf20Sopenharmony_ci * the list if @page is final isolated subpage in the zspage. 20478c2ecf20Sopenharmony_ci */ 20488c2ecf20Sopenharmony_ci if (!is_zspage_isolated(zspage)) { 20498c2ecf20Sopenharmony_ci /* 20508c2ecf20Sopenharmony_ci * We cannot race with zs_destroy_pool() here because we wait 20518c2ecf20Sopenharmony_ci * for isolation to hit zero before we start destroying. 20528c2ecf20Sopenharmony_ci * Also, we ensure that everyone can see pool->destroying before 20538c2ecf20Sopenharmony_ci * we start waiting. 20548c2ecf20Sopenharmony_ci */ 20558c2ecf20Sopenharmony_ci putback_zspage_deferred(pool, class, zspage); 20568c2ecf20Sopenharmony_ci zs_pool_dec_isolated(pool); 20578c2ecf20Sopenharmony_ci } 20588c2ecf20Sopenharmony_ci 20598c2ecf20Sopenharmony_ci if (page_zone(newpage) != page_zone(page)) { 20608c2ecf20Sopenharmony_ci dec_zone_page_state(page, NR_ZSPAGES); 20618c2ecf20Sopenharmony_ci inc_zone_page_state(newpage, NR_ZSPAGES); 20628c2ecf20Sopenharmony_ci } 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci reset_page(page); 20658c2ecf20Sopenharmony_ci put_page(page); 20668c2ecf20Sopenharmony_ci page = newpage; 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci ret = MIGRATEPAGE_SUCCESS; 20698c2ecf20Sopenharmony_ciunpin_objects: 20708c2ecf20Sopenharmony_ci for (addr = s_addr + offset; addr < s_addr + pos; 20718c2ecf20Sopenharmony_ci addr += class->size) { 20728c2ecf20Sopenharmony_ci head = obj_to_head(page, addr); 20738c2ecf20Sopenharmony_ci if (head & OBJ_ALLOCATED_TAG) { 20748c2ecf20Sopenharmony_ci handle = head & ~OBJ_ALLOCATED_TAG; 20758c2ecf20Sopenharmony_ci if (!testpin_tag(handle)) 20768c2ecf20Sopenharmony_ci BUG(); 20778c2ecf20Sopenharmony_ci unpin_tag(handle); 20788c2ecf20Sopenharmony_ci } 20798c2ecf20Sopenharmony_ci } 20808c2ecf20Sopenharmony_ci kunmap_atomic(s_addr); 20818c2ecf20Sopenharmony_ci spin_unlock(&class->lock); 20828c2ecf20Sopenharmony_ci migrate_write_unlock(zspage); 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci return ret; 20858c2ecf20Sopenharmony_ci} 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_cistatic void zs_page_putback(struct page *page) 20888c2ecf20Sopenharmony_ci{ 20898c2ecf20Sopenharmony_ci struct zs_pool *pool; 20908c2ecf20Sopenharmony_ci struct size_class *class; 20918c2ecf20Sopenharmony_ci int class_idx; 20928c2ecf20Sopenharmony_ci enum fullness_group fg; 20938c2ecf20Sopenharmony_ci struct address_space *mapping; 20948c2ecf20Sopenharmony_ci struct zspage *zspage; 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci VM_BUG_ON_PAGE(!PageMovable(page), page); 20978c2ecf20Sopenharmony_ci VM_BUG_ON_PAGE(!PageIsolated(page), page); 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci zspage = get_zspage(page); 21008c2ecf20Sopenharmony_ci get_zspage_mapping(zspage, &class_idx, &fg); 21018c2ecf20Sopenharmony_ci mapping = page_mapping(page); 21028c2ecf20Sopenharmony_ci pool = mapping->private_data; 21038c2ecf20Sopenharmony_ci class = pool->size_class[class_idx]; 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci spin_lock(&class->lock); 21068c2ecf20Sopenharmony_ci dec_zspage_isolation(zspage); 21078c2ecf20Sopenharmony_ci if (!is_zspage_isolated(zspage)) { 21088c2ecf20Sopenharmony_ci /* 21098c2ecf20Sopenharmony_ci * Due to page_lock, we cannot free zspage immediately 21108c2ecf20Sopenharmony_ci * so let's defer. 21118c2ecf20Sopenharmony_ci */ 21128c2ecf20Sopenharmony_ci putback_zspage_deferred(pool, class, zspage); 21138c2ecf20Sopenharmony_ci zs_pool_dec_isolated(pool); 21148c2ecf20Sopenharmony_ci } 21158c2ecf20Sopenharmony_ci spin_unlock(&class->lock); 21168c2ecf20Sopenharmony_ci} 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_cistatic const struct address_space_operations zsmalloc_aops = { 21198c2ecf20Sopenharmony_ci .isolate_page = zs_page_isolate, 21208c2ecf20Sopenharmony_ci .migratepage = zs_page_migrate, 21218c2ecf20Sopenharmony_ci .putback_page = zs_page_putback, 21228c2ecf20Sopenharmony_ci}; 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_cistatic int zs_register_migration(struct zs_pool *pool) 21258c2ecf20Sopenharmony_ci{ 21268c2ecf20Sopenharmony_ci pool->inode = alloc_anon_inode(zsmalloc_mnt->mnt_sb); 21278c2ecf20Sopenharmony_ci if (IS_ERR(pool->inode)) { 21288c2ecf20Sopenharmony_ci pool->inode = NULL; 21298c2ecf20Sopenharmony_ci return 1; 21308c2ecf20Sopenharmony_ci } 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_ci pool->inode->i_mapping->private_data = pool; 21338c2ecf20Sopenharmony_ci pool->inode->i_mapping->a_ops = &zsmalloc_aops; 21348c2ecf20Sopenharmony_ci return 0; 21358c2ecf20Sopenharmony_ci} 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_cistatic bool pool_isolated_are_drained(struct zs_pool *pool) 21388c2ecf20Sopenharmony_ci{ 21398c2ecf20Sopenharmony_ci return atomic_long_read(&pool->isolated_pages) == 0; 21408c2ecf20Sopenharmony_ci} 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_ci/* Function for resolving migration */ 21438c2ecf20Sopenharmony_cistatic void wait_for_isolated_drain(struct zs_pool *pool) 21448c2ecf20Sopenharmony_ci{ 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci /* 21478c2ecf20Sopenharmony_ci * We're in the process of destroying the pool, so there are no 21488c2ecf20Sopenharmony_ci * active allocations. zs_page_isolate() fails for completely free 21498c2ecf20Sopenharmony_ci * zspages, so we need only wait for the zs_pool's isolated 21508c2ecf20Sopenharmony_ci * count to hit zero. 21518c2ecf20Sopenharmony_ci */ 21528c2ecf20Sopenharmony_ci wait_event(pool->migration_wait, 21538c2ecf20Sopenharmony_ci pool_isolated_are_drained(pool)); 21548c2ecf20Sopenharmony_ci} 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_cistatic void zs_unregister_migration(struct zs_pool *pool) 21578c2ecf20Sopenharmony_ci{ 21588c2ecf20Sopenharmony_ci pool->destroying = true; 21598c2ecf20Sopenharmony_ci /* 21608c2ecf20Sopenharmony_ci * We need a memory barrier here to ensure global visibility of 21618c2ecf20Sopenharmony_ci * pool->destroying. Thus pool->isolated pages will either be 0 in which 21628c2ecf20Sopenharmony_ci * case we don't care, or it will be > 0 and pool->destroying will 21638c2ecf20Sopenharmony_ci * ensure that we wake up once isolation hits 0. 21648c2ecf20Sopenharmony_ci */ 21658c2ecf20Sopenharmony_ci smp_mb(); 21668c2ecf20Sopenharmony_ci wait_for_isolated_drain(pool); /* This can block */ 21678c2ecf20Sopenharmony_ci flush_work(&pool->free_work); 21688c2ecf20Sopenharmony_ci iput(pool->inode); 21698c2ecf20Sopenharmony_ci} 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_ci/* 21728c2ecf20Sopenharmony_ci * Caller should hold page_lock of all pages in the zspage 21738c2ecf20Sopenharmony_ci * In here, we cannot use zspage meta data. 21748c2ecf20Sopenharmony_ci */ 21758c2ecf20Sopenharmony_cistatic void async_free_zspage(struct work_struct *work) 21768c2ecf20Sopenharmony_ci{ 21778c2ecf20Sopenharmony_ci int i; 21788c2ecf20Sopenharmony_ci struct size_class *class; 21798c2ecf20Sopenharmony_ci unsigned int class_idx; 21808c2ecf20Sopenharmony_ci enum fullness_group fullness; 21818c2ecf20Sopenharmony_ci struct zspage *zspage, *tmp; 21828c2ecf20Sopenharmony_ci LIST_HEAD(free_pages); 21838c2ecf20Sopenharmony_ci struct zs_pool *pool = container_of(work, struct zs_pool, 21848c2ecf20Sopenharmony_ci free_work); 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci for (i = 0; i < ZS_SIZE_CLASSES; i++) { 21878c2ecf20Sopenharmony_ci class = pool->size_class[i]; 21888c2ecf20Sopenharmony_ci if (class->index != i) 21898c2ecf20Sopenharmony_ci continue; 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci spin_lock(&class->lock); 21928c2ecf20Sopenharmony_ci list_splice_init(&class->fullness_list[ZS_EMPTY], &free_pages); 21938c2ecf20Sopenharmony_ci spin_unlock(&class->lock); 21948c2ecf20Sopenharmony_ci } 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci list_for_each_entry_safe(zspage, tmp, &free_pages, list) { 21988c2ecf20Sopenharmony_ci list_del(&zspage->list); 21998c2ecf20Sopenharmony_ci lock_zspage(zspage); 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci get_zspage_mapping(zspage, &class_idx, &fullness); 22028c2ecf20Sopenharmony_ci VM_BUG_ON(fullness != ZS_EMPTY); 22038c2ecf20Sopenharmony_ci class = pool->size_class[class_idx]; 22048c2ecf20Sopenharmony_ci spin_lock(&class->lock); 22058c2ecf20Sopenharmony_ci __free_zspage(pool, pool->size_class[class_idx], zspage); 22068c2ecf20Sopenharmony_ci spin_unlock(&class->lock); 22078c2ecf20Sopenharmony_ci } 22088c2ecf20Sopenharmony_ci}; 22098c2ecf20Sopenharmony_ci 22108c2ecf20Sopenharmony_cistatic void kick_deferred_free(struct zs_pool *pool) 22118c2ecf20Sopenharmony_ci{ 22128c2ecf20Sopenharmony_ci schedule_work(&pool->free_work); 22138c2ecf20Sopenharmony_ci} 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_cistatic void init_deferred_free(struct zs_pool *pool) 22168c2ecf20Sopenharmony_ci{ 22178c2ecf20Sopenharmony_ci INIT_WORK(&pool->free_work, async_free_zspage); 22188c2ecf20Sopenharmony_ci} 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_cistatic void SetZsPageMovable(struct zs_pool *pool, struct zspage *zspage) 22218c2ecf20Sopenharmony_ci{ 22228c2ecf20Sopenharmony_ci struct page *page = get_first_page(zspage); 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci do { 22258c2ecf20Sopenharmony_ci WARN_ON(!trylock_page(page)); 22268c2ecf20Sopenharmony_ci __SetPageMovable(page, pool->inode->i_mapping); 22278c2ecf20Sopenharmony_ci unlock_page(page); 22288c2ecf20Sopenharmony_ci } while ((page = get_next_page(page)) != NULL); 22298c2ecf20Sopenharmony_ci} 22308c2ecf20Sopenharmony_ci#endif 22318c2ecf20Sopenharmony_ci 22328c2ecf20Sopenharmony_ci/* 22338c2ecf20Sopenharmony_ci * 22348c2ecf20Sopenharmony_ci * Based on the number of unused allocated objects calculate 22358c2ecf20Sopenharmony_ci * and return the number of pages that we can free. 22368c2ecf20Sopenharmony_ci */ 22378c2ecf20Sopenharmony_cistatic unsigned long zs_can_compact(struct size_class *class) 22388c2ecf20Sopenharmony_ci{ 22398c2ecf20Sopenharmony_ci unsigned long obj_wasted; 22408c2ecf20Sopenharmony_ci unsigned long obj_allocated = zs_stat_get(class, OBJ_ALLOCATED); 22418c2ecf20Sopenharmony_ci unsigned long obj_used = zs_stat_get(class, OBJ_USED); 22428c2ecf20Sopenharmony_ci 22438c2ecf20Sopenharmony_ci if (obj_allocated <= obj_used) 22448c2ecf20Sopenharmony_ci return 0; 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_ci obj_wasted = obj_allocated - obj_used; 22478c2ecf20Sopenharmony_ci obj_wasted /= class->objs_per_zspage; 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_ci return obj_wasted * class->pages_per_zspage; 22508c2ecf20Sopenharmony_ci} 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_cistatic unsigned long __zs_compact(struct zs_pool *pool, 22538c2ecf20Sopenharmony_ci struct size_class *class) 22548c2ecf20Sopenharmony_ci{ 22558c2ecf20Sopenharmony_ci struct zs_compact_control cc; 22568c2ecf20Sopenharmony_ci struct zspage *src_zspage; 22578c2ecf20Sopenharmony_ci struct zspage *dst_zspage = NULL; 22588c2ecf20Sopenharmony_ci unsigned long pages_freed = 0; 22598c2ecf20Sopenharmony_ci 22608c2ecf20Sopenharmony_ci spin_lock(&class->lock); 22618c2ecf20Sopenharmony_ci while ((src_zspage = isolate_zspage(class, true))) { 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci if (!zs_can_compact(class)) 22648c2ecf20Sopenharmony_ci break; 22658c2ecf20Sopenharmony_ci 22668c2ecf20Sopenharmony_ci cc.obj_idx = 0; 22678c2ecf20Sopenharmony_ci cc.s_page = get_first_page(src_zspage); 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_ci while ((dst_zspage = isolate_zspage(class, false))) { 22708c2ecf20Sopenharmony_ci cc.d_page = get_first_page(dst_zspage); 22718c2ecf20Sopenharmony_ci /* 22728c2ecf20Sopenharmony_ci * If there is no more space in dst_page, resched 22738c2ecf20Sopenharmony_ci * and see if anyone had allocated another zspage. 22748c2ecf20Sopenharmony_ci */ 22758c2ecf20Sopenharmony_ci if (!migrate_zspage(pool, class, &cc)) 22768c2ecf20Sopenharmony_ci break; 22778c2ecf20Sopenharmony_ci 22788c2ecf20Sopenharmony_ci putback_zspage(class, dst_zspage); 22798c2ecf20Sopenharmony_ci } 22808c2ecf20Sopenharmony_ci 22818c2ecf20Sopenharmony_ci /* Stop if we couldn't find slot */ 22828c2ecf20Sopenharmony_ci if (dst_zspage == NULL) 22838c2ecf20Sopenharmony_ci break; 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci putback_zspage(class, dst_zspage); 22868c2ecf20Sopenharmony_ci if (putback_zspage(class, src_zspage) == ZS_EMPTY) { 22878c2ecf20Sopenharmony_ci free_zspage(pool, class, src_zspage); 22888c2ecf20Sopenharmony_ci pages_freed += class->pages_per_zspage; 22898c2ecf20Sopenharmony_ci } 22908c2ecf20Sopenharmony_ci spin_unlock(&class->lock); 22918c2ecf20Sopenharmony_ci cond_resched(); 22928c2ecf20Sopenharmony_ci spin_lock(&class->lock); 22938c2ecf20Sopenharmony_ci } 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci if (src_zspage) 22968c2ecf20Sopenharmony_ci putback_zspage(class, src_zspage); 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci spin_unlock(&class->lock); 22998c2ecf20Sopenharmony_ci 23008c2ecf20Sopenharmony_ci return pages_freed; 23018c2ecf20Sopenharmony_ci} 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_ciunsigned long zs_compact(struct zs_pool *pool) 23048c2ecf20Sopenharmony_ci{ 23058c2ecf20Sopenharmony_ci int i; 23068c2ecf20Sopenharmony_ci struct size_class *class; 23078c2ecf20Sopenharmony_ci unsigned long pages_freed = 0; 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci for (i = ZS_SIZE_CLASSES - 1; i >= 0; i--) { 23108c2ecf20Sopenharmony_ci class = pool->size_class[i]; 23118c2ecf20Sopenharmony_ci if (!class) 23128c2ecf20Sopenharmony_ci continue; 23138c2ecf20Sopenharmony_ci if (class->index != i) 23148c2ecf20Sopenharmony_ci continue; 23158c2ecf20Sopenharmony_ci pages_freed += __zs_compact(pool, class); 23168c2ecf20Sopenharmony_ci } 23178c2ecf20Sopenharmony_ci atomic_long_add(pages_freed, &pool->stats.pages_compacted); 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci return pages_freed; 23208c2ecf20Sopenharmony_ci} 23218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_compact); 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_civoid zs_pool_stats(struct zs_pool *pool, struct zs_pool_stats *stats) 23248c2ecf20Sopenharmony_ci{ 23258c2ecf20Sopenharmony_ci memcpy(stats, &pool->stats, sizeof(struct zs_pool_stats)); 23268c2ecf20Sopenharmony_ci} 23278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_pool_stats); 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_cistatic unsigned long zs_shrinker_scan(struct shrinker *shrinker, 23308c2ecf20Sopenharmony_ci struct shrink_control *sc) 23318c2ecf20Sopenharmony_ci{ 23328c2ecf20Sopenharmony_ci unsigned long pages_freed; 23338c2ecf20Sopenharmony_ci struct zs_pool *pool = container_of(shrinker, struct zs_pool, 23348c2ecf20Sopenharmony_ci shrinker); 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ci /* 23378c2ecf20Sopenharmony_ci * Compact classes and calculate compaction delta. 23388c2ecf20Sopenharmony_ci * Can run concurrently with a manually triggered 23398c2ecf20Sopenharmony_ci * (by user) compaction. 23408c2ecf20Sopenharmony_ci */ 23418c2ecf20Sopenharmony_ci pages_freed = zs_compact(pool); 23428c2ecf20Sopenharmony_ci 23438c2ecf20Sopenharmony_ci return pages_freed ? pages_freed : SHRINK_STOP; 23448c2ecf20Sopenharmony_ci} 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_cistatic unsigned long zs_shrinker_count(struct shrinker *shrinker, 23478c2ecf20Sopenharmony_ci struct shrink_control *sc) 23488c2ecf20Sopenharmony_ci{ 23498c2ecf20Sopenharmony_ci int i; 23508c2ecf20Sopenharmony_ci struct size_class *class; 23518c2ecf20Sopenharmony_ci unsigned long pages_to_free = 0; 23528c2ecf20Sopenharmony_ci struct zs_pool *pool = container_of(shrinker, struct zs_pool, 23538c2ecf20Sopenharmony_ci shrinker); 23548c2ecf20Sopenharmony_ci 23558c2ecf20Sopenharmony_ci for (i = ZS_SIZE_CLASSES - 1; i >= 0; i--) { 23568c2ecf20Sopenharmony_ci class = pool->size_class[i]; 23578c2ecf20Sopenharmony_ci if (!class) 23588c2ecf20Sopenharmony_ci continue; 23598c2ecf20Sopenharmony_ci if (class->index != i) 23608c2ecf20Sopenharmony_ci continue; 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci pages_to_free += zs_can_compact(class); 23638c2ecf20Sopenharmony_ci } 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci return pages_to_free; 23668c2ecf20Sopenharmony_ci} 23678c2ecf20Sopenharmony_ci 23688c2ecf20Sopenharmony_cistatic void zs_unregister_shrinker(struct zs_pool *pool) 23698c2ecf20Sopenharmony_ci{ 23708c2ecf20Sopenharmony_ci unregister_shrinker(&pool->shrinker); 23718c2ecf20Sopenharmony_ci} 23728c2ecf20Sopenharmony_ci 23738c2ecf20Sopenharmony_cistatic int zs_register_shrinker(struct zs_pool *pool) 23748c2ecf20Sopenharmony_ci{ 23758c2ecf20Sopenharmony_ci pool->shrinker.scan_objects = zs_shrinker_scan; 23768c2ecf20Sopenharmony_ci pool->shrinker.count_objects = zs_shrinker_count; 23778c2ecf20Sopenharmony_ci pool->shrinker.batch = 0; 23788c2ecf20Sopenharmony_ci pool->shrinker.seeks = DEFAULT_SEEKS; 23798c2ecf20Sopenharmony_ci 23808c2ecf20Sopenharmony_ci return register_shrinker(&pool->shrinker); 23818c2ecf20Sopenharmony_ci} 23828c2ecf20Sopenharmony_ci 23838c2ecf20Sopenharmony_ci/** 23848c2ecf20Sopenharmony_ci * zs_create_pool - Creates an allocation pool to work from. 23858c2ecf20Sopenharmony_ci * @name: pool name to be created 23868c2ecf20Sopenharmony_ci * 23878c2ecf20Sopenharmony_ci * This function must be called before anything when using 23888c2ecf20Sopenharmony_ci * the zsmalloc allocator. 23898c2ecf20Sopenharmony_ci * 23908c2ecf20Sopenharmony_ci * On success, a pointer to the newly created pool is returned, 23918c2ecf20Sopenharmony_ci * otherwise NULL. 23928c2ecf20Sopenharmony_ci */ 23938c2ecf20Sopenharmony_cistruct zs_pool *zs_create_pool(const char *name) 23948c2ecf20Sopenharmony_ci{ 23958c2ecf20Sopenharmony_ci int i; 23968c2ecf20Sopenharmony_ci struct zs_pool *pool; 23978c2ecf20Sopenharmony_ci struct size_class *prev_class = NULL; 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_ci pool = kzalloc(sizeof(*pool), GFP_KERNEL); 24008c2ecf20Sopenharmony_ci if (!pool) 24018c2ecf20Sopenharmony_ci return NULL; 24028c2ecf20Sopenharmony_ci 24038c2ecf20Sopenharmony_ci init_deferred_free(pool); 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci pool->name = kstrdup(name, GFP_KERNEL); 24068c2ecf20Sopenharmony_ci if (!pool->name) 24078c2ecf20Sopenharmony_ci goto err; 24088c2ecf20Sopenharmony_ci 24098c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPACTION 24108c2ecf20Sopenharmony_ci init_waitqueue_head(&pool->migration_wait); 24118c2ecf20Sopenharmony_ci#endif 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_ci if (create_cache(pool)) 24148c2ecf20Sopenharmony_ci goto err; 24158c2ecf20Sopenharmony_ci 24168c2ecf20Sopenharmony_ci /* 24178c2ecf20Sopenharmony_ci * Iterate reversely, because, size of size_class that we want to use 24188c2ecf20Sopenharmony_ci * for merging should be larger or equal to current size. 24198c2ecf20Sopenharmony_ci */ 24208c2ecf20Sopenharmony_ci for (i = ZS_SIZE_CLASSES - 1; i >= 0; i--) { 24218c2ecf20Sopenharmony_ci int size; 24228c2ecf20Sopenharmony_ci int pages_per_zspage; 24238c2ecf20Sopenharmony_ci int objs_per_zspage; 24248c2ecf20Sopenharmony_ci struct size_class *class; 24258c2ecf20Sopenharmony_ci int fullness = 0; 24268c2ecf20Sopenharmony_ci 24278c2ecf20Sopenharmony_ci size = ZS_MIN_ALLOC_SIZE + i * ZS_SIZE_CLASS_DELTA; 24288c2ecf20Sopenharmony_ci if (size > ZS_MAX_ALLOC_SIZE) 24298c2ecf20Sopenharmony_ci size = ZS_MAX_ALLOC_SIZE; 24308c2ecf20Sopenharmony_ci pages_per_zspage = get_pages_per_zspage(size); 24318c2ecf20Sopenharmony_ci objs_per_zspage = pages_per_zspage * PAGE_SIZE / size; 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci /* 24348c2ecf20Sopenharmony_ci * We iterate from biggest down to smallest classes, 24358c2ecf20Sopenharmony_ci * so huge_class_size holds the size of the first huge 24368c2ecf20Sopenharmony_ci * class. Any object bigger than or equal to that will 24378c2ecf20Sopenharmony_ci * endup in the huge class. 24388c2ecf20Sopenharmony_ci */ 24398c2ecf20Sopenharmony_ci if (pages_per_zspage != 1 && objs_per_zspage != 1 && 24408c2ecf20Sopenharmony_ci !huge_class_size) { 24418c2ecf20Sopenharmony_ci huge_class_size = size; 24428c2ecf20Sopenharmony_ci /* 24438c2ecf20Sopenharmony_ci * The object uses ZS_HANDLE_SIZE bytes to store the 24448c2ecf20Sopenharmony_ci * handle. We need to subtract it, because zs_malloc() 24458c2ecf20Sopenharmony_ci * unconditionally adds handle size before it performs 24468c2ecf20Sopenharmony_ci * size class search - so object may be smaller than 24478c2ecf20Sopenharmony_ci * huge class size, yet it still can end up in the huge 24488c2ecf20Sopenharmony_ci * class because it grows by ZS_HANDLE_SIZE extra bytes 24498c2ecf20Sopenharmony_ci * right before class lookup. 24508c2ecf20Sopenharmony_ci */ 24518c2ecf20Sopenharmony_ci huge_class_size -= (ZS_HANDLE_SIZE - 1); 24528c2ecf20Sopenharmony_ci } 24538c2ecf20Sopenharmony_ci 24548c2ecf20Sopenharmony_ci /* 24558c2ecf20Sopenharmony_ci * size_class is used for normal zsmalloc operation such 24568c2ecf20Sopenharmony_ci * as alloc/free for that size. Although it is natural that we 24578c2ecf20Sopenharmony_ci * have one size_class for each size, there is a chance that we 24588c2ecf20Sopenharmony_ci * can get more memory utilization if we use one size_class for 24598c2ecf20Sopenharmony_ci * many different sizes whose size_class have same 24608c2ecf20Sopenharmony_ci * characteristics. So, we makes size_class point to 24618c2ecf20Sopenharmony_ci * previous size_class if possible. 24628c2ecf20Sopenharmony_ci */ 24638c2ecf20Sopenharmony_ci if (prev_class) { 24648c2ecf20Sopenharmony_ci if (can_merge(prev_class, pages_per_zspage, objs_per_zspage)) { 24658c2ecf20Sopenharmony_ci pool->size_class[i] = prev_class; 24668c2ecf20Sopenharmony_ci continue; 24678c2ecf20Sopenharmony_ci } 24688c2ecf20Sopenharmony_ci } 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_ci class = kzalloc(sizeof(struct size_class), GFP_KERNEL); 24718c2ecf20Sopenharmony_ci if (!class) 24728c2ecf20Sopenharmony_ci goto err; 24738c2ecf20Sopenharmony_ci 24748c2ecf20Sopenharmony_ci class->size = size; 24758c2ecf20Sopenharmony_ci class->index = i; 24768c2ecf20Sopenharmony_ci class->pages_per_zspage = pages_per_zspage; 24778c2ecf20Sopenharmony_ci class->objs_per_zspage = objs_per_zspage; 24788c2ecf20Sopenharmony_ci spin_lock_init(&class->lock); 24798c2ecf20Sopenharmony_ci pool->size_class[i] = class; 24808c2ecf20Sopenharmony_ci for (fullness = ZS_EMPTY; fullness < NR_ZS_FULLNESS; 24818c2ecf20Sopenharmony_ci fullness++) 24828c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&class->fullness_list[fullness]); 24838c2ecf20Sopenharmony_ci 24848c2ecf20Sopenharmony_ci prev_class = class; 24858c2ecf20Sopenharmony_ci } 24868c2ecf20Sopenharmony_ci 24878c2ecf20Sopenharmony_ci /* debug only, don't abort if it fails */ 24888c2ecf20Sopenharmony_ci zs_pool_stat_create(pool, name); 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_ci if (zs_register_migration(pool)) 24918c2ecf20Sopenharmony_ci goto err; 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci /* 24948c2ecf20Sopenharmony_ci * Not critical since shrinker is only used to trigger internal 24958c2ecf20Sopenharmony_ci * defragmentation of the pool which is pretty optional thing. If 24968c2ecf20Sopenharmony_ci * registration fails we still can use the pool normally and user can 24978c2ecf20Sopenharmony_ci * trigger compaction manually. Thus, ignore return code. 24988c2ecf20Sopenharmony_ci */ 24998c2ecf20Sopenharmony_ci zs_register_shrinker(pool); 25008c2ecf20Sopenharmony_ci 25018c2ecf20Sopenharmony_ci return pool; 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_cierr: 25048c2ecf20Sopenharmony_ci zs_destroy_pool(pool); 25058c2ecf20Sopenharmony_ci return NULL; 25068c2ecf20Sopenharmony_ci} 25078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_create_pool); 25088c2ecf20Sopenharmony_ci 25098c2ecf20Sopenharmony_civoid zs_destroy_pool(struct zs_pool *pool) 25108c2ecf20Sopenharmony_ci{ 25118c2ecf20Sopenharmony_ci int i; 25128c2ecf20Sopenharmony_ci 25138c2ecf20Sopenharmony_ci zs_unregister_shrinker(pool); 25148c2ecf20Sopenharmony_ci zs_unregister_migration(pool); 25158c2ecf20Sopenharmony_ci zs_pool_stat_destroy(pool); 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci for (i = 0; i < ZS_SIZE_CLASSES; i++) { 25188c2ecf20Sopenharmony_ci int fg; 25198c2ecf20Sopenharmony_ci struct size_class *class = pool->size_class[i]; 25208c2ecf20Sopenharmony_ci 25218c2ecf20Sopenharmony_ci if (!class) 25228c2ecf20Sopenharmony_ci continue; 25238c2ecf20Sopenharmony_ci 25248c2ecf20Sopenharmony_ci if (class->index != i) 25258c2ecf20Sopenharmony_ci continue; 25268c2ecf20Sopenharmony_ci 25278c2ecf20Sopenharmony_ci for (fg = ZS_EMPTY; fg < NR_ZS_FULLNESS; fg++) { 25288c2ecf20Sopenharmony_ci if (!list_empty(&class->fullness_list[fg])) { 25298c2ecf20Sopenharmony_ci pr_info("Freeing non-empty class with size %db, fullness group %d\n", 25308c2ecf20Sopenharmony_ci class->size, fg); 25318c2ecf20Sopenharmony_ci } 25328c2ecf20Sopenharmony_ci } 25338c2ecf20Sopenharmony_ci kfree(class); 25348c2ecf20Sopenharmony_ci } 25358c2ecf20Sopenharmony_ci 25368c2ecf20Sopenharmony_ci destroy_cache(pool); 25378c2ecf20Sopenharmony_ci kfree(pool->name); 25388c2ecf20Sopenharmony_ci kfree(pool); 25398c2ecf20Sopenharmony_ci} 25408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(zs_destroy_pool); 25418c2ecf20Sopenharmony_ci 25428c2ecf20Sopenharmony_cistatic int __init zs_init(void) 25438c2ecf20Sopenharmony_ci{ 25448c2ecf20Sopenharmony_ci int ret; 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci ret = zsmalloc_mount(); 25478c2ecf20Sopenharmony_ci if (ret) 25488c2ecf20Sopenharmony_ci goto out; 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ci ret = cpuhp_setup_state(CPUHP_MM_ZS_PREPARE, "mm/zsmalloc:prepare", 25518c2ecf20Sopenharmony_ci zs_cpu_prepare, zs_cpu_dead); 25528c2ecf20Sopenharmony_ci if (ret) 25538c2ecf20Sopenharmony_ci goto hp_setup_fail; 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_ci#ifdef CONFIG_ZPOOL 25568c2ecf20Sopenharmony_ci zpool_register_driver(&zs_zpool_driver); 25578c2ecf20Sopenharmony_ci#endif 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ci zs_stat_init(); 25608c2ecf20Sopenharmony_ci 25618c2ecf20Sopenharmony_ci return 0; 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_cihp_setup_fail: 25648c2ecf20Sopenharmony_ci zsmalloc_unmount(); 25658c2ecf20Sopenharmony_ciout: 25668c2ecf20Sopenharmony_ci return ret; 25678c2ecf20Sopenharmony_ci} 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_cistatic void __exit zs_exit(void) 25708c2ecf20Sopenharmony_ci{ 25718c2ecf20Sopenharmony_ci#ifdef CONFIG_ZPOOL 25728c2ecf20Sopenharmony_ci zpool_unregister_driver(&zs_zpool_driver); 25738c2ecf20Sopenharmony_ci#endif 25748c2ecf20Sopenharmony_ci zsmalloc_unmount(); 25758c2ecf20Sopenharmony_ci cpuhp_remove_state(CPUHP_MM_ZS_PREPARE); 25768c2ecf20Sopenharmony_ci 25778c2ecf20Sopenharmony_ci zs_stat_exit(); 25788c2ecf20Sopenharmony_ci} 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_cimodule_init(zs_init); 25818c2ecf20Sopenharmony_cimodule_exit(zs_exit); 25828c2ecf20Sopenharmony_ci 25838c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 25848c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nitin Gupta <ngupta@vflare.org>"); 2585