Skip to content

Commit 0f6273a

Browse files
Chunhai Guohsiangkao
authored andcommitted
erofs: add a reserved buffer pool for lz4 decompression
This adds a special global buffer pool (in the end) for reserved pages. Using a reserved pool for LZ4 decompression significantly reduces the time spent on extra temporary page allocation for the extreme cases in low memory scenarios. The table below shows the reduction in time spent on page allocation for LZ4 decompression when using a reserved pool. The results were obtained from multi-app launch benchmarks on ARM64 Android devices running the 5.15 kernel with an 8-core CPU and 8GB of memory. In the benchmark, we launched 16 frequently-used apps, and the camera app was the last one in each round. The data in the table is the average time of camera app for each round. After using the reserved pool, there was an average improvement of 150ms in the overall launch time of our camera app, which was obtained from the systrace log. +--------------+---------------+--------------+---------+ | | w/o page pool | w/ page pool | diff | +--------------+---------------+--------------+---------+ | Average (ms) | 3434 | 21 | -99.38% | +--------------+---------------+--------------+---------+ Based on the benchmark logs, 64 pages are sufficient for 95% of scenarios. This value can be adjusted with a module parameter `reserved_pages`. The default value is 0. This pool is currently only used for the LZ4 decompressor, but it can be applied to more decompressors if needed. Signed-off-by: Chunhai Guo <[email protected]> Reviewed-by: Gao Xiang <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Gao Xiang <[email protected]>
1 parent d6db47e commit 0f6273a

File tree

3 files changed

+52
-17
lines changed

3 files changed

+52
-17
lines changed

