18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * zswap.c - zswap driver file 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * zswap is a backend for frontswap that takes pages that are in the process 68c2ecf20Sopenharmony_ci * of being swapped out and attempts to compress and store them in a 78c2ecf20Sopenharmony_ci * RAM-based memory pool. This can result in a significant I/O reduction on 88c2ecf20Sopenharmony_ci * the swap device and, in the case where decompressing from RAM is faster 98c2ecf20Sopenharmony_ci * than reading from the swap device, can also improve workload performance. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Copyright (C) 2012 Seth Jennings <sjenning@linux.vnet.ibm.com> 128c2ecf20Sopenharmony_ci*/ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/cpu.h> 188c2ecf20Sopenharmony_ci#include <linux/highmem.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 218c2ecf20Sopenharmony_ci#include <linux/types.h> 228c2ecf20Sopenharmony_ci#include <linux/atomic.h> 238c2ecf20Sopenharmony_ci#include <linux/frontswap.h> 248c2ecf20Sopenharmony_ci#include <linux/rbtree.h> 258c2ecf20Sopenharmony_ci#include <linux/swap.h> 268c2ecf20Sopenharmony_ci#include <linux/crypto.h> 278c2ecf20Sopenharmony_ci#include <linux/mempool.h> 288c2ecf20Sopenharmony_ci#include <linux/zpool.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <linux/mm_types.h> 318c2ecf20Sopenharmony_ci#include <linux/page-flags.h> 328c2ecf20Sopenharmony_ci#include <linux/swapops.h> 338c2ecf20Sopenharmony_ci#include <linux/writeback.h> 348c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 358c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/********************************* 388c2ecf20Sopenharmony_ci* statistics 398c2ecf20Sopenharmony_ci**********************************/ 408c2ecf20Sopenharmony_ci/* Total bytes used by the compressed storage */ 418c2ecf20Sopenharmony_cistatic u64 zswap_pool_total_size; 428c2ecf20Sopenharmony_ci/* The number of compressed pages currently stored in zswap */ 438c2ecf20Sopenharmony_cistatic atomic_t zswap_stored_pages = ATOMIC_INIT(0); 448c2ecf20Sopenharmony_ci/* The number of same-value filled pages currently stored in zswap */ 458c2ecf20Sopenharmony_cistatic atomic_t zswap_same_filled_pages = ATOMIC_INIT(0); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * The statistics below are not protected from concurrent access for 498c2ecf20Sopenharmony_ci * performance reasons so they may not be a 100% accurate. However, 508c2ecf20Sopenharmony_ci * they do provide useful information on roughly how many times a 518c2ecf20Sopenharmony_ci * certain event is occurring. 528c2ecf20Sopenharmony_ci*/ 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* Pool limit was hit (see zswap_max_pool_percent) */ 558c2ecf20Sopenharmony_cistatic u64 zswap_pool_limit_hit; 568c2ecf20Sopenharmony_ci/* Pages written back when pool limit was reached */ 578c2ecf20Sopenharmony_cistatic u64 zswap_written_back_pages; 588c2ecf20Sopenharmony_ci/* Store failed due to a reclaim failure after pool limit was reached */ 598c2ecf20Sopenharmony_cistatic u64 zswap_reject_reclaim_fail; 608c2ecf20Sopenharmony_ci/* Compressed page was too big for the allocator to (optimally) store */ 618c2ecf20Sopenharmony_cistatic u64 zswap_reject_compress_poor; 628c2ecf20Sopenharmony_ci/* Store failed because underlying allocator could not get memory */ 638c2ecf20Sopenharmony_cistatic u64 zswap_reject_alloc_fail; 648c2ecf20Sopenharmony_ci/* Store failed because the entry metadata could not be allocated (rare) */ 658c2ecf20Sopenharmony_cistatic u64 zswap_reject_kmemcache_fail; 668c2ecf20Sopenharmony_ci/* Duplicate store was encountered (rare) */ 678c2ecf20Sopenharmony_cistatic u64 zswap_duplicate_entry; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* Shrinker work queue */ 708c2ecf20Sopenharmony_cistatic struct workqueue_struct *shrink_wq; 718c2ecf20Sopenharmony_ci/* Pool limit was hit, we need to calm down */ 728c2ecf20Sopenharmony_cistatic bool zswap_pool_reached_full; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/********************************* 758c2ecf20Sopenharmony_ci* tunables 768c2ecf20Sopenharmony_ci**********************************/ 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define ZSWAP_PARAM_UNSET "" 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* Enable/disable zswap */ 818c2ecf20Sopenharmony_cistatic bool zswap_enabled = IS_ENABLED(CONFIG_ZSWAP_DEFAULT_ON); 828c2ecf20Sopenharmony_cistatic int zswap_enabled_param_set(const char *, 838c2ecf20Sopenharmony_ci const struct kernel_param *); 848c2ecf20Sopenharmony_cistatic struct kernel_param_ops zswap_enabled_param_ops = { 858c2ecf20Sopenharmony_ci .set = zswap_enabled_param_set, 868c2ecf20Sopenharmony_ci .get = param_get_bool, 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_cimodule_param_cb(enabled, &zswap_enabled_param_ops, &zswap_enabled, 0644); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* Crypto compressor to use */ 918c2ecf20Sopenharmony_cistatic char *zswap_compressor = CONFIG_ZSWAP_COMPRESSOR_DEFAULT; 928c2ecf20Sopenharmony_cistatic int zswap_compressor_param_set(const char *, 938c2ecf20Sopenharmony_ci const struct kernel_param *); 948c2ecf20Sopenharmony_cistatic struct kernel_param_ops zswap_compressor_param_ops = { 958c2ecf20Sopenharmony_ci .set = zswap_compressor_param_set, 968c2ecf20Sopenharmony_ci .get = param_get_charp, 978c2ecf20Sopenharmony_ci .free = param_free_charp, 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_cimodule_param_cb(compressor, &zswap_compressor_param_ops, 1008c2ecf20Sopenharmony_ci &zswap_compressor, 0644); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* Compressed storage zpool to use */ 1038c2ecf20Sopenharmony_cistatic char *zswap_zpool_type = CONFIG_ZSWAP_ZPOOL_DEFAULT; 1048c2ecf20Sopenharmony_cistatic int zswap_zpool_param_set(const char *, const struct kernel_param *); 1058c2ecf20Sopenharmony_cistatic struct kernel_param_ops zswap_zpool_param_ops = { 1068c2ecf20Sopenharmony_ci .set = zswap_zpool_param_set, 1078c2ecf20Sopenharmony_ci .get = param_get_charp, 1088c2ecf20Sopenharmony_ci .free = param_free_charp, 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_cimodule_param_cb(zpool, &zswap_zpool_param_ops, &zswap_zpool_type, 0644); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* The maximum percentage of memory that the compressed pool can occupy */ 1138c2ecf20Sopenharmony_cistatic unsigned int zswap_max_pool_percent = 20; 1148c2ecf20Sopenharmony_cimodule_param_named(max_pool_percent, zswap_max_pool_percent, uint, 0644); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* The threshold for accepting new pages after the max_pool_percent was hit */ 1178c2ecf20Sopenharmony_cistatic unsigned int zswap_accept_thr_percent = 90; /* of max pool size */ 1188c2ecf20Sopenharmony_cimodule_param_named(accept_threshold_percent, zswap_accept_thr_percent, 1198c2ecf20Sopenharmony_ci uint, 0644); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* Enable/disable handling same-value filled pages (enabled by default) */ 1228c2ecf20Sopenharmony_cistatic bool zswap_same_filled_pages_enabled = true; 1238c2ecf20Sopenharmony_cimodule_param_named(same_filled_pages_enabled, zswap_same_filled_pages_enabled, 1248c2ecf20Sopenharmony_ci bool, 0644); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/********************************* 1278c2ecf20Sopenharmony_ci* data structures 1288c2ecf20Sopenharmony_ci**********************************/ 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistruct zswap_pool { 1318c2ecf20Sopenharmony_ci struct zpool *zpool; 1328c2ecf20Sopenharmony_ci struct crypto_comp * __percpu *tfm; 1338c2ecf20Sopenharmony_ci struct kref kref; 1348c2ecf20Sopenharmony_ci struct list_head list; 1358c2ecf20Sopenharmony_ci struct work_struct release_work; 1368c2ecf20Sopenharmony_ci struct work_struct shrink_work; 1378c2ecf20Sopenharmony_ci struct hlist_node node; 1388c2ecf20Sopenharmony_ci char tfm_name[CRYPTO_MAX_ALG_NAME]; 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/* 1428c2ecf20Sopenharmony_ci * struct zswap_entry 1438c2ecf20Sopenharmony_ci * 1448c2ecf20Sopenharmony_ci * This structure contains the metadata for tracking a single compressed 1458c2ecf20Sopenharmony_ci * page within zswap. 1468c2ecf20Sopenharmony_ci * 1478c2ecf20Sopenharmony_ci * rbnode - links the entry into red-black tree for the appropriate swap type 1488c2ecf20Sopenharmony_ci * offset - the swap offset for the entry. Index into the red-black tree. 1498c2ecf20Sopenharmony_ci * refcount - the number of outstanding reference to the entry. This is needed 1508c2ecf20Sopenharmony_ci * to protect against premature freeing of the entry by code 1518c2ecf20Sopenharmony_ci * concurrent calls to load, invalidate, and writeback. The lock 1528c2ecf20Sopenharmony_ci * for the zswap_tree structure that contains the entry must 1538c2ecf20Sopenharmony_ci * be held while changing the refcount. Since the lock must 1548c2ecf20Sopenharmony_ci * be held, there is no reason to also make refcount atomic. 1558c2ecf20Sopenharmony_ci * length - the length in bytes of the compressed page data. Needed during 1568c2ecf20Sopenharmony_ci * decompression. For a same value filled page length is 0. 1578c2ecf20Sopenharmony_ci * pool - the zswap_pool the entry's data is in 1588c2ecf20Sopenharmony_ci * handle - zpool allocation handle that stores the compressed page data 1598c2ecf20Sopenharmony_ci * value - value of the same-value filled pages which have same content 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_cistruct zswap_entry { 1628c2ecf20Sopenharmony_ci struct rb_node rbnode; 1638c2ecf20Sopenharmony_ci pgoff_t offset; 1648c2ecf20Sopenharmony_ci int refcount; 1658c2ecf20Sopenharmony_ci unsigned int length; 1668c2ecf20Sopenharmony_ci struct zswap_pool *pool; 1678c2ecf20Sopenharmony_ci union { 1688c2ecf20Sopenharmony_ci unsigned long handle; 1698c2ecf20Sopenharmony_ci unsigned long value; 1708c2ecf20Sopenharmony_ci }; 1718c2ecf20Sopenharmony_ci}; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistruct zswap_header { 1748c2ecf20Sopenharmony_ci swp_entry_t swpentry; 1758c2ecf20Sopenharmony_ci}; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci/* 1788c2ecf20Sopenharmony_ci * The tree lock in the zswap_tree struct protects a few things: 1798c2ecf20Sopenharmony_ci * - the rbtree 1808c2ecf20Sopenharmony_ci * - the refcount field of each entry in the tree 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_cistruct zswap_tree { 1838c2ecf20Sopenharmony_ci struct rb_root rbroot; 1848c2ecf20Sopenharmony_ci spinlock_t lock; 1858c2ecf20Sopenharmony_ci}; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic struct zswap_tree *zswap_trees[MAX_SWAPFILES]; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* RCU-protected iteration */ 1908c2ecf20Sopenharmony_cistatic LIST_HEAD(zswap_pools); 1918c2ecf20Sopenharmony_ci/* protects zswap_pools list modification */ 1928c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(zswap_pools_lock); 1938c2ecf20Sopenharmony_ci/* pool counter to provide unique names to zpool */ 1948c2ecf20Sopenharmony_cistatic atomic_t zswap_pools_count = ATOMIC_INIT(0); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* used by param callback function */ 1978c2ecf20Sopenharmony_cistatic bool zswap_init_started; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* fatal error during init */ 2008c2ecf20Sopenharmony_cistatic bool zswap_init_failed; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/* init completed, but couldn't create the initial pool */ 2038c2ecf20Sopenharmony_cistatic bool zswap_has_pool; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/********************************* 2068c2ecf20Sopenharmony_ci* helpers and fwd declarations 2078c2ecf20Sopenharmony_ci**********************************/ 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci#define zswap_pool_debug(msg, p) \ 2108c2ecf20Sopenharmony_ci pr_debug("%s pool %s/%s\n", msg, (p)->tfm_name, \ 2118c2ecf20Sopenharmony_ci zpool_get_type((p)->zpool)) 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic int zswap_writeback_entry(struct zpool *pool, unsigned long handle); 2148c2ecf20Sopenharmony_cistatic int zswap_pool_get(struct zswap_pool *pool); 2158c2ecf20Sopenharmony_cistatic void zswap_pool_put(struct zswap_pool *pool); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic const struct zpool_ops zswap_zpool_ops = { 2188c2ecf20Sopenharmony_ci .evict = zswap_writeback_entry 2198c2ecf20Sopenharmony_ci}; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic bool zswap_is_full(void) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci return totalram_pages() * zswap_max_pool_percent / 100 < 2248c2ecf20Sopenharmony_ci DIV_ROUND_UP(zswap_pool_total_size, PAGE_SIZE); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic bool zswap_can_accept(void) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci return totalram_pages() * zswap_accept_thr_percent / 100 * 2308c2ecf20Sopenharmony_ci zswap_max_pool_percent / 100 > 2318c2ecf20Sopenharmony_ci DIV_ROUND_UP(zswap_pool_total_size, PAGE_SIZE); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic void zswap_update_total_size(void) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct zswap_pool *pool; 2378c2ecf20Sopenharmony_ci u64 total = 0; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci rcu_read_lock(); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci list_for_each_entry_rcu(pool, &zswap_pools, list) 2428c2ecf20Sopenharmony_ci total += zpool_get_total_size(pool->zpool); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci rcu_read_unlock(); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci zswap_pool_total_size = total; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci/********************************* 2508c2ecf20Sopenharmony_ci* zswap entry functions 2518c2ecf20Sopenharmony_ci**********************************/ 2528c2ecf20Sopenharmony_cistatic struct kmem_cache *zswap_entry_cache; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int __init zswap_entry_cache_create(void) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci zswap_entry_cache = KMEM_CACHE(zswap_entry, 0); 2578c2ecf20Sopenharmony_ci return zswap_entry_cache == NULL; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic void __init zswap_entry_cache_destroy(void) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci kmem_cache_destroy(zswap_entry_cache); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic struct zswap_entry *zswap_entry_cache_alloc(gfp_t gfp) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct zswap_entry *entry; 2688c2ecf20Sopenharmony_ci entry = kmem_cache_alloc(zswap_entry_cache, gfp); 2698c2ecf20Sopenharmony_ci if (!entry) 2708c2ecf20Sopenharmony_ci return NULL; 2718c2ecf20Sopenharmony_ci entry->refcount = 1; 2728c2ecf20Sopenharmony_ci RB_CLEAR_NODE(&entry->rbnode); 2738c2ecf20Sopenharmony_ci return entry; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic void zswap_entry_cache_free(struct zswap_entry *entry) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci kmem_cache_free(zswap_entry_cache, entry); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci/********************************* 2828c2ecf20Sopenharmony_ci* rbtree functions 2838c2ecf20Sopenharmony_ci**********************************/ 2848c2ecf20Sopenharmony_cistatic struct zswap_entry *zswap_rb_search(struct rb_root *root, pgoff_t offset) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct rb_node *node = root->rb_node; 2878c2ecf20Sopenharmony_ci struct zswap_entry *entry; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci while (node) { 2908c2ecf20Sopenharmony_ci entry = rb_entry(node, struct zswap_entry, rbnode); 2918c2ecf20Sopenharmony_ci if (entry->offset > offset) 2928c2ecf20Sopenharmony_ci node = node->rb_left; 2938c2ecf20Sopenharmony_ci else if (entry->offset < offset) 2948c2ecf20Sopenharmony_ci node = node->rb_right; 2958c2ecf20Sopenharmony_ci else 2968c2ecf20Sopenharmony_ci return entry; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci return NULL; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/* 3028c2ecf20Sopenharmony_ci * In the case that a entry with the same offset is found, a pointer to 3038c2ecf20Sopenharmony_ci * the existing entry is stored in dupentry and the function returns -EEXIST 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_cistatic int zswap_rb_insert(struct rb_root *root, struct zswap_entry *entry, 3068c2ecf20Sopenharmony_ci struct zswap_entry **dupentry) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct rb_node **link = &root->rb_node, *parent = NULL; 3098c2ecf20Sopenharmony_ci struct zswap_entry *myentry; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci while (*link) { 3128c2ecf20Sopenharmony_ci parent = *link; 3138c2ecf20Sopenharmony_ci myentry = rb_entry(parent, struct zswap_entry, rbnode); 3148c2ecf20Sopenharmony_ci if (myentry->offset > entry->offset) 3158c2ecf20Sopenharmony_ci link = &(*link)->rb_left; 3168c2ecf20Sopenharmony_ci else if (myentry->offset < entry->offset) 3178c2ecf20Sopenharmony_ci link = &(*link)->rb_right; 3188c2ecf20Sopenharmony_ci else { 3198c2ecf20Sopenharmony_ci *dupentry = myentry; 3208c2ecf20Sopenharmony_ci return -EEXIST; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci rb_link_node(&entry->rbnode, parent, link); 3248c2ecf20Sopenharmony_ci rb_insert_color(&entry->rbnode, root); 3258c2ecf20Sopenharmony_ci return 0; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic void zswap_rb_erase(struct rb_root *root, struct zswap_entry *entry) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci if (!RB_EMPTY_NODE(&entry->rbnode)) { 3318c2ecf20Sopenharmony_ci rb_erase(&entry->rbnode, root); 3328c2ecf20Sopenharmony_ci RB_CLEAR_NODE(&entry->rbnode); 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci/* 3378c2ecf20Sopenharmony_ci * Carries out the common pattern of freeing and entry's zpool allocation, 3388c2ecf20Sopenharmony_ci * freeing the entry itself, and decrementing the number of stored pages. 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_cistatic void zswap_free_entry(struct zswap_entry *entry) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci if (!entry->length) 3438c2ecf20Sopenharmony_ci atomic_dec(&zswap_same_filled_pages); 3448c2ecf20Sopenharmony_ci else { 3458c2ecf20Sopenharmony_ci zpool_free(entry->pool->zpool, entry->handle); 3468c2ecf20Sopenharmony_ci zswap_pool_put(entry->pool); 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci zswap_entry_cache_free(entry); 3498c2ecf20Sopenharmony_ci atomic_dec(&zswap_stored_pages); 3508c2ecf20Sopenharmony_ci zswap_update_total_size(); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci/* caller must hold the tree lock */ 3548c2ecf20Sopenharmony_cistatic void zswap_entry_get(struct zswap_entry *entry) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci entry->refcount++; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/* caller must hold the tree lock 3608c2ecf20Sopenharmony_ci* remove from the tree and free it, if nobody reference the entry 3618c2ecf20Sopenharmony_ci*/ 3628c2ecf20Sopenharmony_cistatic void zswap_entry_put(struct zswap_tree *tree, 3638c2ecf20Sopenharmony_ci struct zswap_entry *entry) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci int refcount = --entry->refcount; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci BUG_ON(refcount < 0); 3688c2ecf20Sopenharmony_ci if (refcount == 0) { 3698c2ecf20Sopenharmony_ci zswap_rb_erase(&tree->rbroot, entry); 3708c2ecf20Sopenharmony_ci zswap_free_entry(entry); 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci/* caller must hold the tree lock */ 3758c2ecf20Sopenharmony_cistatic struct zswap_entry *zswap_entry_find_get(struct rb_root *root, 3768c2ecf20Sopenharmony_ci pgoff_t offset) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci struct zswap_entry *entry; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci entry = zswap_rb_search(root, offset); 3818c2ecf20Sopenharmony_ci if (entry) 3828c2ecf20Sopenharmony_ci zswap_entry_get(entry); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci return entry; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci/********************************* 3888c2ecf20Sopenharmony_ci* per-cpu code 3898c2ecf20Sopenharmony_ci**********************************/ 3908c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(u8 *, zswap_dstmem); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic int zswap_dstmem_prepare(unsigned int cpu) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci u8 *dst; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci dst = kmalloc_node(PAGE_SIZE * 2, GFP_KERNEL, cpu_to_node(cpu)); 3978c2ecf20Sopenharmony_ci if (!dst) 3988c2ecf20Sopenharmony_ci return -ENOMEM; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci per_cpu(zswap_dstmem, cpu) = dst; 4018c2ecf20Sopenharmony_ci return 0; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic int zswap_dstmem_dead(unsigned int cpu) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci u8 *dst; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci dst = per_cpu(zswap_dstmem, cpu); 4098c2ecf20Sopenharmony_ci kfree(dst); 4108c2ecf20Sopenharmony_ci per_cpu(zswap_dstmem, cpu) = NULL; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci return 0; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic int zswap_cpu_comp_prepare(unsigned int cpu, struct hlist_node *node) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci struct zswap_pool *pool = hlist_entry(node, struct zswap_pool, node); 4188c2ecf20Sopenharmony_ci struct crypto_comp *tfm; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (WARN_ON(*per_cpu_ptr(pool->tfm, cpu))) 4218c2ecf20Sopenharmony_ci return 0; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci tfm = crypto_alloc_comp(pool->tfm_name, 0, 0); 4248c2ecf20Sopenharmony_ci if (IS_ERR(tfm)) { 4258c2ecf20Sopenharmony_ci pr_err("could not alloc crypto comp %s : %ld\n", 4268c2ecf20Sopenharmony_ci pool->tfm_name, PTR_ERR(tfm)); 4278c2ecf20Sopenharmony_ci return -ENOMEM; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci *per_cpu_ptr(pool->tfm, cpu) = tfm; 4308c2ecf20Sopenharmony_ci return 0; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic int zswap_cpu_comp_dead(unsigned int cpu, struct hlist_node *node) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci struct zswap_pool *pool = hlist_entry(node, struct zswap_pool, node); 4368c2ecf20Sopenharmony_ci struct crypto_comp *tfm; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci tfm = *per_cpu_ptr(pool->tfm, cpu); 4398c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(tfm)) 4408c2ecf20Sopenharmony_ci crypto_free_comp(tfm); 4418c2ecf20Sopenharmony_ci *per_cpu_ptr(pool->tfm, cpu) = NULL; 4428c2ecf20Sopenharmony_ci return 0; 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci/********************************* 4468c2ecf20Sopenharmony_ci* pool functions 4478c2ecf20Sopenharmony_ci**********************************/ 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic struct zswap_pool *__zswap_pool_current(void) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci struct zswap_pool *pool; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci pool = list_first_or_null_rcu(&zswap_pools, typeof(*pool), list); 4548c2ecf20Sopenharmony_ci WARN_ONCE(!pool && zswap_has_pool, 4558c2ecf20Sopenharmony_ci "%s: no page storage pool!\n", __func__); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return pool; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic struct zswap_pool *zswap_pool_current(void) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci assert_spin_locked(&zswap_pools_lock); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci return __zswap_pool_current(); 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic struct zswap_pool *zswap_pool_current_get(void) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci struct zswap_pool *pool; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci rcu_read_lock(); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci pool = __zswap_pool_current(); 4748c2ecf20Sopenharmony_ci if (!zswap_pool_get(pool)) 4758c2ecf20Sopenharmony_ci pool = NULL; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci rcu_read_unlock(); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci return pool; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic struct zswap_pool *zswap_pool_last_get(void) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct zswap_pool *pool, *last = NULL; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci rcu_read_lock(); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci list_for_each_entry_rcu(pool, &zswap_pools, list) 4898c2ecf20Sopenharmony_ci last = pool; 4908c2ecf20Sopenharmony_ci WARN_ONCE(!last && zswap_has_pool, 4918c2ecf20Sopenharmony_ci "%s: no page storage pool!\n", __func__); 4928c2ecf20Sopenharmony_ci if (!zswap_pool_get(last)) 4938c2ecf20Sopenharmony_ci last = NULL; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci rcu_read_unlock(); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return last; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci/* type and compressor must be null-terminated */ 5018c2ecf20Sopenharmony_cistatic struct zswap_pool *zswap_pool_find_get(char *type, char *compressor) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci struct zswap_pool *pool; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci assert_spin_locked(&zswap_pools_lock); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci list_for_each_entry_rcu(pool, &zswap_pools, list) { 5088c2ecf20Sopenharmony_ci if (strcmp(pool->tfm_name, compressor)) 5098c2ecf20Sopenharmony_ci continue; 5108c2ecf20Sopenharmony_ci if (strcmp(zpool_get_type(pool->zpool), type)) 5118c2ecf20Sopenharmony_ci continue; 5128c2ecf20Sopenharmony_ci /* if we can't get it, it's about to be destroyed */ 5138c2ecf20Sopenharmony_ci if (!zswap_pool_get(pool)) 5148c2ecf20Sopenharmony_ci continue; 5158c2ecf20Sopenharmony_ci return pool; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return NULL; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic void shrink_worker(struct work_struct *w) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct zswap_pool *pool = container_of(w, typeof(*pool), 5248c2ecf20Sopenharmony_ci shrink_work); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci if (zpool_shrink(pool->zpool, 1, NULL)) 5278c2ecf20Sopenharmony_ci zswap_reject_reclaim_fail++; 5288c2ecf20Sopenharmony_ci zswap_pool_put(pool); 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic struct zswap_pool *zswap_pool_create(char *type, char *compressor) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct zswap_pool *pool; 5348c2ecf20Sopenharmony_ci char name[38]; /* 'zswap' + 32 char (max) num + \0 */ 5358c2ecf20Sopenharmony_ci gfp_t gfp = __GFP_NORETRY | __GFP_NOWARN | __GFP_KSWAPD_RECLAIM; 5368c2ecf20Sopenharmony_ci int ret; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci if (!zswap_has_pool) { 5398c2ecf20Sopenharmony_ci /* if either are unset, pool initialization failed, and we 5408c2ecf20Sopenharmony_ci * need both params to be set correctly before trying to 5418c2ecf20Sopenharmony_ci * create a pool. 5428c2ecf20Sopenharmony_ci */ 5438c2ecf20Sopenharmony_ci if (!strcmp(type, ZSWAP_PARAM_UNSET)) 5448c2ecf20Sopenharmony_ci return NULL; 5458c2ecf20Sopenharmony_ci if (!strcmp(compressor, ZSWAP_PARAM_UNSET)) 5468c2ecf20Sopenharmony_ci return NULL; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci pool = kzalloc(sizeof(*pool), GFP_KERNEL); 5508c2ecf20Sopenharmony_ci if (!pool) 5518c2ecf20Sopenharmony_ci return NULL; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci /* unique name for each pool specifically required by zsmalloc */ 5548c2ecf20Sopenharmony_ci snprintf(name, 38, "zswap%x", atomic_inc_return(&zswap_pools_count)); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci pool->zpool = zpool_create_pool(type, name, gfp, &zswap_zpool_ops); 5578c2ecf20Sopenharmony_ci if (!pool->zpool) { 5588c2ecf20Sopenharmony_ci pr_err("%s zpool not available\n", type); 5598c2ecf20Sopenharmony_ci goto error; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci pr_debug("using %s zpool\n", zpool_get_type(pool->zpool)); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci strlcpy(pool->tfm_name, compressor, sizeof(pool->tfm_name)); 5648c2ecf20Sopenharmony_ci pool->tfm = alloc_percpu(struct crypto_comp *); 5658c2ecf20Sopenharmony_ci if (!pool->tfm) { 5668c2ecf20Sopenharmony_ci pr_err("percpu alloc failed\n"); 5678c2ecf20Sopenharmony_ci goto error; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci ret = cpuhp_state_add_instance(CPUHP_MM_ZSWP_POOL_PREPARE, 5718c2ecf20Sopenharmony_ci &pool->node); 5728c2ecf20Sopenharmony_ci if (ret) 5738c2ecf20Sopenharmony_ci goto error; 5748c2ecf20Sopenharmony_ci pr_debug("using %s compressor\n", pool->tfm_name); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci /* being the current pool takes 1 ref; this func expects the 5778c2ecf20Sopenharmony_ci * caller to always add the new pool as the current pool 5788c2ecf20Sopenharmony_ci */ 5798c2ecf20Sopenharmony_ci kref_init(&pool->kref); 5808c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pool->list); 5818c2ecf20Sopenharmony_ci INIT_WORK(&pool->shrink_work, shrink_worker); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci zswap_pool_debug("created", pool); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci return pool; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cierror: 5888c2ecf20Sopenharmony_ci free_percpu(pool->tfm); 5898c2ecf20Sopenharmony_ci if (pool->zpool) 5908c2ecf20Sopenharmony_ci zpool_destroy_pool(pool->zpool); 5918c2ecf20Sopenharmony_ci kfree(pool); 5928c2ecf20Sopenharmony_ci return NULL; 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic __init struct zswap_pool *__zswap_pool_create_fallback(void) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci bool has_comp, has_zpool; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci has_comp = crypto_has_comp(zswap_compressor, 0, 0); 6008c2ecf20Sopenharmony_ci if (!has_comp && strcmp(zswap_compressor, 6018c2ecf20Sopenharmony_ci CONFIG_ZSWAP_COMPRESSOR_DEFAULT)) { 6028c2ecf20Sopenharmony_ci pr_err("compressor %s not available, using default %s\n", 6038c2ecf20Sopenharmony_ci zswap_compressor, CONFIG_ZSWAP_COMPRESSOR_DEFAULT); 6048c2ecf20Sopenharmony_ci param_free_charp(&zswap_compressor); 6058c2ecf20Sopenharmony_ci zswap_compressor = CONFIG_ZSWAP_COMPRESSOR_DEFAULT; 6068c2ecf20Sopenharmony_ci has_comp = crypto_has_comp(zswap_compressor, 0, 0); 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci if (!has_comp) { 6098c2ecf20Sopenharmony_ci pr_err("default compressor %s not available\n", 6108c2ecf20Sopenharmony_ci zswap_compressor); 6118c2ecf20Sopenharmony_ci param_free_charp(&zswap_compressor); 6128c2ecf20Sopenharmony_ci zswap_compressor = ZSWAP_PARAM_UNSET; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci has_zpool = zpool_has_pool(zswap_zpool_type); 6168c2ecf20Sopenharmony_ci if (!has_zpool && strcmp(zswap_zpool_type, 6178c2ecf20Sopenharmony_ci CONFIG_ZSWAP_ZPOOL_DEFAULT)) { 6188c2ecf20Sopenharmony_ci pr_err("zpool %s not available, using default %s\n", 6198c2ecf20Sopenharmony_ci zswap_zpool_type, CONFIG_ZSWAP_ZPOOL_DEFAULT); 6208c2ecf20Sopenharmony_ci param_free_charp(&zswap_zpool_type); 6218c2ecf20Sopenharmony_ci zswap_zpool_type = CONFIG_ZSWAP_ZPOOL_DEFAULT; 6228c2ecf20Sopenharmony_ci has_zpool = zpool_has_pool(zswap_zpool_type); 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci if (!has_zpool) { 6258c2ecf20Sopenharmony_ci pr_err("default zpool %s not available\n", 6268c2ecf20Sopenharmony_ci zswap_zpool_type); 6278c2ecf20Sopenharmony_ci param_free_charp(&zswap_zpool_type); 6288c2ecf20Sopenharmony_ci zswap_zpool_type = ZSWAP_PARAM_UNSET; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci if (!has_comp || !has_zpool) 6328c2ecf20Sopenharmony_ci return NULL; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci return zswap_pool_create(zswap_zpool_type, zswap_compressor); 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cistatic void zswap_pool_destroy(struct zswap_pool *pool) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci zswap_pool_debug("destroying", pool); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci cpuhp_state_remove_instance(CPUHP_MM_ZSWP_POOL_PREPARE, &pool->node); 6428c2ecf20Sopenharmony_ci free_percpu(pool->tfm); 6438c2ecf20Sopenharmony_ci zpool_destroy_pool(pool->zpool); 6448c2ecf20Sopenharmony_ci kfree(pool); 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic int __must_check zswap_pool_get(struct zswap_pool *pool) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci if (!pool) 6508c2ecf20Sopenharmony_ci return 0; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci return kref_get_unless_zero(&pool->kref); 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic void __zswap_pool_release(struct work_struct *work) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci struct zswap_pool *pool = container_of(work, typeof(*pool), 6588c2ecf20Sopenharmony_ci release_work); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci synchronize_rcu(); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci /* nobody should have been able to get a kref... */ 6638c2ecf20Sopenharmony_ci WARN_ON(kref_get_unless_zero(&pool->kref)); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci /* pool is now off zswap_pools list and has no references. */ 6668c2ecf20Sopenharmony_ci zswap_pool_destroy(pool); 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic void __zswap_pool_empty(struct kref *kref) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci struct zswap_pool *pool; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci pool = container_of(kref, typeof(*pool), kref); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci spin_lock(&zswap_pools_lock); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci WARN_ON(pool == zswap_pool_current()); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci list_del_rcu(&pool->list); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci INIT_WORK(&pool->release_work, __zswap_pool_release); 6828c2ecf20Sopenharmony_ci schedule_work(&pool->release_work); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci spin_unlock(&zswap_pools_lock); 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_cistatic void zswap_pool_put(struct zswap_pool *pool) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci kref_put(&pool->kref, __zswap_pool_empty); 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci/********************************* 6938c2ecf20Sopenharmony_ci* param callbacks 6948c2ecf20Sopenharmony_ci**********************************/ 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci/* val must be a null-terminated string */ 6978c2ecf20Sopenharmony_cistatic int __zswap_param_set(const char *val, const struct kernel_param *kp, 6988c2ecf20Sopenharmony_ci char *type, char *compressor) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci struct zswap_pool *pool, *put_pool = NULL; 7018c2ecf20Sopenharmony_ci char *s = strstrip((char *)val); 7028c2ecf20Sopenharmony_ci int ret; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (zswap_init_failed) { 7058c2ecf20Sopenharmony_ci pr_err("can't set param, initialization failed\n"); 7068c2ecf20Sopenharmony_ci return -ENODEV; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci /* no change required */ 7108c2ecf20Sopenharmony_ci if (!strcmp(s, *(char **)kp->arg) && zswap_has_pool) 7118c2ecf20Sopenharmony_ci return 0; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci /* if this is load-time (pre-init) param setting, 7148c2ecf20Sopenharmony_ci * don't create a pool; that's done during init. 7158c2ecf20Sopenharmony_ci */ 7168c2ecf20Sopenharmony_ci if (!zswap_init_started) 7178c2ecf20Sopenharmony_ci return param_set_charp(s, kp); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci if (!type) { 7208c2ecf20Sopenharmony_ci if (!zpool_has_pool(s)) { 7218c2ecf20Sopenharmony_ci pr_err("zpool %s not available\n", s); 7228c2ecf20Sopenharmony_ci return -ENOENT; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci type = s; 7258c2ecf20Sopenharmony_ci } else if (!compressor) { 7268c2ecf20Sopenharmony_ci if (!crypto_has_comp(s, 0, 0)) { 7278c2ecf20Sopenharmony_ci pr_err("compressor %s not available\n", s); 7288c2ecf20Sopenharmony_ci return -ENOENT; 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci compressor = s; 7318c2ecf20Sopenharmony_ci } else { 7328c2ecf20Sopenharmony_ci WARN_ON(1); 7338c2ecf20Sopenharmony_ci return -EINVAL; 7348c2ecf20Sopenharmony_ci } 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci spin_lock(&zswap_pools_lock); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci pool = zswap_pool_find_get(type, compressor); 7398c2ecf20Sopenharmony_ci if (pool) { 7408c2ecf20Sopenharmony_ci zswap_pool_debug("using existing", pool); 7418c2ecf20Sopenharmony_ci WARN_ON(pool == zswap_pool_current()); 7428c2ecf20Sopenharmony_ci list_del_rcu(&pool->list); 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci spin_unlock(&zswap_pools_lock); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (!pool) 7488c2ecf20Sopenharmony_ci pool = zswap_pool_create(type, compressor); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci if (pool) 7518c2ecf20Sopenharmony_ci ret = param_set_charp(s, kp); 7528c2ecf20Sopenharmony_ci else 7538c2ecf20Sopenharmony_ci ret = -EINVAL; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci spin_lock(&zswap_pools_lock); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci if (!ret) { 7588c2ecf20Sopenharmony_ci put_pool = zswap_pool_current(); 7598c2ecf20Sopenharmony_ci list_add_rcu(&pool->list, &zswap_pools); 7608c2ecf20Sopenharmony_ci zswap_has_pool = true; 7618c2ecf20Sopenharmony_ci } else if (pool) { 7628c2ecf20Sopenharmony_ci /* add the possibly pre-existing pool to the end of the pools 7638c2ecf20Sopenharmony_ci * list; if it's new (and empty) then it'll be removed and 7648c2ecf20Sopenharmony_ci * destroyed by the put after we drop the lock 7658c2ecf20Sopenharmony_ci */ 7668c2ecf20Sopenharmony_ci list_add_tail_rcu(&pool->list, &zswap_pools); 7678c2ecf20Sopenharmony_ci put_pool = pool; 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci spin_unlock(&zswap_pools_lock); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci if (!zswap_has_pool && !pool) { 7738c2ecf20Sopenharmony_ci /* if initial pool creation failed, and this pool creation also 7748c2ecf20Sopenharmony_ci * failed, maybe both compressor and zpool params were bad. 7758c2ecf20Sopenharmony_ci * Allow changing this param, so pool creation will succeed 7768c2ecf20Sopenharmony_ci * when the other param is changed. We already verified this 7778c2ecf20Sopenharmony_ci * param is ok in the zpool_has_pool() or crypto_has_comp() 7788c2ecf20Sopenharmony_ci * checks above. 7798c2ecf20Sopenharmony_ci */ 7808c2ecf20Sopenharmony_ci ret = param_set_charp(s, kp); 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci /* drop the ref from either the old current pool, 7848c2ecf20Sopenharmony_ci * or the new pool we failed to add 7858c2ecf20Sopenharmony_ci */ 7868c2ecf20Sopenharmony_ci if (put_pool) 7878c2ecf20Sopenharmony_ci zswap_pool_put(put_pool); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci return ret; 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_cistatic int zswap_compressor_param_set(const char *val, 7938c2ecf20Sopenharmony_ci const struct kernel_param *kp) 7948c2ecf20Sopenharmony_ci{ 7958c2ecf20Sopenharmony_ci return __zswap_param_set(val, kp, zswap_zpool_type, NULL); 7968c2ecf20Sopenharmony_ci} 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_cistatic int zswap_zpool_param_set(const char *val, 7998c2ecf20Sopenharmony_ci const struct kernel_param *kp) 8008c2ecf20Sopenharmony_ci{ 8018c2ecf20Sopenharmony_ci return __zswap_param_set(val, kp, NULL, zswap_compressor); 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_cistatic int zswap_enabled_param_set(const char *val, 8058c2ecf20Sopenharmony_ci const struct kernel_param *kp) 8068c2ecf20Sopenharmony_ci{ 8078c2ecf20Sopenharmony_ci if (zswap_init_failed) { 8088c2ecf20Sopenharmony_ci pr_err("can't enable, initialization failed\n"); 8098c2ecf20Sopenharmony_ci return -ENODEV; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci if (!zswap_has_pool && zswap_init_started) { 8128c2ecf20Sopenharmony_ci pr_err("can't enable, no pool configured\n"); 8138c2ecf20Sopenharmony_ci return -ENODEV; 8148c2ecf20Sopenharmony_ci } 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci return param_set_bool(val, kp); 8178c2ecf20Sopenharmony_ci} 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci/********************************* 8208c2ecf20Sopenharmony_ci* writeback code 8218c2ecf20Sopenharmony_ci**********************************/ 8228c2ecf20Sopenharmony_ci/* return enum for zswap_get_swap_cache_page */ 8238c2ecf20Sopenharmony_cienum zswap_get_swap_ret { 8248c2ecf20Sopenharmony_ci ZSWAP_SWAPCACHE_NEW, 8258c2ecf20Sopenharmony_ci ZSWAP_SWAPCACHE_EXIST, 8268c2ecf20Sopenharmony_ci ZSWAP_SWAPCACHE_FAIL, 8278c2ecf20Sopenharmony_ci}; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci/* 8308c2ecf20Sopenharmony_ci * zswap_get_swap_cache_page 8318c2ecf20Sopenharmony_ci * 8328c2ecf20Sopenharmony_ci * This is an adaption of read_swap_cache_async() 8338c2ecf20Sopenharmony_ci * 8348c2ecf20Sopenharmony_ci * This function tries to find a page with the given swap entry 8358c2ecf20Sopenharmony_ci * in the swapper_space address space (the swap cache). If the page 8368c2ecf20Sopenharmony_ci * is found, it is returned in retpage. Otherwise, a page is allocated, 8378c2ecf20Sopenharmony_ci * added to the swap cache, and returned in retpage. 8388c2ecf20Sopenharmony_ci * 8398c2ecf20Sopenharmony_ci * If success, the swap cache page is returned in retpage 8408c2ecf20Sopenharmony_ci * Returns ZSWAP_SWAPCACHE_EXIST if page was already in the swap cache 8418c2ecf20Sopenharmony_ci * Returns ZSWAP_SWAPCACHE_NEW if the new page needs to be populated, 8428c2ecf20Sopenharmony_ci * the new page is added to swapcache and locked 8438c2ecf20Sopenharmony_ci * Returns ZSWAP_SWAPCACHE_FAIL on error 8448c2ecf20Sopenharmony_ci */ 8458c2ecf20Sopenharmony_cistatic int zswap_get_swap_cache_page(swp_entry_t entry, 8468c2ecf20Sopenharmony_ci struct page **retpage) 8478c2ecf20Sopenharmony_ci{ 8488c2ecf20Sopenharmony_ci bool page_was_allocated; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci *retpage = __read_swap_cache_async(entry, GFP_KERNEL, 8518c2ecf20Sopenharmony_ci NULL, 0, &page_was_allocated); 8528c2ecf20Sopenharmony_ci if (page_was_allocated) 8538c2ecf20Sopenharmony_ci return ZSWAP_SWAPCACHE_NEW; 8548c2ecf20Sopenharmony_ci if (!*retpage) 8558c2ecf20Sopenharmony_ci return ZSWAP_SWAPCACHE_FAIL; 8568c2ecf20Sopenharmony_ci return ZSWAP_SWAPCACHE_EXIST; 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci/* 8608c2ecf20Sopenharmony_ci * Attempts to free an entry by adding a page to the swap cache, 8618c2ecf20Sopenharmony_ci * decompressing the entry data into the page, and issuing a 8628c2ecf20Sopenharmony_ci * bio write to write the page back to the swap device. 8638c2ecf20Sopenharmony_ci * 8648c2ecf20Sopenharmony_ci * This can be thought of as a "resumed writeback" of the page 8658c2ecf20Sopenharmony_ci * to the swap device. We are basically resuming the same swap 8668c2ecf20Sopenharmony_ci * writeback path that was intercepted with the frontswap_store() 8678c2ecf20Sopenharmony_ci * in the first place. After the page has been decompressed into 8688c2ecf20Sopenharmony_ci * the swap cache, the compressed version stored by zswap can be 8698c2ecf20Sopenharmony_ci * freed. 8708c2ecf20Sopenharmony_ci */ 8718c2ecf20Sopenharmony_cistatic int zswap_writeback_entry(struct zpool *pool, unsigned long handle) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci struct zswap_header *zhdr; 8748c2ecf20Sopenharmony_ci swp_entry_t swpentry; 8758c2ecf20Sopenharmony_ci struct zswap_tree *tree; 8768c2ecf20Sopenharmony_ci pgoff_t offset; 8778c2ecf20Sopenharmony_ci struct zswap_entry *entry; 8788c2ecf20Sopenharmony_ci struct page *page; 8798c2ecf20Sopenharmony_ci struct crypto_comp *tfm; 8808c2ecf20Sopenharmony_ci u8 *src, *dst, *tmp = NULL; 8818c2ecf20Sopenharmony_ci unsigned int dlen; 8828c2ecf20Sopenharmony_ci int ret; 8838c2ecf20Sopenharmony_ci struct writeback_control wbc = { 8848c2ecf20Sopenharmony_ci .sync_mode = WB_SYNC_NONE, 8858c2ecf20Sopenharmony_ci }; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (!zpool_can_sleep_mapped(pool)) { 8888c2ecf20Sopenharmony_ci tmp = kmalloc(PAGE_SIZE, GFP_ATOMIC); 8898c2ecf20Sopenharmony_ci if (!tmp) 8908c2ecf20Sopenharmony_ci return -ENOMEM; 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci /* extract swpentry from data */ 8948c2ecf20Sopenharmony_ci zhdr = zpool_map_handle(pool, handle, ZPOOL_MM_RO); 8958c2ecf20Sopenharmony_ci swpentry = zhdr->swpentry; /* here */ 8968c2ecf20Sopenharmony_ci tree = zswap_trees[swp_type(swpentry)]; 8978c2ecf20Sopenharmony_ci offset = swp_offset(swpentry); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci /* find and ref zswap entry */ 9008c2ecf20Sopenharmony_ci spin_lock(&tree->lock); 9018c2ecf20Sopenharmony_ci entry = zswap_entry_find_get(&tree->rbroot, offset); 9028c2ecf20Sopenharmony_ci if (!entry) { 9038c2ecf20Sopenharmony_ci /* entry was invalidated */ 9048c2ecf20Sopenharmony_ci spin_unlock(&tree->lock); 9058c2ecf20Sopenharmony_ci zpool_unmap_handle(pool, handle); 9068c2ecf20Sopenharmony_ci kfree(tmp); 9078c2ecf20Sopenharmony_ci return 0; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci spin_unlock(&tree->lock); 9108c2ecf20Sopenharmony_ci BUG_ON(offset != entry->offset); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci src = (u8 *)zhdr + sizeof(struct zswap_header); 9138c2ecf20Sopenharmony_ci if (!zpool_can_sleep_mapped(pool)) { 9148c2ecf20Sopenharmony_ci memcpy(tmp, src, entry->length); 9158c2ecf20Sopenharmony_ci src = tmp; 9168c2ecf20Sopenharmony_ci zpool_unmap_handle(pool, handle); 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci /* try to allocate swap cache page */ 9208c2ecf20Sopenharmony_ci switch (zswap_get_swap_cache_page(swpentry, &page)) { 9218c2ecf20Sopenharmony_ci case ZSWAP_SWAPCACHE_FAIL: /* no memory or invalidate happened */ 9228c2ecf20Sopenharmony_ci ret = -ENOMEM; 9238c2ecf20Sopenharmony_ci goto fail; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci case ZSWAP_SWAPCACHE_EXIST: 9268c2ecf20Sopenharmony_ci /* page is already in the swap cache, ignore for now */ 9278c2ecf20Sopenharmony_ci put_page(page); 9288c2ecf20Sopenharmony_ci ret = -EEXIST; 9298c2ecf20Sopenharmony_ci goto fail; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci case ZSWAP_SWAPCACHE_NEW: /* page is locked */ 9328c2ecf20Sopenharmony_ci /* decompress */ 9338c2ecf20Sopenharmony_ci dlen = PAGE_SIZE; 9348c2ecf20Sopenharmony_ci dst = kmap_atomic(page); 9358c2ecf20Sopenharmony_ci tfm = *get_cpu_ptr(entry->pool->tfm); 9368c2ecf20Sopenharmony_ci ret = crypto_comp_decompress(tfm, src, entry->length, 9378c2ecf20Sopenharmony_ci dst, &dlen); 9388c2ecf20Sopenharmony_ci put_cpu_ptr(entry->pool->tfm); 9398c2ecf20Sopenharmony_ci kunmap_atomic(dst); 9408c2ecf20Sopenharmony_ci BUG_ON(ret); 9418c2ecf20Sopenharmony_ci BUG_ON(dlen != PAGE_SIZE); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci /* page is up to date */ 9448c2ecf20Sopenharmony_ci SetPageUptodate(page); 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci /* move it to the tail of the inactive list after end_writeback */ 9488c2ecf20Sopenharmony_ci SetPageReclaim(page); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci /* start writeback */ 9518c2ecf20Sopenharmony_ci __swap_writepage(page, &wbc, end_swap_bio_write); 9528c2ecf20Sopenharmony_ci put_page(page); 9538c2ecf20Sopenharmony_ci zswap_written_back_pages++; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci spin_lock(&tree->lock); 9568c2ecf20Sopenharmony_ci /* drop local reference */ 9578c2ecf20Sopenharmony_ci zswap_entry_put(tree, entry); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci /* 9608c2ecf20Sopenharmony_ci * There are two possible situations for entry here: 9618c2ecf20Sopenharmony_ci * (1) refcount is 1(normal case), entry is valid and on the tree 9628c2ecf20Sopenharmony_ci * (2) refcount is 0, entry is freed and not on the tree 9638c2ecf20Sopenharmony_ci * because invalidate happened during writeback 9648c2ecf20Sopenharmony_ci * search the tree and free the entry if find entry 9658c2ecf20Sopenharmony_ci */ 9668c2ecf20Sopenharmony_ci if (entry == zswap_rb_search(&tree->rbroot, offset)) 9678c2ecf20Sopenharmony_ci zswap_entry_put(tree, entry); 9688c2ecf20Sopenharmony_ci spin_unlock(&tree->lock); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci goto end; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci /* 9738c2ecf20Sopenharmony_ci * if we get here due to ZSWAP_SWAPCACHE_EXIST 9748c2ecf20Sopenharmony_ci * a load may happening concurrently 9758c2ecf20Sopenharmony_ci * it is safe and okay to not free the entry 9768c2ecf20Sopenharmony_ci * if we free the entry in the following put 9778c2ecf20Sopenharmony_ci * it it either okay to return !0 9788c2ecf20Sopenharmony_ci */ 9798c2ecf20Sopenharmony_cifail: 9808c2ecf20Sopenharmony_ci spin_lock(&tree->lock); 9818c2ecf20Sopenharmony_ci zswap_entry_put(tree, entry); 9828c2ecf20Sopenharmony_ci spin_unlock(&tree->lock); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ciend: 9858c2ecf20Sopenharmony_ci if (zpool_can_sleep_mapped(pool)) 9868c2ecf20Sopenharmony_ci zpool_unmap_handle(pool, handle); 9878c2ecf20Sopenharmony_ci else 9888c2ecf20Sopenharmony_ci kfree(tmp); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci return ret; 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_cistatic int zswap_is_page_same_filled(void *ptr, unsigned long *value) 9948c2ecf20Sopenharmony_ci{ 9958c2ecf20Sopenharmony_ci unsigned int pos; 9968c2ecf20Sopenharmony_ci unsigned long *page; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci page = (unsigned long *)ptr; 9998c2ecf20Sopenharmony_ci for (pos = 1; pos < PAGE_SIZE / sizeof(*page); pos++) { 10008c2ecf20Sopenharmony_ci if (page[pos] != page[0]) 10018c2ecf20Sopenharmony_ci return 0; 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci *value = page[0]; 10048c2ecf20Sopenharmony_ci return 1; 10058c2ecf20Sopenharmony_ci} 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_cistatic void zswap_fill_page(void *ptr, unsigned long value) 10088c2ecf20Sopenharmony_ci{ 10098c2ecf20Sopenharmony_ci unsigned long *page; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci page = (unsigned long *)ptr; 10128c2ecf20Sopenharmony_ci memset_l(page, value, PAGE_SIZE / sizeof(unsigned long)); 10138c2ecf20Sopenharmony_ci} 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci/********************************* 10168c2ecf20Sopenharmony_ci* frontswap hooks 10178c2ecf20Sopenharmony_ci**********************************/ 10188c2ecf20Sopenharmony_ci/* attempts to compress and store an single page */ 10198c2ecf20Sopenharmony_cistatic int zswap_frontswap_store(unsigned type, pgoff_t offset, 10208c2ecf20Sopenharmony_ci struct page *page) 10218c2ecf20Sopenharmony_ci{ 10228c2ecf20Sopenharmony_ci struct zswap_tree *tree = zswap_trees[type]; 10238c2ecf20Sopenharmony_ci struct zswap_entry *entry, *dupentry; 10248c2ecf20Sopenharmony_ci struct crypto_comp *tfm; 10258c2ecf20Sopenharmony_ci int ret; 10268c2ecf20Sopenharmony_ci unsigned int hlen, dlen = PAGE_SIZE; 10278c2ecf20Sopenharmony_ci unsigned long handle, value; 10288c2ecf20Sopenharmony_ci char *buf; 10298c2ecf20Sopenharmony_ci u8 *src, *dst; 10308c2ecf20Sopenharmony_ci struct zswap_header zhdr = { .swpentry = swp_entry(type, offset) }; 10318c2ecf20Sopenharmony_ci gfp_t gfp; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci /* THP isn't supported */ 10348c2ecf20Sopenharmony_ci if (PageTransHuge(page)) { 10358c2ecf20Sopenharmony_ci ret = -EINVAL; 10368c2ecf20Sopenharmony_ci goto reject; 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci if (!zswap_enabled || !tree) { 10408c2ecf20Sopenharmony_ci ret = -ENODEV; 10418c2ecf20Sopenharmony_ci goto reject; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci /* reclaim space if needed */ 10458c2ecf20Sopenharmony_ci if (zswap_is_full()) { 10468c2ecf20Sopenharmony_ci struct zswap_pool *pool; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci zswap_pool_limit_hit++; 10498c2ecf20Sopenharmony_ci zswap_pool_reached_full = true; 10508c2ecf20Sopenharmony_ci pool = zswap_pool_last_get(); 10518c2ecf20Sopenharmony_ci if (pool) 10528c2ecf20Sopenharmony_ci queue_work(shrink_wq, &pool->shrink_work); 10538c2ecf20Sopenharmony_ci ret = -ENOMEM; 10548c2ecf20Sopenharmony_ci goto reject; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci if (zswap_pool_reached_full) { 10588c2ecf20Sopenharmony_ci if (!zswap_can_accept()) { 10598c2ecf20Sopenharmony_ci ret = -ENOMEM; 10608c2ecf20Sopenharmony_ci goto reject; 10618c2ecf20Sopenharmony_ci } else 10628c2ecf20Sopenharmony_ci zswap_pool_reached_full = false; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci /* allocate entry */ 10668c2ecf20Sopenharmony_ci entry = zswap_entry_cache_alloc(GFP_KERNEL); 10678c2ecf20Sopenharmony_ci if (!entry) { 10688c2ecf20Sopenharmony_ci zswap_reject_kmemcache_fail++; 10698c2ecf20Sopenharmony_ci ret = -ENOMEM; 10708c2ecf20Sopenharmony_ci goto reject; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci if (zswap_same_filled_pages_enabled) { 10748c2ecf20Sopenharmony_ci src = kmap_atomic(page); 10758c2ecf20Sopenharmony_ci if (zswap_is_page_same_filled(src, &value)) { 10768c2ecf20Sopenharmony_ci kunmap_atomic(src); 10778c2ecf20Sopenharmony_ci entry->offset = offset; 10788c2ecf20Sopenharmony_ci entry->length = 0; 10798c2ecf20Sopenharmony_ci entry->value = value; 10808c2ecf20Sopenharmony_ci atomic_inc(&zswap_same_filled_pages); 10818c2ecf20Sopenharmony_ci goto insert_entry; 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci kunmap_atomic(src); 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci /* if entry is successfully added, it keeps the reference */ 10878c2ecf20Sopenharmony_ci entry->pool = zswap_pool_current_get(); 10888c2ecf20Sopenharmony_ci if (!entry->pool) { 10898c2ecf20Sopenharmony_ci ret = -EINVAL; 10908c2ecf20Sopenharmony_ci goto freepage; 10918c2ecf20Sopenharmony_ci } 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci /* compress */ 10948c2ecf20Sopenharmony_ci dst = get_cpu_var(zswap_dstmem); 10958c2ecf20Sopenharmony_ci tfm = *get_cpu_ptr(entry->pool->tfm); 10968c2ecf20Sopenharmony_ci src = kmap_atomic(page); 10978c2ecf20Sopenharmony_ci ret = crypto_comp_compress(tfm, src, PAGE_SIZE, dst, &dlen); 10988c2ecf20Sopenharmony_ci kunmap_atomic(src); 10998c2ecf20Sopenharmony_ci put_cpu_ptr(entry->pool->tfm); 11008c2ecf20Sopenharmony_ci if (ret) { 11018c2ecf20Sopenharmony_ci ret = -EINVAL; 11028c2ecf20Sopenharmony_ci goto put_dstmem; 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci /* store */ 11068c2ecf20Sopenharmony_ci hlen = zpool_evictable(entry->pool->zpool) ? sizeof(zhdr) : 0; 11078c2ecf20Sopenharmony_ci gfp = __GFP_NORETRY | __GFP_NOWARN | __GFP_KSWAPD_RECLAIM; 11088c2ecf20Sopenharmony_ci if (zpool_malloc_support_movable(entry->pool->zpool)) 11098c2ecf20Sopenharmony_ci gfp |= __GFP_HIGHMEM | __GFP_MOVABLE; 11108c2ecf20Sopenharmony_ci ret = zpool_malloc(entry->pool->zpool, hlen + dlen, gfp, &handle); 11118c2ecf20Sopenharmony_ci if (ret == -ENOSPC) { 11128c2ecf20Sopenharmony_ci zswap_reject_compress_poor++; 11138c2ecf20Sopenharmony_ci goto put_dstmem; 11148c2ecf20Sopenharmony_ci } 11158c2ecf20Sopenharmony_ci if (ret) { 11168c2ecf20Sopenharmony_ci zswap_reject_alloc_fail++; 11178c2ecf20Sopenharmony_ci goto put_dstmem; 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci buf = zpool_map_handle(entry->pool->zpool, handle, ZPOOL_MM_RW); 11208c2ecf20Sopenharmony_ci memcpy(buf, &zhdr, hlen); 11218c2ecf20Sopenharmony_ci memcpy(buf + hlen, dst, dlen); 11228c2ecf20Sopenharmony_ci zpool_unmap_handle(entry->pool->zpool, handle); 11238c2ecf20Sopenharmony_ci put_cpu_var(zswap_dstmem); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci /* populate entry */ 11268c2ecf20Sopenharmony_ci entry->offset = offset; 11278c2ecf20Sopenharmony_ci entry->handle = handle; 11288c2ecf20Sopenharmony_ci entry->length = dlen; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ciinsert_entry: 11318c2ecf20Sopenharmony_ci /* map */ 11328c2ecf20Sopenharmony_ci spin_lock(&tree->lock); 11338c2ecf20Sopenharmony_ci do { 11348c2ecf20Sopenharmony_ci ret = zswap_rb_insert(&tree->rbroot, entry, &dupentry); 11358c2ecf20Sopenharmony_ci if (ret == -EEXIST) { 11368c2ecf20Sopenharmony_ci zswap_duplicate_entry++; 11378c2ecf20Sopenharmony_ci /* remove from rbtree */ 11388c2ecf20Sopenharmony_ci zswap_rb_erase(&tree->rbroot, dupentry); 11398c2ecf20Sopenharmony_ci zswap_entry_put(tree, dupentry); 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci } while (ret == -EEXIST); 11428c2ecf20Sopenharmony_ci spin_unlock(&tree->lock); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci /* update stats */ 11458c2ecf20Sopenharmony_ci atomic_inc(&zswap_stored_pages); 11468c2ecf20Sopenharmony_ci zswap_update_total_size(); 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci return 0; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ciput_dstmem: 11518c2ecf20Sopenharmony_ci put_cpu_var(zswap_dstmem); 11528c2ecf20Sopenharmony_ci zswap_pool_put(entry->pool); 11538c2ecf20Sopenharmony_cifreepage: 11548c2ecf20Sopenharmony_ci zswap_entry_cache_free(entry); 11558c2ecf20Sopenharmony_cireject: 11568c2ecf20Sopenharmony_ci return ret; 11578c2ecf20Sopenharmony_ci} 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci/* 11608c2ecf20Sopenharmony_ci * returns 0 if the page was successfully decompressed 11618c2ecf20Sopenharmony_ci * return -1 on entry not found or error 11628c2ecf20Sopenharmony_ci*/ 11638c2ecf20Sopenharmony_cistatic int zswap_frontswap_load(unsigned type, pgoff_t offset, 11648c2ecf20Sopenharmony_ci struct page *page) 11658c2ecf20Sopenharmony_ci{ 11668c2ecf20Sopenharmony_ci struct zswap_tree *tree = zswap_trees[type]; 11678c2ecf20Sopenharmony_ci struct zswap_entry *entry; 11688c2ecf20Sopenharmony_ci struct crypto_comp *tfm; 11698c2ecf20Sopenharmony_ci u8 *src, *dst, *tmp; 11708c2ecf20Sopenharmony_ci unsigned int dlen; 11718c2ecf20Sopenharmony_ci int ret; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci /* find */ 11748c2ecf20Sopenharmony_ci spin_lock(&tree->lock); 11758c2ecf20Sopenharmony_ci entry = zswap_entry_find_get(&tree->rbroot, offset); 11768c2ecf20Sopenharmony_ci if (!entry) { 11778c2ecf20Sopenharmony_ci /* entry was written back */ 11788c2ecf20Sopenharmony_ci spin_unlock(&tree->lock); 11798c2ecf20Sopenharmony_ci return -1; 11808c2ecf20Sopenharmony_ci } 11818c2ecf20Sopenharmony_ci spin_unlock(&tree->lock); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci if (!entry->length) { 11848c2ecf20Sopenharmony_ci dst = kmap_atomic(page); 11858c2ecf20Sopenharmony_ci zswap_fill_page(dst, entry->value); 11868c2ecf20Sopenharmony_ci kunmap_atomic(dst); 11878c2ecf20Sopenharmony_ci ret = 0; 11888c2ecf20Sopenharmony_ci goto freeentry; 11898c2ecf20Sopenharmony_ci } 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci if (!zpool_can_sleep_mapped(entry->pool->zpool)) { 11928c2ecf20Sopenharmony_ci tmp = kmalloc(entry->length, GFP_ATOMIC); 11938c2ecf20Sopenharmony_ci if (!tmp) { 11948c2ecf20Sopenharmony_ci ret = -ENOMEM; 11958c2ecf20Sopenharmony_ci goto freeentry; 11968c2ecf20Sopenharmony_ci } 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci /* decompress */ 12008c2ecf20Sopenharmony_ci dlen = PAGE_SIZE; 12018c2ecf20Sopenharmony_ci src = zpool_map_handle(entry->pool->zpool, entry->handle, ZPOOL_MM_RO); 12028c2ecf20Sopenharmony_ci if (zpool_evictable(entry->pool->zpool)) 12038c2ecf20Sopenharmony_ci src += sizeof(struct zswap_header); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci if (!zpool_can_sleep_mapped(entry->pool->zpool)) { 12068c2ecf20Sopenharmony_ci memcpy(tmp, src, entry->length); 12078c2ecf20Sopenharmony_ci src = tmp; 12088c2ecf20Sopenharmony_ci zpool_unmap_handle(entry->pool->zpool, entry->handle); 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci dst = kmap_atomic(page); 12128c2ecf20Sopenharmony_ci tfm = *get_cpu_ptr(entry->pool->tfm); 12138c2ecf20Sopenharmony_ci ret = crypto_comp_decompress(tfm, src, entry->length, dst, &dlen); 12148c2ecf20Sopenharmony_ci put_cpu_ptr(entry->pool->tfm); 12158c2ecf20Sopenharmony_ci kunmap_atomic(dst); 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci if (zpool_can_sleep_mapped(entry->pool->zpool)) 12188c2ecf20Sopenharmony_ci zpool_unmap_handle(entry->pool->zpool, entry->handle); 12198c2ecf20Sopenharmony_ci else 12208c2ecf20Sopenharmony_ci kfree(tmp); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci BUG_ON(ret); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_cifreeentry: 12258c2ecf20Sopenharmony_ci spin_lock(&tree->lock); 12268c2ecf20Sopenharmony_ci zswap_entry_put(tree, entry); 12278c2ecf20Sopenharmony_ci spin_unlock(&tree->lock); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci return ret; 12308c2ecf20Sopenharmony_ci} 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci/* frees an entry in zswap */ 12338c2ecf20Sopenharmony_cistatic void zswap_frontswap_invalidate_page(unsigned type, pgoff_t offset) 12348c2ecf20Sopenharmony_ci{ 12358c2ecf20Sopenharmony_ci struct zswap_tree *tree = zswap_trees[type]; 12368c2ecf20Sopenharmony_ci struct zswap_entry *entry; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci /* find */ 12398c2ecf20Sopenharmony_ci spin_lock(&tree->lock); 12408c2ecf20Sopenharmony_ci entry = zswap_rb_search(&tree->rbroot, offset); 12418c2ecf20Sopenharmony_ci if (!entry) { 12428c2ecf20Sopenharmony_ci /* entry was written back */ 12438c2ecf20Sopenharmony_ci spin_unlock(&tree->lock); 12448c2ecf20Sopenharmony_ci return; 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci /* remove from rbtree */ 12488c2ecf20Sopenharmony_ci zswap_rb_erase(&tree->rbroot, entry); 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci /* drop the initial reference from entry creation */ 12518c2ecf20Sopenharmony_ci zswap_entry_put(tree, entry); 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci spin_unlock(&tree->lock); 12548c2ecf20Sopenharmony_ci} 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci/* frees all zswap entries for the given swap type */ 12578c2ecf20Sopenharmony_cistatic void zswap_frontswap_invalidate_area(unsigned type) 12588c2ecf20Sopenharmony_ci{ 12598c2ecf20Sopenharmony_ci struct zswap_tree *tree = zswap_trees[type]; 12608c2ecf20Sopenharmony_ci struct zswap_entry *entry, *n; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci if (!tree) 12638c2ecf20Sopenharmony_ci return; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci /* walk the tree and free everything */ 12668c2ecf20Sopenharmony_ci spin_lock(&tree->lock); 12678c2ecf20Sopenharmony_ci rbtree_postorder_for_each_entry_safe(entry, n, &tree->rbroot, rbnode) 12688c2ecf20Sopenharmony_ci zswap_free_entry(entry); 12698c2ecf20Sopenharmony_ci tree->rbroot = RB_ROOT; 12708c2ecf20Sopenharmony_ci spin_unlock(&tree->lock); 12718c2ecf20Sopenharmony_ci kfree(tree); 12728c2ecf20Sopenharmony_ci zswap_trees[type] = NULL; 12738c2ecf20Sopenharmony_ci} 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_cistatic void zswap_frontswap_init(unsigned type) 12768c2ecf20Sopenharmony_ci{ 12778c2ecf20Sopenharmony_ci struct zswap_tree *tree; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci tree = kzalloc(sizeof(*tree), GFP_KERNEL); 12808c2ecf20Sopenharmony_ci if (!tree) { 12818c2ecf20Sopenharmony_ci pr_err("alloc failed, zswap disabled for swap type %d\n", type); 12828c2ecf20Sopenharmony_ci return; 12838c2ecf20Sopenharmony_ci } 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci tree->rbroot = RB_ROOT; 12868c2ecf20Sopenharmony_ci spin_lock_init(&tree->lock); 12878c2ecf20Sopenharmony_ci zswap_trees[type] = tree; 12888c2ecf20Sopenharmony_ci} 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_cistatic struct frontswap_ops zswap_frontswap_ops = { 12918c2ecf20Sopenharmony_ci .store = zswap_frontswap_store, 12928c2ecf20Sopenharmony_ci .load = zswap_frontswap_load, 12938c2ecf20Sopenharmony_ci .invalidate_page = zswap_frontswap_invalidate_page, 12948c2ecf20Sopenharmony_ci .invalidate_area = zswap_frontswap_invalidate_area, 12958c2ecf20Sopenharmony_ci .init = zswap_frontswap_init 12968c2ecf20Sopenharmony_ci}; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci/********************************* 12998c2ecf20Sopenharmony_ci* debugfs functions 13008c2ecf20Sopenharmony_ci**********************************/ 13018c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 13028c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_cistatic struct dentry *zswap_debugfs_root; 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_cistatic int __init zswap_debugfs_init(void) 13078c2ecf20Sopenharmony_ci{ 13088c2ecf20Sopenharmony_ci if (!debugfs_initialized()) 13098c2ecf20Sopenharmony_ci return -ENODEV; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci zswap_debugfs_root = debugfs_create_dir("zswap", NULL); 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci debugfs_create_u64("pool_limit_hit", 0444, 13148c2ecf20Sopenharmony_ci zswap_debugfs_root, &zswap_pool_limit_hit); 13158c2ecf20Sopenharmony_ci debugfs_create_u64("reject_reclaim_fail", 0444, 13168c2ecf20Sopenharmony_ci zswap_debugfs_root, &zswap_reject_reclaim_fail); 13178c2ecf20Sopenharmony_ci debugfs_create_u64("reject_alloc_fail", 0444, 13188c2ecf20Sopenharmony_ci zswap_debugfs_root, &zswap_reject_alloc_fail); 13198c2ecf20Sopenharmony_ci debugfs_create_u64("reject_kmemcache_fail", 0444, 13208c2ecf20Sopenharmony_ci zswap_debugfs_root, &zswap_reject_kmemcache_fail); 13218c2ecf20Sopenharmony_ci debugfs_create_u64("reject_compress_poor", 0444, 13228c2ecf20Sopenharmony_ci zswap_debugfs_root, &zswap_reject_compress_poor); 13238c2ecf20Sopenharmony_ci debugfs_create_u64("written_back_pages", 0444, 13248c2ecf20Sopenharmony_ci zswap_debugfs_root, &zswap_written_back_pages); 13258c2ecf20Sopenharmony_ci debugfs_create_u64("duplicate_entry", 0444, 13268c2ecf20Sopenharmony_ci zswap_debugfs_root, &zswap_duplicate_entry); 13278c2ecf20Sopenharmony_ci debugfs_create_u64("pool_total_size", 0444, 13288c2ecf20Sopenharmony_ci zswap_debugfs_root, &zswap_pool_total_size); 13298c2ecf20Sopenharmony_ci debugfs_create_atomic_t("stored_pages", 0444, 13308c2ecf20Sopenharmony_ci zswap_debugfs_root, &zswap_stored_pages); 13318c2ecf20Sopenharmony_ci debugfs_create_atomic_t("same_filled_pages", 0444, 13328c2ecf20Sopenharmony_ci zswap_debugfs_root, &zswap_same_filled_pages); 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci return 0; 13358c2ecf20Sopenharmony_ci} 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_cistatic void __exit zswap_debugfs_exit(void) 13388c2ecf20Sopenharmony_ci{ 13398c2ecf20Sopenharmony_ci debugfs_remove_recursive(zswap_debugfs_root); 13408c2ecf20Sopenharmony_ci} 13418c2ecf20Sopenharmony_ci#else 13428c2ecf20Sopenharmony_cistatic int __init zswap_debugfs_init(void) 13438c2ecf20Sopenharmony_ci{ 13448c2ecf20Sopenharmony_ci return 0; 13458c2ecf20Sopenharmony_ci} 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_cistatic void __exit zswap_debugfs_exit(void) { } 13488c2ecf20Sopenharmony_ci#endif 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci/********************************* 13518c2ecf20Sopenharmony_ci* module init and exit 13528c2ecf20Sopenharmony_ci**********************************/ 13538c2ecf20Sopenharmony_cistatic int __init init_zswap(void) 13548c2ecf20Sopenharmony_ci{ 13558c2ecf20Sopenharmony_ci struct zswap_pool *pool; 13568c2ecf20Sopenharmony_ci int ret; 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci zswap_init_started = true; 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci if (zswap_entry_cache_create()) { 13618c2ecf20Sopenharmony_ci pr_err("entry cache creation failed\n"); 13628c2ecf20Sopenharmony_ci goto cache_fail; 13638c2ecf20Sopenharmony_ci } 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci ret = cpuhp_setup_state(CPUHP_MM_ZSWP_MEM_PREPARE, "mm/zswap:prepare", 13668c2ecf20Sopenharmony_ci zswap_dstmem_prepare, zswap_dstmem_dead); 13678c2ecf20Sopenharmony_ci if (ret) { 13688c2ecf20Sopenharmony_ci pr_err("dstmem alloc failed\n"); 13698c2ecf20Sopenharmony_ci goto dstmem_fail; 13708c2ecf20Sopenharmony_ci } 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci ret = cpuhp_setup_state_multi(CPUHP_MM_ZSWP_POOL_PREPARE, 13738c2ecf20Sopenharmony_ci "mm/zswap_pool:prepare", 13748c2ecf20Sopenharmony_ci zswap_cpu_comp_prepare, 13758c2ecf20Sopenharmony_ci zswap_cpu_comp_dead); 13768c2ecf20Sopenharmony_ci if (ret) 13778c2ecf20Sopenharmony_ci goto hp_fail; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci pool = __zswap_pool_create_fallback(); 13808c2ecf20Sopenharmony_ci if (pool) { 13818c2ecf20Sopenharmony_ci pr_info("loaded using pool %s/%s\n", pool->tfm_name, 13828c2ecf20Sopenharmony_ci zpool_get_type(pool->zpool)); 13838c2ecf20Sopenharmony_ci list_add(&pool->list, &zswap_pools); 13848c2ecf20Sopenharmony_ci zswap_has_pool = true; 13858c2ecf20Sopenharmony_ci } else { 13868c2ecf20Sopenharmony_ci pr_err("pool creation failed\n"); 13878c2ecf20Sopenharmony_ci zswap_enabled = false; 13888c2ecf20Sopenharmony_ci } 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci shrink_wq = create_workqueue("zswap-shrink"); 13918c2ecf20Sopenharmony_ci if (!shrink_wq) 13928c2ecf20Sopenharmony_ci goto fallback_fail; 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci frontswap_register_ops(&zswap_frontswap_ops); 13958c2ecf20Sopenharmony_ci if (zswap_debugfs_init()) 13968c2ecf20Sopenharmony_ci pr_warn("debugfs initialization failed\n"); 13978c2ecf20Sopenharmony_ci return 0; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_cifallback_fail: 14008c2ecf20Sopenharmony_ci if (pool) 14018c2ecf20Sopenharmony_ci zswap_pool_destroy(pool); 14028c2ecf20Sopenharmony_cihp_fail: 14038c2ecf20Sopenharmony_ci cpuhp_remove_state(CPUHP_MM_ZSWP_MEM_PREPARE); 14048c2ecf20Sopenharmony_cidstmem_fail: 14058c2ecf20Sopenharmony_ci zswap_entry_cache_destroy(); 14068c2ecf20Sopenharmony_cicache_fail: 14078c2ecf20Sopenharmony_ci /* if built-in, we aren't unloaded on failure; don't allow use */ 14088c2ecf20Sopenharmony_ci zswap_init_failed = true; 14098c2ecf20Sopenharmony_ci zswap_enabled = false; 14108c2ecf20Sopenharmony_ci return -ENOMEM; 14118c2ecf20Sopenharmony_ci} 14128c2ecf20Sopenharmony_ci/* must be late so crypto has time to come up */ 14138c2ecf20Sopenharmony_cilate_initcall(init_zswap); 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 14168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Seth Jennings <sjennings@variantweb.net>"); 14178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Compressed cache for swap pages"); 1418