Skip to content

Commit d8becc3

Browse files
Barry Song (Xiaomi)kawasaki
authored andcommitted
zram: support asynchronous GC for lazy slot freeing
Swap freeing can be expensive when unmapping a VMA containing many swap entries. This has been reported to significantly delay memory reclamation during Android’s low-memory killing, especially when multiple processes are terminated to free memory, with slot_free() accounting for more than 80% of the total cost of freeing swap entries. Two earlier attempts by Lei and Zhiguo added a new thread in the mm core to asynchronously collect and free swap entries [1][2], but the design itself is fairly complex. When anon folios and swap entries are mixed within a process, reclaiming anon folios from killed processes helps return memory to the system as quickly as possible, so that newly launched applications can satisfy their memory demands. It is not ideal for swap freeing to block anon folio freeing. On the other hand, swap freeing can still return memory to the system, although at a slower rate due to memory compression. Therefore, in zram, we introduce a GC worker to allow anon folio freeing and slot_free to run in parallel, since slot_free is performed asynchronously, maximizing the rate at which memory is returned to the system. Xueyuan’s test on RK3588 shows that unmapping a 256MB swap-filled VMA becomes 3.4× faster when pinning tasks to CPU2, reducing the execution time from 63,102,982 ns to 18,570,726 ns. A positive side effect is that async GC also slightly improves do_swap_page() performance, as it no longer has to wait for slot_free() to complete. Xueyuan’s test shows that swapping in 256MB of data (each page filled with repeating patterns such as “1024 one”, “1024 two”, “1024 three”, and “1024 four”) reduces execution time from 1,358,133,886 ns to 1,104,315,986 ns, achieving a 1.22× speedup. [1] https://lore.kernel.org/all/[email protected]/ [2] https://lore.kernel.org/all/[email protected]/ Tested-by: Xueyuan Chen <[email protected]> Signed-off-by: Barry Song (Xiaomi) <[email protected]>
1 parent 857ada9 commit d8becc3

2 files changed

Lines changed: 58 additions & 1 deletion

File tree

drivers/block/zram/zram_drv.c

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1958,6 +1958,23 @@ static ssize_t debug_stat_show(struct device *dev,
19581958
return ret;
19591959
}
19601960

1961+
static void gc_slots_free(struct zram *zram)
1962+
{
1963+
size_t num_pages = zram->disksize >> PAGE_SHIFT;
1964+
unsigned long index;
1965+
1966+
index = find_next_bit(zram->gc_map, num_pages, 0);
1967+
while (index < num_pages) {
1968+
if (slot_trylock(zram, index)) {
1969+
if (test_bit(index, zram->gc_map))
1970+
slot_free(zram, index);
1971+
slot_unlock(zram, index);
1972+
cond_resched();
1973+
}
1974+
index = find_next_bit(zram->gc_map, num_pages, index + 1);
1975+
}
1976+
}
1977+
19611978
static void zram_meta_free(struct zram *zram, u64 disksize)
19621979
{
19631980
size_t num_pages = disksize >> PAGE_SHIFT;
@@ -1966,13 +1983,24 @@ static void zram_meta_free(struct zram *zram, u64 disksize)
19661983
if (!zram->table)
19671984
return;
19681985

1986+
flush_work(&zram->gc_work);
1987+
gc_slots_free(zram);
1988+
19691989
/* Free all pages that are still in this zram device */
19701990
for (index = 0; index < num_pages; index++)
19711991
slot_free(zram, index);
19721992

19731993
zs_destroy_pool(zram->mem_pool);
19741994
vfree(zram->table);
19751995
zram->table = NULL;
1996+
bitmap_free(zram->gc_map);
1997+
}
1998+
1999+
static void zram_gc_work(struct work_struct *work)
2000+
{
2001+
struct zram *zram = container_of(work, struct zram, gc_work);
2002+
2003+
gc_slots_free(zram);
19762004
}
19772005

19782006
static bool zram_meta_alloc(struct zram *zram, u64 disksize)
@@ -1991,12 +2019,22 @@ static bool zram_meta_alloc(struct zram *zram, u64 disksize)
19912019
return false;
19922020
}
19932021

2022+
zram->gc_map = bitmap_zalloc(num_pages, GFP_KERNEL);
2023+
if (!zram->gc_map) {
2024+
zs_destroy_pool(zram->mem_pool);
2025+
vfree(zram->table);
2026+
zram->table = NULL;
2027+
return false;
2028+
}
2029+
19942030
if (!huge_class_size)
19952031
huge_class_size = zs_huge_class_size(zram->mem_pool);
19962032

19972033
for (index = 0; index < num_pages; index++)
19982034
slot_lock_init(zram, index);
19992035

2036+
INIT_WORK(&zram->gc_work, zram_gc_work);
2037+
20002038
return true;
20012039
}
20022040

@@ -2008,6 +2046,8 @@ static void slot_free(struct zram *zram, u32 index)
20082046
zram->table[index].attr.ac_time = 0;
20092047
#endif
20102048

2049+
if (test_and_clear_bit(index, zram->gc_map))
2050+
atomic64_dec(&zram->stats.gc_slots);
20112051
clear_slot_flag(zram, index, ZRAM_IDLE);
20122052
clear_slot_flag(zram, index, ZRAM_INCOMPRESSIBLE);
20132053
clear_slot_flag(zram, index, ZRAM_PP_SLOT);
@@ -2784,6 +2824,19 @@ static void zram_submit_bio(struct bio *bio)
27842824
}
27852825
}
27862826

2827+
static bool try_slot_lazy_free(struct zram *zram, unsigned long index)
2828+
{
2829+
/* too many lazy-free slots, perform direct free */
2830+
if (atomic64_read(&zram->stats.gc_slots) > 30000)
2831+
return false;
2832+
if (test_and_set_bit(index, zram->gc_map))
2833+
return false;
2834+
/* accumulated lazy-free slots, wake up GC worker */
2835+
if (atomic64_inc_return(&zram->stats.gc_slots) > 200)
2836+
queue_work(system_dfl_wq, &zram->gc_work);
2837+
return true;
2838+
}
2839+
27872840
static void zram_slot_free_notify(struct block_device *bdev,
27882841
unsigned long index)
27892842
{
@@ -2797,7 +2850,8 @@ static void zram_slot_free_notify(struct block_device *bdev,
27972850
return;
27982851
}
27992852

2800-
slot_free(zram, index);
2853+
if (!try_slot_lazy_free(zram, index))
2854+
slot_free(zram, index);
28012855
slot_unlock(zram, index);
28022856
}
28032857

drivers/block/zram/zram_drv.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ struct zram_stats {
8888
atomic64_t pages_stored; /* no. of pages currently stored */
8989
atomic_long_t max_used_pages; /* no. of maximum pages stored */
9090
atomic64_t miss_free; /* no. of missed free */
91+
atomic64_t gc_slots; /* no. of queued for lazy free by gc */
9192
#ifdef CONFIG_ZRAM_WRITEBACK
9293
atomic64_t bd_count; /* no. of pages in backing device */
9394
atomic64_t bd_reads; /* no. of reads from backing device */
@@ -142,5 +143,7 @@ struct zram {
142143
#ifdef CONFIG_ZRAM_MEMORY_TRACKING
143144
struct dentry *debugfs_dir;
144145
#endif
146+
unsigned long *gc_map;
147+
struct work_struct gc_work;
145148
};
146149
#endif

0 commit comments

Comments
 (0)