* [PATCH] zswap: add allocation hysteresis if pool limit is hit
@ 2019-12-25 20:37 vitaly.wool
2019-12-31 22:57 ` Andrew Morton
0 siblings, 1 reply; 2+ messages in thread
From: vitaly.wool @ 2019-12-25 20:37 UTC (permalink / raw)
To: linux-mm; +Cc: ddstreet, akpm, Vitaly Wool
From: Vitaly Wool <vitaly.wool@konsulko.com>
zswap will always try to shrink pool when zswap is full. If there
is a high pressure on zswap it will result in flipping pages in
and out zswap pool without any real benefit, and the overall system
performance will drop. The previous discussion on this subject [1]
ended up with a suggestion to implement a sort of hysteresis to
refuse taking pages into zswap pool until it has sufficient space
if the limit has been hit. This is my take on this.
Hysteresis is controlled with a sysfs-configurable parameter
'accept_threhsold_percent'. It specifies the threshold at which
zswap would start accepting pages again after it became full.
[1] https://lkml.org/lkml/2019/11/8/949
Signed-off-by: Vitaly Wool <vitaly.wool@konsulko.com>
---
mm/zswap.c | 85 ++++++++++++++++++++++++++++++++++--------------------
1 file changed, 54 insertions(+), 31 deletions(-)
diff --git a/mm/zswap.c b/mm/zswap.c
index 46a322316e52..7ec8bd912d13 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -32,6 +32,7 @@
#include <linux/swapops.h>
#include <linux/writeback.h>
#include <linux/pagemap.h>
+#include <linux/workqueue.h>
/*********************************
* statistics
@@ -65,6 +66,11 @@ static u64 zswap_reject_kmemcache_fail;
/* Duplicate store was encountered (rare) */
static u64 zswap_duplicate_entry;
+/* Shrinker work queue */
+static struct workqueue_struct *shrink_wq;
+/* Pool limit was hit, we need to calm down */
+static bool zswap_pool_reached_full;
+
/*********************************
* tunables
**********************************/
@@ -109,6 +115,11 @@ module_param_cb(zpool, &zswap_zpool_param_ops, &zswap_zpool_type, 0644);
static unsigned int zswap_max_pool_percent = 20;
module_param_named(max_pool_percent, zswap_max_pool_percent, uint, 0644);
+/* The threshold for accepting new pages after the max_pool_percent was hit */
+static unsigned int zswap_accept_thr_percent = 90; /* of max pool size */
+module_param_named(accept_threshold_percent, zswap_accept_thr_percent,
+ uint, 0644);
+
/* Enable/disable handling same-value filled pages (enabled by default) */
static bool zswap_same_filled_pages_enabled = true;
module_param_named(same_filled_pages_enabled, zswap_same_filled_pages_enabled,
@@ -123,7 +134,8 @@ struct zswap_pool {
struct crypto_comp * __percpu *tfm;
struct kref kref;
struct list_head list;
- struct work_struct work;
+ struct work_struct release_work;
+ struct work_struct shrink_work;
struct hlist_node node;
char tfm_name[CRYPTO_MAX_ALG_NAME];
};
@@ -214,6 +226,13 @@ static bool zswap_is_full(void)
DIV_ROUND_UP(zswap_pool_total_size, PAGE_SIZE);
}
+static bool zswap_can_accept(void)
+{
+ return totalram_pages() * zswap_accept_thr_percent / 100 *
+ zswap_max_pool_percent / 100 >
+ DIV_ROUND_UP(zswap_pool_total_size, PAGE_SIZE);
+}
+
static void zswap_update_total_size(void)
{
struct zswap_pool *pool;
@@ -501,6 +520,16 @@ static struct zswap_pool *zswap_pool_find_get(char *type, char *compressor)
return NULL;
}
+static void shrink_worker(struct work_struct *w)
+{
+ struct zswap_pool *pool = container_of(w, typeof(*pool),
+ shrink_work);
+
+ if (zpool_shrink(pool->zpool, 1, NULL))
+ zswap_reject_reclaim_fail++;
+ zswap_pool_put(pool);
+}
+
static struct zswap_pool *zswap_pool_create(char *type, char *compressor)
{
struct zswap_pool *pool;
@@ -551,6 +580,7 @@ static struct zswap_pool *zswap_pool_create(char *type, char *compressor)
*/
kref_init(&pool->kref);
INIT_LIST_HEAD(&pool->list);
+ INIT_WORK(&pool->shrink_work, shrink_worker);
zswap_pool_debug("created", pool);
@@ -624,7 +654,8 @@ static int __must_check zswap_pool_get(struct zswap_pool *pool)
static void __zswap_pool_release(struct work_struct *work)
{
- struct zswap_pool *pool = container_of(work, typeof(*pool), work);
+ struct zswap_pool *pool = container_of(work, typeof(*pool),
+ release_work);
synchronize_rcu();
@@ -647,8 +678,8 @@ static void __zswap_pool_empty(struct kref *kref)
list_del_rcu(&pool->list);
- INIT_WORK(&pool->work, __zswap_pool_release);
- schedule_work(&pool->work);
+ INIT_WORK(&pool->release_work, __zswap_pool_release);
+ schedule_work(&pool->release_work);
spin_unlock(&zswap_pools_lock);
}
@@ -942,22 +973,6 @@ static int zswap_writeback_entry(struct zpool *pool, unsigned long handle)
return ret;
}
-static int zswap_shrink(void)
-{
- struct zswap_pool *pool;
- int ret;
-
- pool = zswap_pool_last_get();
- if (!pool)
- return -ENOENT;
-
- ret = zpool_shrink(pool->zpool, 1, NULL);
-
- zswap_pool_put(pool);
-
- return ret;
-}
-
static int zswap_is_page_same_filled(void *ptr, unsigned long *value)
{
unsigned int pos;
@@ -1011,21 +1026,23 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
/* reclaim space if needed */
if (zswap_is_full()) {
+ struct zswap_pool *pool;
+
zswap_pool_limit_hit++;
- if (zswap_shrink()) {
- zswap_reject_reclaim_fail++;
- ret = -ENOMEM;
- goto reject;
- }
+ zswap_pool_reached_full = true;
+ pool = zswap_pool_last_get();
+ if (pool)
+ queue_work(shrink_wq, &pool->shrink_work);
+ ret = -ENOMEM;
+ goto reject;
+ }
- /* A second zswap_is_full() check after
- * zswap_shrink() to make sure it's now
- * under the max_pool_percent
- */
- if (zswap_is_full()) {
+ if (zswap_pool_reached_full) {
+ if (!zswap_can_accept()) {
ret = -ENOMEM;
goto reject;
- }
+ } else
+ zswap_pool_reached_full = false;
}
/* allocate entry */
@@ -1332,11 +1349,17 @@ static int __init init_zswap(void)
zswap_enabled = false;
}
+ shrink_wq = create_workqueue("zswap-shrink");
+ if (!shrink_wq)
+ goto fallback_fail;
+
frontswap_register_ops(&zswap_frontswap_ops);
if (zswap_debugfs_init())
pr_warn("debugfs initialization failed\n");
return 0;
+fallback_fail:
+ zswap_pool_destroy(pool);
hp_fail:
cpuhp_remove_state(CPUHP_MM_ZSWP_MEM_PREPARE);
dstmem_fail:
--
2.20.1
^ permalink raw reply [flat|nested] 2+ messages in thread* Re: [PATCH] zswap: add allocation hysteresis if pool limit is hit
2019-12-25 20:37 [PATCH] zswap: add allocation hysteresis if pool limit is hit vitaly.wool
@ 2019-12-31 22:57 ` Andrew Morton
0 siblings, 0 replies; 2+ messages in thread
From: Andrew Morton @ 2019-12-31 22:57 UTC (permalink / raw)
To: vitaly.wool; +Cc: linux-mm, ddstreet
On Wed, 25 Dec 2019 22:37:30 +0200 vitaly.wool@konsulko.com wrote:
> From: Vitaly Wool <vitaly.wool@konsulko.com>
>
> zswap will always try to shrink pool when zswap is full. If there
> is a high pressure on zswap it will result in flipping pages in
> and out zswap pool without any real benefit, and the overall system
> performance will drop. The previous discussion on this subject [1]
> ended up with a suggestion to implement a sort of hysteresis to
> refuse taking pages into zswap pool until it has sufficient space
> if the limit has been hit. This is my take on this.
>
> Hysteresis is controlled with a sysfs-configurable parameter
> 'accept_threhsold_percent'. It specifies the threshold at which
> zswap would start accepting pages again after it became full.
Please let's give the full path to the sysfs file.
Can we please include an update to the documentation?
Documentation/vm/zswap.rst, I assume.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2019-12-31 22:57 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-12-25 20:37 [PATCH] zswap: add allocation hysteresis if pool limit is hit vitaly.wool
2019-12-31 22:57 ` Andrew Morton
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox