FAILED: patch "[PATCH] zsmalloc: account the number of compacted pages correctly" failed to apply to 5.4-stable tree
Subsystems (5)
[email protected]
maintainerThe patch below does not apply to the 5.4-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 2395928158059b8f9858365fce7713ce7fef62e4 Mon Sep 17 00:00:00 2001
From: Rokudo Yan <[email protected]>
Date: Thu, 25 Feb 2021 17:18:31 -0800
Subject: [PATCH] zsmalloc: account the number of compacted pages correctly
There exists multiple path may do zram compaction concurrently.
1. auto-compaction triggered during memory reclaim
2. userspace utils write zram<id>/compaction node
So, multiple threads may call zs_shrinker_scan/zs_compact concurrently. But pages_compacted is a per zsmalloc pool variable and modification of the variable is not serialized(through under class->lock). There are two issues here:
1. the pages_compacted may not equal to total number of pages freed(due to concurrently add).
2. zs_shrinker_scan may not return the correct number of pages freed(issued by current shrinker).
The fix is simple:
1. account the number of pages freed in zs_compact locally.
2. use actomic variable pages_compacted to accumulate total number.
drivers/block/zram/zram_drv.c +1 -1
@@ -1081,7 +1081,7 @@ static ssize_t mm_stat_show(struct device *dev, zram->limit_pages <<PAGE_SHIFT , max_used <<PAGE_SHIFT , (u64 )atomic64_read (&zram->stats.same_pages), - pool_stats.pages_compacted, +atomic_long_read (&pool_stats.pages_compacted), (u64 )atomic64_read (&zram->stats.huge_pages), (u64)atomic64_read(&zram->stats.huge_pages_since));up_read (&zram->init_lock);
include/linux/zsmalloc.h +1 -1
@@ -35,7 +35,7 @@ enum zs_mapmode {struct zs_pool_stats {/* How many pages were migrated (freed) */ -unsigned long pages_compacted; +atomic_long_t pages_compacted; };struct zs_pool ;
mm/zsmalloc.c +11 -6
@@ -2212,11 +2212,13 @@ static unsigned long zs_can_compact(struct size_class *class)return obj_wasted * class->pages_per_zspage; } -static void __zs_compact (struct zs_pool *pool,struct size_class *class) +static unsigned long __zs_compact (struct zs_pool *pool, +struct size_class *class) {struct zs_compact_control cc;struct zspage *src_zspage;struct zspage *dst_zspage =NULL ; +unsigned long pages_freed =0 ;spin_lock (&class->lock);while ((src_zspage =isolate_zspage (class, true))) {
@@ -2246,7 +2248,7 @@ static void __zs_compact(struct zs_pool *pool, struct size_class *class)putback_zspage (class, dst_zspage);if (putback_zspage (class, src_zspage) ==ZS_EMPTY ) {free_zspage (pool, class, src_zspage); - pool->stats.pages_compacted += class->pages_per_zspage; + pages_freed += class->pages_per_zspage; }spin_unlock (&class->lock);cond_resched ();
@@ -2257,12 +2259,15 @@ static void __zs_compact(struct zs_pool *pool, struct size_class *class)putback_zspage (class, src_zspage);spin_unlock (&class->lock); + +return pages_freed; }unsigned long zs_compact (struct zs_pool *pool) {int i;struct size_class *class; +unsigned long pages_freed =0 ;for (i =ZS_SIZE_CLASSES -1 ; i >=0 ; i--) { class = pool->size_class[i];
@@ -2270,10 +2275,11 @@ unsigned long zs_compact(struct zs_pool *pool)continue ;if (class->index != i)continue ; -__zs_compact (pool, class); + pages_freed +=__zs_compact (pool, class); } +atomic_long_add (pages_freed, &pool->stats.pages_compacted); -return pool->stats.pages_compacted; +return pages_freed; }EXPORT_SYMBOL_GPL (zs_compact);
@@ -2290,13 +2296,12 @@ static unsigned long zs_shrinker_scan(struct shrinker *shrinker, struct zs_pool *pool = container_of(shrinker, struct zs_pool, shrinker); - pages_freed = pool->stats.pages_compacted;/* * Compact classes and calculate compaction delta. * Can run concurrently with a manually triggered * (by user) compaction. */ - pages_freed =zs_compact (pool) - pages_freed; + pages_freed =zs_compact (pool);return pages_freed ? pages_freed :SHRINK_STOP ; }
PATCH 1/1 zsmalloc: account the number of compacted pages correctly
commit 2395928158059b8f9858365fce7713ce7fef62e4 upstream.
There exists multiple path may do zram compaction concurrently.
1. auto-compaction triggered during memory reclaim
2. userspace utils write zram<id>/compaction node
So, multiple threads may call zs_shrinker_scan/zs_compact concurrently. But pages_compacted is a per zsmalloc pool variable and modification of the variable is not serialized(through under class->lock). There are two issues here:
1. the pages_compacted may not equal to total number of pages freed(due to concurrently add).
2. zs_shrinker_scan may not return the correct number of pages freed(issued by current shrinker).
The fix is simple:
1. account the number of pages freed in zs_compact locally.
2. use actomic variable pages_compacted to accumulate total number.
--- drivers/block/zram/zram_drv.c | 2 +- include/linux/zsmalloc.h | 2 +- mm/zsmalloc.c | 17 +++++++++++------ 3 files changed, 13 insertions(+), 8 deletions(-)
drivers/block/zram/zram_drv.c +1 -1
@@ -1072,7 +1072,7 @@ static ssize_t mm_stat_show(struct device *dev, zram->limit_pages <<PAGE_SHIFT , max_used <<PAGE_SHIFT , (u64 )atomic64_read (&zram->stats.same_pages), - pool_stats.pages_compacted, +atomic_long_read (&pool_stats.pages_compacted), (u64)atomic64_read(&zram->stats.huge_pages));up_read (&zram->init_lock);
include/linux/zsmalloc.h +1 -1
@@ -35,7 +35,7 @@ enum zs_mapmode {struct zs_pool_stats {/* How many pages were migrated (freed) */ -unsigned long pages_compacted; +atomic_long_t pages_compacted; };struct zs_pool ;
mm/zsmalloc.c +11 -6
@@ -2216,11 +2216,13 @@ static unsigned long zs_can_compact(struct size_class *class)return obj_wasted * class->pages_per_zspage; } -static void __zs_compact (struct zs_pool *pool,struct size_class *class) +static unsigned long __zs_compact (struct zs_pool *pool, +struct size_class *class) {struct zs_compact_control cc;struct zspage *src_zspage;struct zspage *dst_zspage =NULL ; +unsigned long pages_freed =0 ;spin_lock (&class->lock);while ((src_zspage =isolate_zspage (class, true))) {
@@ -2250,7 +2252,7 @@ static void __zs_compact(struct zs_pool *pool, struct size_class *class)putback_zspage (class, dst_zspage);if (putback_zspage (class, src_zspage) ==ZS_EMPTY ) {free_zspage (pool, class, src_zspage); - pool->stats.pages_compacted += class->pages_per_zspage; + pages_freed += class->pages_per_zspage; }spin_unlock (&class->lock);cond_resched ();
@@ -2261,12 +2263,15 @@ static void __zs_compact(struct zs_pool *pool, struct size_class *class)putback_zspage (class, src_zspage);spin_unlock (&class->lock); + +return pages_freed; }unsigned long zs_compact (struct zs_pool *pool) {int i;struct size_class *class; +unsigned long pages_freed =0 ;for (i =ZS_SIZE_CLASSES -1 ; i >=0 ; i--) { class = pool->size_class[i];
@@ -2274,10 +2279,11 @@ unsigned long zs_compact(struct zs_pool *pool)continue ;if (class->index != i)continue ; -__zs_compact (pool, class); + pages_freed +=__zs_compact (pool, class); } +atomic_long_add (pages_freed, &pool->stats.pages_compacted); -return pool->stats.pages_compacted; +return pages_freed; }EXPORT_SYMBOL_GPL (zs_compact);
@@ -2294,13 +2300,12 @@ static unsigned long zs_shrinker_scan(struct shrinker *shrinker, struct zs_pool *pool = container_of(shrinker, struct zs_pool, shrinker); - pages_freed = pool->stats.pages_compacted;/* * Compact classes and calculate compaction delta. * Can run concurrently with a manually triggered * (by user) compaction. */ - pages_freed =zs_compact (pool) - pages_freed; + pages_freed =zs_compact (pool);return pages_freed ? pages_freed :SHRINK_STOP ; }
Signature · 1 line
2.25.1
commit 2395928158059b8f9858365fce7713ce7fef62e4 backported to 5.4-stable tree.
There exists multiple path may do zram compaction concurrently.
1. auto-compaction triggered during memory reclaim
2. userspace utils write zram<id>/compaction node
So, multiple threads may call zs_shrinker_scan/zs_compact concurrently. But pages_compacted is a per zsmalloc pool variable and modification of the variable is not serialized(through under class->lock). There are two issues here:
1. the pages_compacted may not equal to total number of pages freed(due to concurrently add).
2. zs_shrinker_scan may not return the correct number of pages freed(issued by current shrinker).
The fix is simple:
1. account the number of pages freed in zs_compact locally.
2. use actomic variable pages_compacted to accumulate total number.
--- drivers/block/zram/zram_drv.c | 2 +- include/linux/zsmalloc.h | 2 +- mm/zsmalloc.c | 17 +++++++++++------ 3 files changed, 13 insertions(+), 8 deletions(-)
drivers/block/zram/zram_drv.c +1 -1
@@ -1072,7 +1072,7 @@ static ssize_t mm_stat_show(struct device *dev, zram->limit_pages <<PAGE_SHIFT , max_used <<PAGE_SHIFT , (u64 )atomic64_read (&zram->stats.same_pages), - pool_stats.pages_compacted, +atomic_long_read (&pool_stats.pages_compacted), (u64)atomic64_read(&zram->stats.huge_pages));up_read (&zram->init_lock);
include/linux/zsmalloc.h +1 -1
@@ -35,7 +35,7 @@ enum zs_mapmode {struct zs_pool_stats {/* How many pages were migrated (freed) */ -unsigned long pages_compacted; +atomic_long_t pages_compacted; };struct zs_pool ;
mm/zsmalloc.c +11 -6
@@ -2216,11 +2216,13 @@ static unsigned long zs_can_compact(struct size_class *class)return obj_wasted * class->pages_per_zspage; } -static void __zs_compact (struct zs_pool *pool,struct size_class *class) +static unsigned long __zs_compact (struct zs_pool *pool, +struct size_class *class) {struct zs_compact_control cc;struct zspage *src_zspage;struct zspage *dst_zspage =NULL ; +unsigned long pages_freed =0 ;spin_lock (&class->lock);while ((src_zspage =isolate_zspage (class, true))) {
@@ -2250,7 +2252,7 @@ static void __zs_compact(struct zs_pool *pool, struct size_class *class)putback_zspage (class, dst_zspage);if (putback_zspage (class, src_zspage) ==ZS_EMPTY ) {free_zspage (pool, class, src_zspage); - pool->stats.pages_compacted += class->pages_per_zspage; + pages_freed += class->pages_per_zspage; }spin_unlock (&class->lock);cond_resched ();
@@ -2261,12 +2263,15 @@ static void __zs_compact(struct zs_pool *pool, struct size_class *class)putback_zspage (class, src_zspage);spin_unlock (&class->lock); + +return pages_freed; }unsigned long zs_compact (struct zs_pool *pool) {int i;struct size_class *class; +unsigned long pages_freed =0 ;for (i =ZS_SIZE_CLASSES -1 ; i >=0 ; i--) { class = pool->size_class[i];
@@ -2274,10 +2279,11 @@ unsigned long zs_compact(struct zs_pool *pool)continue ;if (class->index != i)continue ; -__zs_compact (pool, class); + pages_freed +=__zs_compact (pool, class); } +atomic_long_add (pages_freed, &pool->stats.pages_compacted); -return pool->stats.pages_compacted; +return pages_freed; }EXPORT_SYMBOL_GPL (zs_compact);
@@ -2294,13 +2300,12 @@ static unsigned long zs_shrinker_scan(struct shrinker *shrinker, struct zs_pool *pool = container_of(shrinker, struct zs_pool, shrinker); - pages_freed = pool->stats.pages_compacted;/* * Compact classes and calculate compaction delta. * Can run concurrently with a manually triggered * (by user) compaction. */ - pages_freed =zs_compact (pool) - pages_freed; + pages_freed =zs_compact (pool);return pages_freed ? pages_freed :SHRINK_STOP ; }
Signature · 1 line
2.25.1