Commit d9281660 authored by Chunhai Guo's avatar Chunhai Guo Committed by Gao Xiang

erofs: relaxed temporary buffers allocation on readahead

Even with inplace decompression, sometimes very few temporary buffers
may be still needed for a single decompression shot (e.g. 16 pages for
64k sliding window or 4 pages for 16k sliding window).  In low-memory
scenarios, it would be better to try to allocate with GFP_NOWAIT on
readahead first.  That can help reduce the time spent on page allocation
under durative memory pressure.

Here are detailed performance numbers under multi-app launch benchmark
workload [1] on ARM64 Android devices (8-core CPU and 8GB of memory)
running a 5.15 LTS kernel with EROFS of 4k pclusters:

+----------------------------------------------+
|      LZ4       | vanilla | patched |  diff   |
|----------------+---------+---------+---------|
|  Average (ms)  |  3364   |  2684   | -20.21% | [64k sliding window]
|----------------+---------+---------+---------|
|  Average (ms)  |  2079   |  1610   | -22.56% | [16k sliding window]
+----------------------------------------------+

The total size of system images for 4k pclusters is almost unchanged:
(64k sliding window)  9,117,044 KB
(16k sliding window)  9,113,096 KB

Therefore, in addition to switch the sliding window from 64k to 16k,
after applying this patch, it can eventually save 52.14% (3364 -> 1610)
on average with no memory reservation.  That is particularly useful for
embedded devices with limited resources.

[1] https://lore.kernel.org/r/20240109074143.4138783-1-guochunhai@vivo.comSuggested-by: default avatarGao Xiang <xiang@kernel.org>
Signed-off-by: default avatarChunhai Guo <guochunhai@vivo.com>
Signed-off-by: default avatarGao Xiang <hsiangkao@linux.alibaba.com>
Reviewed-by: default avatarYue Hu <huyue2@coolpad.com>
Link: https://lore.kernel.org/r/20240126140142.201718-1-hsiangkao@linux.alibaba.com
parent cc4b2dd9
...@@ -11,13 +11,12 @@ ...@@ -11,13 +11,12 @@
struct z_erofs_decompress_req { struct z_erofs_decompress_req {
struct super_block *sb; struct super_block *sb;
struct page **in, **out; struct page **in, **out;
unsigned short pageofs_in, pageofs_out; unsigned short pageofs_in, pageofs_out;
unsigned int inputsize, outputsize; unsigned int inputsize, outputsize;
/* indicate the algorithm will be used for decompression */ unsigned int alg; /* the algorithm for decompression */
unsigned int alg;
bool inplace_io, partial_decoding, fillgaps; bool inplace_io, partial_decoding, fillgaps;
gfp_t gfp; /* allocation flags for extra temporary buffers */
}; };
struct z_erofs_decompressor { struct z_erofs_decompressor {
......
...@@ -111,8 +111,9 @@ static int z_erofs_lz4_prepare_dstpages(struct z_erofs_lz4_decompress_ctx *ctx, ...@@ -111,8 +111,9 @@ static int z_erofs_lz4_prepare_dstpages(struct z_erofs_lz4_decompress_ctx *ctx,
victim = availables[--top]; victim = availables[--top];
get_page(victim); get_page(victim);
} else { } else {
victim = erofs_allocpage(pagepool, victim = erofs_allocpage(pagepool, rq->gfp);
GFP_KERNEL | __GFP_NOFAIL); if (!victim)
return -ENOMEM;
set_page_private(victim, Z_EROFS_SHORTLIVED_PAGE); set_page_private(victim, Z_EROFS_SHORTLIVED_PAGE);
} }
rq->out[i] = victim; rq->out[i] = victim;
......
...@@ -95,7 +95,7 @@ int z_erofs_load_deflate_config(struct super_block *sb, ...@@ -95,7 +95,7 @@ int z_erofs_load_deflate_config(struct super_block *sb,
} }
int z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq, int z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
struct page **pagepool) struct page **pgpl)
{ {
const unsigned int nrpages_out = const unsigned int nrpages_out =
PAGE_ALIGN(rq->pageofs_out + rq->outputsize) >> PAGE_SHIFT; PAGE_ALIGN(rq->pageofs_out + rq->outputsize) >> PAGE_SHIFT;
...@@ -158,8 +158,12 @@ int z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq, ...@@ -158,8 +158,12 @@ int z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
strm->z.avail_out = min_t(u32, outsz, PAGE_SIZE - pofs); strm->z.avail_out = min_t(u32, outsz, PAGE_SIZE - pofs);
outsz -= strm->z.avail_out; outsz -= strm->z.avail_out;
if (!rq->out[no]) { if (!rq->out[no]) {
rq->out[no] = erofs_allocpage(pagepool, rq->out[no] = erofs_allocpage(pgpl, rq->gfp);
GFP_KERNEL | __GFP_NOFAIL); if (!rq->out[no]) {
kout = NULL;
err = -ENOMEM;
break;
}
set_page_private(rq->out[no], set_page_private(rq->out[no],
Z_EROFS_SHORTLIVED_PAGE); Z_EROFS_SHORTLIVED_PAGE);
} }
...@@ -211,8 +215,11 @@ int z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq, ...@@ -211,8 +215,11 @@ int z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
DBG_BUGON(erofs_page_is_managed(EROFS_SB(sb), DBG_BUGON(erofs_page_is_managed(EROFS_SB(sb),
rq->in[j])); rq->in[j]));
tmppage = erofs_allocpage(pagepool, tmppage = erofs_allocpage(pgpl, rq->gfp);
GFP_KERNEL | __GFP_NOFAIL); if (!tmppage) {
err = -ENOMEM;
goto failed;
}
set_page_private(tmppage, Z_EROFS_SHORTLIVED_PAGE); set_page_private(tmppage, Z_EROFS_SHORTLIVED_PAGE);
copy_highpage(tmppage, rq->in[j]); copy_highpage(tmppage, rq->in[j]);
rq->in[j] = tmppage; rq->in[j] = tmppage;
...@@ -230,7 +237,7 @@ int z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq, ...@@ -230,7 +237,7 @@ int z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
break; break;
} }
} }
failed:
if (zlib_inflateEnd(&strm->z) != Z_OK && !err) if (zlib_inflateEnd(&strm->z) != Z_OK && !err)
err = -EIO; err = -EIO;
if (kout) if (kout)
......
...@@ -148,7 +148,7 @@ int z_erofs_load_lzma_config(struct super_block *sb, ...@@ -148,7 +148,7 @@ int z_erofs_load_lzma_config(struct super_block *sb,
} }
int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq, int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq,
struct page **pagepool) struct page **pgpl)
{ {
const unsigned int nrpages_out = const unsigned int nrpages_out =
PAGE_ALIGN(rq->pageofs_out + rq->outputsize) >> PAGE_SHIFT; PAGE_ALIGN(rq->pageofs_out + rq->outputsize) >> PAGE_SHIFT;
...@@ -215,8 +215,11 @@ int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq, ...@@ -215,8 +215,11 @@ int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq,
PAGE_SIZE - pageofs); PAGE_SIZE - pageofs);
outlen -= strm->buf.out_size; outlen -= strm->buf.out_size;
if (!rq->out[no] && rq->fillgaps) { /* deduped */ if (!rq->out[no] && rq->fillgaps) { /* deduped */
rq->out[no] = erofs_allocpage(pagepool, rq->out[no] = erofs_allocpage(pgpl, rq->gfp);
GFP_KERNEL | __GFP_NOFAIL); if (!rq->out[no]) {
err = -ENOMEM;
break;
}
set_page_private(rq->out[no], set_page_private(rq->out[no],
Z_EROFS_SHORTLIVED_PAGE); Z_EROFS_SHORTLIVED_PAGE);
} }
...@@ -258,8 +261,11 @@ int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq, ...@@ -258,8 +261,11 @@ int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq,
DBG_BUGON(erofs_page_is_managed(EROFS_SB(rq->sb), DBG_BUGON(erofs_page_is_managed(EROFS_SB(rq->sb),
rq->in[j])); rq->in[j]));
tmppage = erofs_allocpage(pagepool, tmppage = erofs_allocpage(pgpl, rq->gfp);
GFP_KERNEL | __GFP_NOFAIL); if (!tmppage) {
err = -ENOMEM;
goto failed;
}
set_page_private(tmppage, Z_EROFS_SHORTLIVED_PAGE); set_page_private(tmppage, Z_EROFS_SHORTLIVED_PAGE);
copy_highpage(tmppage, rq->in[j]); copy_highpage(tmppage, rq->in[j]);
rq->in[j] = tmppage; rq->in[j] = tmppage;
...@@ -277,6 +283,7 @@ int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq, ...@@ -277,6 +283,7 @@ int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq,
break; break;
} }
} }
failed:
if (no < nrpages_out && strm->buf.out) if (no < nrpages_out && strm->buf.out)
kunmap(rq->out[no]); kunmap(rq->out[no]);
if (ni < nrpages_in) if (ni < nrpages_in)
......
...@@ -82,6 +82,9 @@ struct z_erofs_pcluster { ...@@ -82,6 +82,9 @@ struct z_erofs_pcluster {
/* L: indicate several pageofs_outs or not */ /* L: indicate several pageofs_outs or not */
bool multibases; bool multibases;
/* L: whether extra buffer allocations are best-effort */
bool besteffort;
/* A: compressed bvecs (can be cached or inplaced pages) */ /* A: compressed bvecs (can be cached or inplaced pages) */
struct z_erofs_bvec compressed_bvecs[]; struct z_erofs_bvec compressed_bvecs[];
}; };
...@@ -960,7 +963,7 @@ static int z_erofs_read_fragment(struct super_block *sb, struct page *page, ...@@ -960,7 +963,7 @@ static int z_erofs_read_fragment(struct super_block *sb, struct page *page,
} }
static int z_erofs_do_read_page(struct z_erofs_decompress_frontend *fe, static int z_erofs_do_read_page(struct z_erofs_decompress_frontend *fe,
struct page *page) struct page *page, bool ra)
{ {
struct inode *const inode = fe->inode; struct inode *const inode = fe->inode;
struct erofs_map_blocks *const map = &fe->map; struct erofs_map_blocks *const map = &fe->map;
...@@ -1010,6 +1013,7 @@ static int z_erofs_do_read_page(struct z_erofs_decompress_frontend *fe, ...@@ -1010,6 +1013,7 @@ static int z_erofs_do_read_page(struct z_erofs_decompress_frontend *fe,
err = z_erofs_pcluster_begin(fe); err = z_erofs_pcluster_begin(fe);
if (err) if (err)
goto out; goto out;
fe->pcl->besteffort |= !ra;
} }
/* /*
...@@ -1276,6 +1280,9 @@ static int z_erofs_decompress_pcluster(struct z_erofs_decompress_backend *be, ...@@ -1276,6 +1280,9 @@ static int z_erofs_decompress_pcluster(struct z_erofs_decompress_backend *be,
.inplace_io = overlapped, .inplace_io = overlapped,
.partial_decoding = pcl->partial, .partial_decoding = pcl->partial,
.fillgaps = pcl->multibases, .fillgaps = pcl->multibases,
.gfp = pcl->besteffort ?
GFP_KERNEL | __GFP_NOFAIL :
GFP_NOWAIT | __GFP_NORETRY
}, be->pagepool); }, be->pagepool);
/* must handle all compressed pages before actual file pages */ /* must handle all compressed pages before actual file pages */
...@@ -1318,6 +1325,7 @@ static int z_erofs_decompress_pcluster(struct z_erofs_decompress_backend *be, ...@@ -1318,6 +1325,7 @@ static int z_erofs_decompress_pcluster(struct z_erofs_decompress_backend *be,
pcl->length = 0; pcl->length = 0;
pcl->partial = true; pcl->partial = true;
pcl->multibases = false; pcl->multibases = false;
pcl->besteffort = false;
pcl->bvset.nextpage = NULL; pcl->bvset.nextpage = NULL;
pcl->vcnt = 0; pcl->vcnt = 0;
...@@ -1787,7 +1795,7 @@ static void z_erofs_pcluster_readmore(struct z_erofs_decompress_frontend *f, ...@@ -1787,7 +1795,7 @@ static void z_erofs_pcluster_readmore(struct z_erofs_decompress_frontend *f,
if (PageUptodate(page)) if (PageUptodate(page))
unlock_page(page); unlock_page(page);
else else
(void)z_erofs_do_read_page(f, page); (void)z_erofs_do_read_page(f, page, !!rac);
put_page(page); put_page(page);
} }
...@@ -1808,7 +1816,7 @@ static int z_erofs_read_folio(struct file *file, struct folio *folio) ...@@ -1808,7 +1816,7 @@ static int z_erofs_read_folio(struct file *file, struct folio *folio)
f.headoffset = (erofs_off_t)folio->index << PAGE_SHIFT; f.headoffset = (erofs_off_t)folio->index << PAGE_SHIFT;
z_erofs_pcluster_readmore(&f, NULL, true); z_erofs_pcluster_readmore(&f, NULL, true);
err = z_erofs_do_read_page(&f, &folio->page); err = z_erofs_do_read_page(&f, &folio->page, false);
z_erofs_pcluster_readmore(&f, NULL, false); z_erofs_pcluster_readmore(&f, NULL, false);
z_erofs_pcluster_end(&f); z_erofs_pcluster_end(&f);
...@@ -1849,7 +1857,7 @@ static void z_erofs_readahead(struct readahead_control *rac) ...@@ -1849,7 +1857,7 @@ static void z_erofs_readahead(struct readahead_control *rac)
folio = head; folio = head;
head = folio_get_private(folio); head = folio_get_private(folio);
err = z_erofs_do_read_page(&f, &folio->page); err = z_erofs_do_read_page(&f, &folio->page, true);
if (err && err != -EINTR) if (err && err != -EINTR)
erofs_err(inode->i_sb, "readahead error at folio %lu @ nid %llu", erofs_err(inode->i_sb, "readahead error at folio %lu @ nid %llu",
folio->index, EROFS_I(inode)->nid); folio->index, EROFS_I(inode)->nid);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment