Skip to content

Commit

Permalink
zsmalloc: account the number of compacted pages correctly
Browse files Browse the repository at this point in the history
commit 2395928 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.

Link: https://lkml.kernel.org/r/20210202122235.26885-1-wu-yan@tcl.com
Fixes: 860c707 ("zsmalloc: account the number of compacted pages")
Signed-off-by: Rokudo Yan <wu-yan@tcl.com>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Rokudo Yan authored and gregkh committed Mar 4, 2021
1 parent bb53cc2 commit d5ef4d3
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 8 deletions.
2 changes: 1 addition & 1 deletion drivers/block/zram/zram_drv.c
Expand Up @@ -1082,7 +1082,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);
Expand Down
2 changes: 1 addition & 1 deletion include/linux/zsmalloc.h
Expand Up @@ -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;
Expand Down
17 changes: 11 additions & 6 deletions mm/zsmalloc.c
Expand Up @@ -2213,11 +2213,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))) {
Expand Down Expand Up @@ -2247,7 +2249,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();
Expand All @@ -2258,23 +2260,27 @@ 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];
if (!class)
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);

Expand All @@ -2291,13 +2297,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;
}
Expand Down

0 comments on commit d5ef4d3

Please sign in to comment.