.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * zswap.c - zswap driver file |
---|
3 | 4 | * |
---|
.. | .. |
---|
8 | 9 | * than reading from the swap device, can also improve workload performance. |
---|
9 | 10 | * |
---|
10 | 11 | * Copyright (C) 2012 Seth Jennings <sjenning@linux.vnet.ibm.com> |
---|
11 | | - * |
---|
12 | | - * This program is free software; you can redistribute it and/or |
---|
13 | | - * modify it under the terms of the GNU General Public License |
---|
14 | | - * as published by the Free Software Foundation; either version 2 |
---|
15 | | - * of the License, or (at your option) any later version. |
---|
16 | | - * |
---|
17 | | - * This program is distributed in the hope that it will be useful, |
---|
18 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
19 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
20 | | - * GNU General Public License for more details. |
---|
21 | 12 | */ |
---|
22 | 13 | |
---|
23 | 14 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
---|
.. | .. |
---|
27 | 18 | #include <linux/highmem.h> |
---|
28 | 19 | #include <linux/slab.h> |
---|
29 | 20 | #include <linux/spinlock.h> |
---|
| 21 | +#include <linux/local_lock.h> |
---|
30 | 22 | #include <linux/types.h> |
---|
31 | 23 | #include <linux/atomic.h> |
---|
32 | 24 | #include <linux/frontswap.h> |
---|
.. | .. |
---|
41 | 33 | #include <linux/swapops.h> |
---|
42 | 34 | #include <linux/writeback.h> |
---|
43 | 35 | #include <linux/pagemap.h> |
---|
| 36 | +#include <linux/workqueue.h> |
---|
44 | 37 | |
---|
45 | 38 | /********************************* |
---|
46 | 39 | * statistics |
---|
.. | .. |
---|
74 | 67 | /* Duplicate store was encountered (rare) */ |
---|
75 | 68 | static u64 zswap_duplicate_entry; |
---|
76 | 69 | |
---|
| 70 | +/* Shrinker work queue */ |
---|
| 71 | +static struct workqueue_struct *shrink_wq; |
---|
| 72 | +/* Pool limit was hit, we need to calm down */ |
---|
| 73 | +static bool zswap_pool_reached_full; |
---|
| 74 | + |
---|
77 | 75 | /********************************* |
---|
78 | 76 | * tunables |
---|
79 | 77 | **********************************/ |
---|
80 | 78 | |
---|
81 | 79 | #define ZSWAP_PARAM_UNSET "" |
---|
82 | 80 | |
---|
83 | | -/* Enable/disable zswap (disabled by default) */ |
---|
84 | | -static bool zswap_enabled; |
---|
| 81 | +/* Enable/disable zswap */ |
---|
| 82 | +static bool zswap_enabled = IS_ENABLED(CONFIG_ZSWAP_DEFAULT_ON); |
---|
85 | 83 | static int zswap_enabled_param_set(const char *, |
---|
86 | 84 | const struct kernel_param *); |
---|
87 | 85 | static struct kernel_param_ops zswap_enabled_param_ops = { |
---|
.. | .. |
---|
91 | 89 | module_param_cb(enabled, &zswap_enabled_param_ops, &zswap_enabled, 0644); |
---|
92 | 90 | |
---|
93 | 91 | /* Crypto compressor to use */ |
---|
94 | | -#define ZSWAP_COMPRESSOR_DEFAULT "lzo" |
---|
95 | | -static char *zswap_compressor = ZSWAP_COMPRESSOR_DEFAULT; |
---|
| 92 | +static char *zswap_compressor = CONFIG_ZSWAP_COMPRESSOR_DEFAULT; |
---|
96 | 93 | static int zswap_compressor_param_set(const char *, |
---|
97 | 94 | const struct kernel_param *); |
---|
98 | 95 | static struct kernel_param_ops zswap_compressor_param_ops = { |
---|
.. | .. |
---|
104 | 101 | &zswap_compressor, 0644); |
---|
105 | 102 | |
---|
106 | 103 | /* Compressed storage zpool to use */ |
---|
107 | | -#define ZSWAP_ZPOOL_DEFAULT "zbud" |
---|
108 | | -static char *zswap_zpool_type = ZSWAP_ZPOOL_DEFAULT; |
---|
| 104 | +static char *zswap_zpool_type = CONFIG_ZSWAP_ZPOOL_DEFAULT; |
---|
109 | 105 | static int zswap_zpool_param_set(const char *, const struct kernel_param *); |
---|
110 | 106 | static struct kernel_param_ops zswap_zpool_param_ops = { |
---|
111 | 107 | .set = zswap_zpool_param_set, |
---|
.. | .. |
---|
117 | 113 | /* The maximum percentage of memory that the compressed pool can occupy */ |
---|
118 | 114 | static unsigned int zswap_max_pool_percent = 20; |
---|
119 | 115 | module_param_named(max_pool_percent, zswap_max_pool_percent, uint, 0644); |
---|
| 116 | + |
---|
| 117 | +/* The threshold for accepting new pages after the max_pool_percent was hit */ |
---|
| 118 | +static unsigned int zswap_accept_thr_percent = 90; /* of max pool size */ |
---|
| 119 | +module_param_named(accept_threshold_percent, zswap_accept_thr_percent, |
---|
| 120 | + uint, 0644); |
---|
120 | 121 | |
---|
121 | 122 | /* Enable/disable handling same-value filled pages (enabled by default) */ |
---|
122 | 123 | static bool zswap_same_filled_pages_enabled = true; |
---|
.. | .. |
---|
132 | 133 | struct crypto_comp * __percpu *tfm; |
---|
133 | 134 | struct kref kref; |
---|
134 | 135 | struct list_head list; |
---|
135 | | - struct work_struct work; |
---|
| 136 | + struct work_struct release_work; |
---|
| 137 | + struct work_struct shrink_work; |
---|
136 | 138 | struct hlist_node node; |
---|
137 | 139 | char tfm_name[CRYPTO_MAX_ALG_NAME]; |
---|
138 | 140 | }; |
---|
.. | .. |
---|
219 | 221 | |
---|
220 | 222 | static bool zswap_is_full(void) |
---|
221 | 223 | { |
---|
222 | | - return totalram_pages * zswap_max_pool_percent / 100 < |
---|
223 | | - DIV_ROUND_UP(zswap_pool_total_size, PAGE_SIZE); |
---|
| 224 | + return totalram_pages() * zswap_max_pool_percent / 100 < |
---|
| 225 | + DIV_ROUND_UP(zswap_pool_total_size, PAGE_SIZE); |
---|
| 226 | +} |
---|
| 227 | + |
---|
| 228 | +static bool zswap_can_accept(void) |
---|
| 229 | +{ |
---|
| 230 | + return totalram_pages() * zswap_accept_thr_percent / 100 * |
---|
| 231 | + zswap_max_pool_percent / 100 > |
---|
| 232 | + DIV_ROUND_UP(zswap_pool_total_size, PAGE_SIZE); |
---|
224 | 233 | } |
---|
225 | 234 | |
---|
226 | 235 | static void zswap_update_total_size(void) |
---|
.. | .. |
---|
379 | 388 | /********************************* |
---|
380 | 389 | * per-cpu code |
---|
381 | 390 | **********************************/ |
---|
382 | | -static DEFINE_PER_CPU(u8 *, zswap_dstmem); |
---|
| 391 | +struct zswap_comp { |
---|
| 392 | + /* Used for per-CPU dstmem and tfm */ |
---|
| 393 | + local_lock_t lock; |
---|
| 394 | + u8 *dstmem; |
---|
| 395 | +}; |
---|
| 396 | + |
---|
| 397 | +static DEFINE_PER_CPU(struct zswap_comp, zswap_comp) = { |
---|
| 398 | + .lock = INIT_LOCAL_LOCK(lock), |
---|
| 399 | +}; |
---|
383 | 400 | |
---|
384 | 401 | static int zswap_dstmem_prepare(unsigned int cpu) |
---|
385 | 402 | { |
---|
| 403 | + struct zswap_comp *zcomp; |
---|
386 | 404 | u8 *dst; |
---|
387 | 405 | |
---|
388 | 406 | dst = kmalloc_node(PAGE_SIZE * 2, GFP_KERNEL, cpu_to_node(cpu)); |
---|
389 | 407 | if (!dst) |
---|
390 | 408 | return -ENOMEM; |
---|
391 | 409 | |
---|
392 | | - per_cpu(zswap_dstmem, cpu) = dst; |
---|
| 410 | + zcomp = per_cpu_ptr(&zswap_comp, cpu); |
---|
| 411 | + zcomp->dstmem = dst; |
---|
393 | 412 | return 0; |
---|
394 | 413 | } |
---|
395 | 414 | |
---|
396 | 415 | static int zswap_dstmem_dead(unsigned int cpu) |
---|
397 | 416 | { |
---|
398 | | - u8 *dst; |
---|
| 417 | + struct zswap_comp *zcomp; |
---|
399 | 418 | |
---|
400 | | - dst = per_cpu(zswap_dstmem, cpu); |
---|
401 | | - kfree(dst); |
---|
402 | | - per_cpu(zswap_dstmem, cpu) = NULL; |
---|
| 419 | + zcomp = per_cpu_ptr(&zswap_comp, cpu); |
---|
| 420 | + kfree(zcomp->dstmem); |
---|
| 421 | + zcomp->dstmem = NULL; |
---|
403 | 422 | |
---|
404 | 423 | return 0; |
---|
405 | 424 | } |
---|
.. | .. |
---|
510 | 529 | return NULL; |
---|
511 | 530 | } |
---|
512 | 531 | |
---|
| 532 | +static void shrink_worker(struct work_struct *w) |
---|
| 533 | +{ |
---|
| 534 | + struct zswap_pool *pool = container_of(w, typeof(*pool), |
---|
| 535 | + shrink_work); |
---|
| 536 | + |
---|
| 537 | + if (zpool_shrink(pool->zpool, 1, NULL)) |
---|
| 538 | + zswap_reject_reclaim_fail++; |
---|
| 539 | + zswap_pool_put(pool); |
---|
| 540 | +} |
---|
| 541 | + |
---|
513 | 542 | static struct zswap_pool *zswap_pool_create(char *type, char *compressor) |
---|
514 | 543 | { |
---|
515 | 544 | struct zswap_pool *pool; |
---|
.. | .. |
---|
560 | 589 | */ |
---|
561 | 590 | kref_init(&pool->kref); |
---|
562 | 591 | INIT_LIST_HEAD(&pool->list); |
---|
| 592 | + INIT_WORK(&pool->shrink_work, shrink_worker); |
---|
563 | 593 | |
---|
564 | 594 | zswap_pool_debug("created", pool); |
---|
565 | 595 | |
---|
.. | .. |
---|
578 | 608 | bool has_comp, has_zpool; |
---|
579 | 609 | |
---|
580 | 610 | has_comp = crypto_has_comp(zswap_compressor, 0, 0); |
---|
581 | | - if (!has_comp && strcmp(zswap_compressor, ZSWAP_COMPRESSOR_DEFAULT)) { |
---|
| 611 | + if (!has_comp && strcmp(zswap_compressor, |
---|
| 612 | + CONFIG_ZSWAP_COMPRESSOR_DEFAULT)) { |
---|
582 | 613 | pr_err("compressor %s not available, using default %s\n", |
---|
583 | | - zswap_compressor, ZSWAP_COMPRESSOR_DEFAULT); |
---|
| 614 | + zswap_compressor, CONFIG_ZSWAP_COMPRESSOR_DEFAULT); |
---|
584 | 615 | param_free_charp(&zswap_compressor); |
---|
585 | | - zswap_compressor = ZSWAP_COMPRESSOR_DEFAULT; |
---|
| 616 | + zswap_compressor = CONFIG_ZSWAP_COMPRESSOR_DEFAULT; |
---|
586 | 617 | has_comp = crypto_has_comp(zswap_compressor, 0, 0); |
---|
587 | 618 | } |
---|
588 | 619 | if (!has_comp) { |
---|
.. | .. |
---|
593 | 624 | } |
---|
594 | 625 | |
---|
595 | 626 | has_zpool = zpool_has_pool(zswap_zpool_type); |
---|
596 | | - if (!has_zpool && strcmp(zswap_zpool_type, ZSWAP_ZPOOL_DEFAULT)) { |
---|
| 627 | + if (!has_zpool && strcmp(zswap_zpool_type, |
---|
| 628 | + CONFIG_ZSWAP_ZPOOL_DEFAULT)) { |
---|
597 | 629 | pr_err("zpool %s not available, using default %s\n", |
---|
598 | | - zswap_zpool_type, ZSWAP_ZPOOL_DEFAULT); |
---|
| 630 | + zswap_zpool_type, CONFIG_ZSWAP_ZPOOL_DEFAULT); |
---|
599 | 631 | param_free_charp(&zswap_zpool_type); |
---|
600 | | - zswap_zpool_type = ZSWAP_ZPOOL_DEFAULT; |
---|
| 632 | + zswap_zpool_type = CONFIG_ZSWAP_ZPOOL_DEFAULT; |
---|
601 | 633 | has_zpool = zpool_has_pool(zswap_zpool_type); |
---|
602 | 634 | } |
---|
603 | 635 | if (!has_zpool) { |
---|
.. | .. |
---|
633 | 665 | |
---|
634 | 666 | static void __zswap_pool_release(struct work_struct *work) |
---|
635 | 667 | { |
---|
636 | | - struct zswap_pool *pool = container_of(work, typeof(*pool), work); |
---|
| 668 | + struct zswap_pool *pool = container_of(work, typeof(*pool), |
---|
| 669 | + release_work); |
---|
637 | 670 | |
---|
638 | 671 | synchronize_rcu(); |
---|
639 | 672 | |
---|
.. | .. |
---|
656 | 689 | |
---|
657 | 690 | list_del_rcu(&pool->list); |
---|
658 | 691 | |
---|
659 | | - INIT_WORK(&pool->work, __zswap_pool_release); |
---|
660 | | - schedule_work(&pool->work); |
---|
| 692 | + INIT_WORK(&pool->release_work, __zswap_pool_release); |
---|
| 693 | + schedule_work(&pool->release_work); |
---|
661 | 694 | |
---|
662 | 695 | spin_unlock(&zswap_pools_lock); |
---|
663 | 696 | } |
---|
.. | .. |
---|
865 | 898 | /* extract swpentry from data */ |
---|
866 | 899 | zhdr = zpool_map_handle(pool, handle, ZPOOL_MM_RO); |
---|
867 | 900 | swpentry = zhdr->swpentry; /* here */ |
---|
868 | | - zpool_unmap_handle(pool, handle); |
---|
869 | 901 | tree = zswap_trees[swp_type(swpentry)]; |
---|
870 | 902 | offset = swp_offset(swpentry); |
---|
871 | 903 | |
---|
.. | .. |
---|
875 | 907 | if (!entry) { |
---|
876 | 908 | /* entry was invalidated */ |
---|
877 | 909 | spin_unlock(&tree->lock); |
---|
| 910 | + zpool_unmap_handle(pool, handle); |
---|
878 | 911 | return 0; |
---|
879 | 912 | } |
---|
880 | 913 | spin_unlock(&tree->lock); |
---|
.. | .. |
---|
895 | 928 | case ZSWAP_SWAPCACHE_NEW: /* page is locked */ |
---|
896 | 929 | /* decompress */ |
---|
897 | 930 | dlen = PAGE_SIZE; |
---|
898 | | - src = (u8 *)zpool_map_handle(entry->pool->zpool, entry->handle, |
---|
899 | | - ZPOOL_MM_RO) + sizeof(struct zswap_header); |
---|
| 931 | + src = (u8 *)zhdr + sizeof(struct zswap_header); |
---|
900 | 932 | dst = kmap_atomic(page); |
---|
901 | | - tfm = *get_cpu_ptr(entry->pool->tfm); |
---|
| 933 | + local_lock(&zswap_comp.lock); |
---|
| 934 | + tfm = *this_cpu_ptr(entry->pool->tfm); |
---|
902 | 935 | ret = crypto_comp_decompress(tfm, src, entry->length, |
---|
903 | 936 | dst, &dlen); |
---|
904 | | - put_cpu_ptr(entry->pool->tfm); |
---|
| 937 | + local_unlock(&zswap_comp.lock); |
---|
905 | 938 | kunmap_atomic(dst); |
---|
906 | | - zpool_unmap_handle(entry->pool->zpool, entry->handle); |
---|
907 | 939 | BUG_ON(ret); |
---|
908 | 940 | BUG_ON(dlen != PAGE_SIZE); |
---|
909 | 941 | |
---|
.. | .. |
---|
949 | 981 | spin_unlock(&tree->lock); |
---|
950 | 982 | |
---|
951 | 983 | end: |
---|
952 | | - return ret; |
---|
953 | | -} |
---|
954 | | - |
---|
955 | | -static int zswap_shrink(void) |
---|
956 | | -{ |
---|
957 | | - struct zswap_pool *pool; |
---|
958 | | - int ret; |
---|
959 | | - |
---|
960 | | - pool = zswap_pool_last_get(); |
---|
961 | | - if (!pool) |
---|
962 | | - return -ENOENT; |
---|
963 | | - |
---|
964 | | - ret = zpool_shrink(pool->zpool, 1, NULL); |
---|
965 | | - |
---|
966 | | - zswap_pool_put(pool); |
---|
967 | | - |
---|
| 984 | + zpool_unmap_handle(pool, handle); |
---|
968 | 985 | return ret; |
---|
969 | 986 | } |
---|
970 | 987 | |
---|
.. | .. |
---|
1006 | 1023 | char *buf; |
---|
1007 | 1024 | u8 *src, *dst; |
---|
1008 | 1025 | struct zswap_header zhdr = { .swpentry = swp_entry(type, offset) }; |
---|
| 1026 | + gfp_t gfp; |
---|
1009 | 1027 | |
---|
1010 | 1028 | /* THP isn't supported */ |
---|
1011 | 1029 | if (PageTransHuge(page)) { |
---|
.. | .. |
---|
1020 | 1038 | |
---|
1021 | 1039 | /* reclaim space if needed */ |
---|
1022 | 1040 | if (zswap_is_full()) { |
---|
1023 | | - zswap_pool_limit_hit++; |
---|
1024 | | - if (zswap_shrink()) { |
---|
1025 | | - zswap_reject_reclaim_fail++; |
---|
1026 | | - ret = -ENOMEM; |
---|
1027 | | - goto reject; |
---|
1028 | | - } |
---|
| 1041 | + struct zswap_pool *pool; |
---|
1029 | 1042 | |
---|
1030 | | - /* A second zswap_is_full() check after |
---|
1031 | | - * zswap_shrink() to make sure it's now |
---|
1032 | | - * under the max_pool_percent |
---|
1033 | | - */ |
---|
1034 | | - if (zswap_is_full()) { |
---|
| 1043 | + zswap_pool_limit_hit++; |
---|
| 1044 | + zswap_pool_reached_full = true; |
---|
| 1045 | + pool = zswap_pool_last_get(); |
---|
| 1046 | + if (pool) |
---|
| 1047 | + queue_work(shrink_wq, &pool->shrink_work); |
---|
| 1048 | + ret = -ENOMEM; |
---|
| 1049 | + goto reject; |
---|
| 1050 | + } |
---|
| 1051 | + |
---|
| 1052 | + if (zswap_pool_reached_full) { |
---|
| 1053 | + if (!zswap_can_accept()) { |
---|
1035 | 1054 | ret = -ENOMEM; |
---|
1036 | 1055 | goto reject; |
---|
1037 | | - } |
---|
| 1056 | + } else |
---|
| 1057 | + zswap_pool_reached_full = false; |
---|
1038 | 1058 | } |
---|
1039 | 1059 | |
---|
1040 | 1060 | /* allocate entry */ |
---|
.. | .. |
---|
1066 | 1086 | } |
---|
1067 | 1087 | |
---|
1068 | 1088 | /* compress */ |
---|
1069 | | - dst = get_cpu_var(zswap_dstmem); |
---|
1070 | | - tfm = *get_cpu_ptr(entry->pool->tfm); |
---|
| 1089 | + local_lock(&zswap_comp.lock); |
---|
| 1090 | + dst = *this_cpu_ptr(&zswap_comp.dstmem); |
---|
| 1091 | + tfm = *this_cpu_ptr(entry->pool->tfm); |
---|
1071 | 1092 | src = kmap_atomic(page); |
---|
1072 | 1093 | ret = crypto_comp_compress(tfm, src, PAGE_SIZE, dst, &dlen); |
---|
1073 | 1094 | kunmap_atomic(src); |
---|
1074 | | - put_cpu_ptr(entry->pool->tfm); |
---|
1075 | 1095 | if (ret) { |
---|
1076 | 1096 | ret = -EINVAL; |
---|
1077 | 1097 | goto put_dstmem; |
---|
.. | .. |
---|
1079 | 1099 | |
---|
1080 | 1100 | /* store */ |
---|
1081 | 1101 | hlen = zpool_evictable(entry->pool->zpool) ? sizeof(zhdr) : 0; |
---|
1082 | | - ret = zpool_malloc(entry->pool->zpool, hlen + dlen, |
---|
1083 | | - __GFP_NORETRY | __GFP_NOWARN | __GFP_KSWAPD_RECLAIM, |
---|
1084 | | - &handle); |
---|
| 1102 | + gfp = __GFP_NORETRY | __GFP_NOWARN | __GFP_KSWAPD_RECLAIM; |
---|
| 1103 | + if (zpool_malloc_support_movable(entry->pool->zpool)) |
---|
| 1104 | + gfp |= __GFP_HIGHMEM | __GFP_MOVABLE; |
---|
| 1105 | + ret = zpool_malloc(entry->pool->zpool, hlen + dlen, gfp, &handle); |
---|
1085 | 1106 | if (ret == -ENOSPC) { |
---|
1086 | 1107 | zswap_reject_compress_poor++; |
---|
1087 | 1108 | goto put_dstmem; |
---|
.. | .. |
---|
1094 | 1115 | memcpy(buf, &zhdr, hlen); |
---|
1095 | 1116 | memcpy(buf + hlen, dst, dlen); |
---|
1096 | 1117 | zpool_unmap_handle(entry->pool->zpool, handle); |
---|
1097 | | - put_cpu_var(zswap_dstmem); |
---|
| 1118 | + local_unlock(&zswap_comp.lock); |
---|
1098 | 1119 | |
---|
1099 | 1120 | /* populate entry */ |
---|
1100 | 1121 | entry->offset = offset; |
---|
.. | .. |
---|
1122 | 1143 | return 0; |
---|
1123 | 1144 | |
---|
1124 | 1145 | put_dstmem: |
---|
1125 | | - put_cpu_var(zswap_dstmem); |
---|
| 1146 | + local_unlock(&zswap_comp.lock); |
---|
1126 | 1147 | zswap_pool_put(entry->pool); |
---|
1127 | 1148 | freepage: |
---|
1128 | 1149 | zswap_entry_cache_free(entry); |
---|
.. | .. |
---|
1167 | 1188 | if (zpool_evictable(entry->pool->zpool)) |
---|
1168 | 1189 | src += sizeof(struct zswap_header); |
---|
1169 | 1190 | dst = kmap_atomic(page); |
---|
1170 | | - tfm = *get_cpu_ptr(entry->pool->tfm); |
---|
| 1191 | + local_lock(&zswap_comp.lock); |
---|
| 1192 | + tfm = *this_cpu_ptr(entry->pool->tfm); |
---|
1171 | 1193 | ret = crypto_comp_decompress(tfm, src, entry->length, dst, &dlen); |
---|
1172 | | - put_cpu_ptr(entry->pool->tfm); |
---|
| 1194 | + local_unlock(&zswap_comp.lock); |
---|
1173 | 1195 | kunmap_atomic(dst); |
---|
1174 | 1196 | zpool_unmap_handle(entry->pool->zpool, entry->handle); |
---|
1175 | 1197 | BUG_ON(ret); |
---|
.. | .. |
---|
1262 | 1284 | return -ENODEV; |
---|
1263 | 1285 | |
---|
1264 | 1286 | zswap_debugfs_root = debugfs_create_dir("zswap", NULL); |
---|
1265 | | - if (!zswap_debugfs_root) |
---|
1266 | | - return -ENOMEM; |
---|
1267 | 1287 | |
---|
1268 | 1288 | debugfs_create_u64("pool_limit_hit", 0444, |
---|
1269 | 1289 | zswap_debugfs_root, &zswap_pool_limit_hit); |
---|
.. | .. |
---|
1342 | 1362 | zswap_enabled = false; |
---|
1343 | 1363 | } |
---|
1344 | 1364 | |
---|
| 1365 | + shrink_wq = create_workqueue("zswap-shrink"); |
---|
| 1366 | + if (!shrink_wq) |
---|
| 1367 | + goto fallback_fail; |
---|
| 1368 | + |
---|
1345 | 1369 | frontswap_register_ops(&zswap_frontswap_ops); |
---|
1346 | 1370 | if (zswap_debugfs_init()) |
---|
1347 | 1371 | pr_warn("debugfs initialization failed\n"); |
---|
1348 | 1372 | return 0; |
---|
1349 | 1373 | |
---|
| 1374 | +fallback_fail: |
---|
| 1375 | + if (pool) |
---|
| 1376 | + zswap_pool_destroy(pool); |
---|
1350 | 1377 | hp_fail: |
---|
1351 | 1378 | cpuhp_remove_state(CPUHP_MM_ZSWP_MEM_PREPARE); |
---|
1352 | 1379 | dstmem_fail: |
---|