fs/erofs/decompressor.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ static int z_erofs_lz4_prepare_dstpages(struct z_erofs_lz4_decompress_ctx *ctx,
111111
victim = availables[--top];
112112
get_page(victim);
113113
} else {
114-
victim = erofs_allocpage(pagepool, rq->gfp);
114+
victim = __erofs_allocpage(pagepool, rq->gfp, true);
115115
if (!victim)
116116
return -ENOMEM;
117117
set_page_private(victim, Z_EROFS_SHORTLIVED_PAGE);

fs/erofs/internal.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,11 @@ void erofs_unregister_sysfs(struct super_block *sb);
438438
int __init erofs_init_sysfs(void);
439439
void erofs_exit_sysfs(void);
440440

441-
struct page *erofs_allocpage(struct page **pagepool, gfp_t gfp);
441+
struct page *__erofs_allocpage(struct page **pagepool, gfp_t gfp, bool tryrsv);
442+
static inline struct page *erofs_allocpage(struct page **pagepool, gfp_t gfp)
443+
{
444+
return __erofs_allocpage(pagepool, gfp, false);
445+
}
442446
static inline void erofs_pagepool_add(struct page **pagepool, struct page *page)
443447
{
444448
set_page_private(page, (unsigned long)*pagepool);

fs/erofs/zutil.c

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ struct z_erofs_gbuf {
1212
unsigned int nrpages;
1313
};
1414

15-
static struct z_erofs_gbuf *z_erofs_gbufpool;
16-
static unsigned int z_erofs_gbuf_count, z_erofs_gbuf_nrpages;
15+
static struct z_erofs_gbuf *z_erofs_gbufpool, *z_erofs_rsvbuf;
16+
static unsigned int z_erofs_gbuf_count, z_erofs_gbuf_nrpages,
17+
z_erofs_rsv_nrpages;
1718

1819
module_param_named(global_buffers, z_erofs_gbuf_count, uint, 0444);
20+
module_param_named(reserved_pages, z_erofs_rsv_nrpages, uint, 0444);
1921

2022
static atomic_long_t erofs_global_shrink_cnt; /* for all mounted instances */
2123
/* protected by 'erofs_sb_list_lock' */
@@ -116,19 +118,30 @@ int z_erofs_gbuf_growsize(unsigned int nrpages)
116118

117119
int __init z_erofs_gbuf_init(void)
118120
{
119-
unsigned int i = num_possible_cpus();
121+
unsigned int i, total = num_possible_cpus();
120122

121-
if (!z_erofs_gbuf_count)
122-
z_erofs_gbuf_count = i;
123-
else
124-
z_erofs_gbuf_count = min(z_erofs_gbuf_count, i);
123+
if (z_erofs_gbuf_count)
124+
total = min(z_erofs_gbuf_count, total);
125+
z_erofs_gbuf_count = total;
125126

126-
z_erofs_gbufpool = kcalloc(z_erofs_gbuf_count,
127-
sizeof(*z_erofs_gbufpool), GFP_KERNEL);
127+
/* The last (special) global buffer is the reserved buffer */
128+
total += !!z_erofs_rsv_nrpages;
129+
130+
z_erofs_gbufpool = kcalloc(total, sizeof(*z_erofs_gbufpool),
131+
GFP_KERNEL);
128132
if (!z_erofs_gbufpool)
129133
return -ENOMEM;
130134

131-
for (i = 0; i < z_erofs_gbuf_count; ++i)
135+
if (z_erofs_rsv_nrpages) {
136+
z_erofs_rsvbuf = &z_erofs_gbufpool[total - 1];
137+
z_erofs_rsvbuf->pages = kcalloc(z_erofs_rsv_nrpages,
138+
sizeof(*z_erofs_rsvbuf->pages), GFP_KERNEL);
139+
if (!z_erofs_rsvbuf->pages) {
140+
z_erofs_rsvbuf = NULL;
141+
z_erofs_rsv_nrpages = 0;
142+
}
143+
}
144+
for (i = 0; i < total; ++i)
132145
spin_lock_init(&z_erofs_gbufpool[i].lock);
133146
return 0;
134147
}
@@ -137,7 +150,7 @@ void z_erofs_gbuf_exit(void)
137150
{
138151
int i;
139152

140-
for (i = 0; i < z_erofs_gbuf_count; ++i) {
153+
for (i = 0; i < z_erofs_gbuf_count + (!!z_erofs_rsvbuf); ++i) {
141154
struct z_erofs_gbuf *gbuf = &z_erofs_gbufpool[i];
142155

143156
if (gbuf->ptr) {
@@ -157,16 +170,22 @@ void z_erofs_gbuf_exit(void)
157170
kfree(z_erofs_gbufpool);
158171
}
159172

160-
struct page *erofs_allocpage(struct page **pagepool, gfp_t gfp)
173+
struct page *__erofs_allocpage(struct page **pagepool, gfp_t gfp, bool tryrsv)
161174
{
162175
struct page *page = *pagepool;
163176

164177
if (page) {
165-
DBG_BUGON(page_ref_count(page) != 1);
166178
*pagepool = (struct page *)page_private(page);
167-
return page;
179+
} else if (tryrsv && z_erofs_rsvbuf && z_erofs_rsvbuf->nrpages) {
180+
spin_lock(&z_erofs_rsvbuf->lock);
181+
if (z_erofs_rsvbuf->nrpages)
182+
page = z_erofs_rsvbuf->pages[--z_erofs_rsvbuf->nrpages];
183+
spin_unlock(&z_erofs_rsvbuf->lock);
168184
}
169-
return alloc_page(gfp);
185+
if (!page)
186+
page = alloc_page(gfp);
187+
DBG_BUGON(page && page_ref_count(page) != 1);
188+
return page;
170189
}
171190

172191
void erofs_release_pages(struct page **pagepool)
@@ -175,6 +194,18 @@ void erofs_release_pages(struct page **pagepool)
175194
struct page *page = *pagepool;
176195

177196
*pagepool = (struct page *)page_private(page);
197+
/* try to fill reserved global pool first */
198+
if (z_erofs_rsvbuf && z_erofs_rsvbuf->nrpages <
199+
z_erofs_rsv_nrpages) {
200+
spin_lock(&z_erofs_rsvbuf->lock);
201+
if (z_erofs_rsvbuf->nrpages < z_erofs_rsv_nrpages) {
202+
z_erofs_rsvbuf->pages[z_erofs_rsvbuf->nrpages++]
203+
= page;
204+
spin_unlock(&z_erofs_rsvbuf->lock);
205+
continue;
206+
}
207+
spin_unlock(&z_erofs_rsvbuf->lock);
208+
}
178209
put_page(page);
179210
}
180211
}

0 commit comments

Comments
 (0)