Skip to content

Commit

Permalink
Merge tag 'erofs-for-6.8-rc1-2' of git://git.kernel.org/pub/scm/linux…
Browse files Browse the repository at this point in the history
…/kernel/git/xiang/erofs

Pull erofs updates from Gao Xiang:
 "In this cycle, we'd like to enable basic sub-page compressed data
  support for Android ecosystem (for vendors to try out 16k page size
  with 4k-block images in their compatibility mode) as well as container
  images (so that 4k-block images can be parsed on arm64 cloud servers
  using 64k page size.)

  In addition, there are several bugfixes and cleanups as usual. All
  commits have been in -next for a while and no potential merge conflict
  is observed.

  Summary:

   - Add basic sub-page compressed data support

   - Fix a memory leak on MicroLZMA and DEFLATE compression

   - Fix a rare LZ4 inplace decompression issue on recent x86 CPUs

   - Fix a KASAN issue reported by syzbot around crafted images

   - Some cleanups"

* tag 'erofs-for-6.8-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs:
  erofs: make erofs_{err,info}() support NULL sb parameter
  erofs: avoid debugging output for (de)compressed data
  erofs: allow partially filled compressed bvecs
  erofs: enable sub-page compressed block support
  erofs: refine z_erofs_transform_plain() for sub-page block support
  erofs: fix ztailpacking for subpage compressed blocks
  erofs: fix up compacted indexes for block size < 4096
  erofs: record `pclustersize` in bytes instead of pages
  erofs: support I/O submission for sub-page compressed blocks
  erofs: fix lz4 inplace decompression
  erofs: fix memory leak on short-lived bounced pages
  • Loading branch information
Linus Torvalds committed Jan 10, 2024
2 parents 17b9e38 + aa12a79 commit 0507d25
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 226 deletions.
120 changes: 65 additions & 55 deletions fs/erofs/decompressor.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,11 @@ static int z_erofs_lz4_prepare_dstpages(struct z_erofs_lz4_decompress_ctx *ctx,
}

static void *z_erofs_lz4_handle_overlap(struct z_erofs_lz4_decompress_ctx *ctx,
void *inpage, unsigned int *inputmargin, int *maptype,
bool may_inplace)
void *inpage, void *out, unsigned int *inputmargin,
int *maptype, bool may_inplace)
{
struct z_erofs_decompress_req *rq = ctx->rq;
unsigned int omargin, total, i, j;
unsigned int omargin, total, i;
struct page **in;
void *src, *tmp;

Expand All @@ -135,20 +135,20 @@ static void *z_erofs_lz4_handle_overlap(struct z_erofs_lz4_decompress_ctx *ctx,
omargin < LZ4_DECOMPRESS_INPLACE_MARGIN(rq->inputsize))
goto docopy;

for (i = 0; i < ctx->inpages; ++i) {
DBG_BUGON(rq->in[i] == NULL);
for (j = 0; j < ctx->outpages - ctx->inpages + i; ++j)
if (rq->out[j] == rq->in[i])
goto docopy;
}
for (i = 0; i < ctx->inpages; ++i)
if (rq->out[ctx->outpages - ctx->inpages + i] !=
rq->in[i])
goto docopy;
kunmap_local(inpage);
*maptype = 3;
return out + ((ctx->outpages - ctx->inpages) << PAGE_SHIFT);
}

if (ctx->inpages <= 1) {
*maptype = 0;
return inpage;
}
kunmap_local(inpage);
might_sleep();
src = erofs_vm_map_ram(rq->in, ctx->inpages);
if (!src)
return ERR_PTR(-ENOMEM);
Expand Down Expand Up @@ -204,12 +204,12 @@ int z_erofs_fixup_insize(struct z_erofs_decompress_req *rq, const char *padbuf,
}

static int z_erofs_lz4_decompress_mem(struct z_erofs_lz4_decompress_ctx *ctx,
u8 *out)
u8 *dst)
{
struct z_erofs_decompress_req *rq = ctx->rq;
bool support_0padding = false, may_inplace = false;
unsigned int inputmargin;
u8 *headpage, *src;
u8 *out, *headpage, *src;
int ret, maptype;

DBG_BUGON(*rq->in == NULL);
Expand All @@ -230,11 +230,12 @@ static int z_erofs_lz4_decompress_mem(struct z_erofs_lz4_decompress_ctx *ctx,
}

inputmargin = rq->pageofs_in;
src = z_erofs_lz4_handle_overlap(ctx, headpage, &inputmargin,
src = z_erofs_lz4_handle_overlap(ctx, headpage, dst, &inputmargin,
&maptype, may_inplace);
if (IS_ERR(src))
return PTR_ERR(src);

out = dst + rq->pageofs_out;
/* legacy format could compress extra data in a pcluster. */
if (rq->partial_decoding || !support_0padding)
ret = LZ4_decompress_safe_partial(src + inputmargin, out,
Expand All @@ -246,15 +247,9 @@ static int z_erofs_lz4_decompress_mem(struct z_erofs_lz4_decompress_ctx *ctx,
if (ret != rq->outputsize) {
erofs_err(rq->sb, "failed to decompress %d in[%u, %u] out[%u]",
ret, rq->inputsize, inputmargin, rq->outputsize);

print_hex_dump(KERN_DEBUG, "[ in]: ", DUMP_PREFIX_OFFSET,
16, 1, src + inputmargin, rq->inputsize, true);
print_hex_dump(KERN_DEBUG, "[out]: ", DUMP_PREFIX_OFFSET,
16, 1, out, rq->outputsize, true);

if (ret >= 0)
memset(out + ret, 0, rq->outputsize - ret);
ret = -EIO;
ret = -EFSCORRUPTED;
} else {
ret = 0;
}
Expand All @@ -265,7 +260,7 @@ static int z_erofs_lz4_decompress_mem(struct z_erofs_lz4_decompress_ctx *ctx,
vm_unmap_ram(src, ctx->inpages);
} else if (maptype == 2) {
erofs_put_pcpubuf(src);
} else {
} else if (maptype != 3) {
DBG_BUGON(1);
return -EFAULT;
}
Expand Down Expand Up @@ -308,7 +303,7 @@ static int z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq,
}

dstmap_out:
ret = z_erofs_lz4_decompress_mem(&ctx, dst + rq->pageofs_out);
ret = z_erofs_lz4_decompress_mem(&ctx, dst);
if (!dst_maptype)
kunmap_local(dst);
else if (dst_maptype == 2)
Expand All @@ -319,43 +314,58 @@ static int z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq,
static int z_erofs_transform_plain(struct z_erofs_decompress_req *rq,
struct page **pagepool)
{
const unsigned int inpages = PAGE_ALIGN(rq->inputsize) >> PAGE_SHIFT;
const unsigned int outpages =
const unsigned int nrpages_in =
PAGE_ALIGN(rq->pageofs_in + rq->inputsize) >> PAGE_SHIFT;
const unsigned int nrpages_out =
PAGE_ALIGN(rq->pageofs_out + rq->outputsize) >> PAGE_SHIFT;
const unsigned int righthalf = min_t(unsigned int, rq->outputsize,
PAGE_SIZE - rq->pageofs_out);
const unsigned int lefthalf = rq->outputsize - righthalf;
const unsigned int interlaced_offset =
rq->alg == Z_EROFS_COMPRESSION_SHIFTED ? 0 : rq->pageofs_out;
u8 *src;

if (outpages > 2 && rq->alg == Z_EROFS_COMPRESSION_SHIFTED) {
DBG_BUGON(1);
return -EFSCORRUPTED;
}

if (rq->out[0] == *rq->in) {
DBG_BUGON(rq->pageofs_out);
return 0;
const unsigned int bs = rq->sb->s_blocksize;
unsigned int cur = 0, ni = 0, no, pi, po, insz, cnt;
u8 *kin;

DBG_BUGON(rq->outputsize > rq->inputsize);
if (rq->alg == Z_EROFS_COMPRESSION_INTERLACED) {
cur = bs - (rq->pageofs_out & (bs - 1));
pi = (rq->pageofs_in + rq->inputsize - cur) & ~PAGE_MASK;
cur = min(cur, rq->outputsize);
if (cur && rq->out[0]) {
kin = kmap_local_page(rq->in[nrpages_in - 1]);
if (rq->out[0] == rq->in[nrpages_in - 1]) {
memmove(kin + rq->pageofs_out, kin + pi, cur);
flush_dcache_page(rq->out[0]);
} else {
memcpy_to_page(rq->out[0], rq->pageofs_out,
kin + pi, cur);
}
kunmap_local(kin);
}
rq->outputsize -= cur;
}

src = kmap_local_page(rq->in[inpages - 1]) + rq->pageofs_in;
if (rq->out[0])
memcpy_to_page(rq->out[0], rq->pageofs_out,
src + interlaced_offset, righthalf);

if (outpages > inpages) {
DBG_BUGON(!rq->out[outpages - 1]);
if (rq->out[outpages - 1] != rq->in[inpages - 1]) {
memcpy_to_page(rq->out[outpages - 1], 0, src +
(interlaced_offset ? 0 : righthalf),
lefthalf);
} else if (!interlaced_offset) {
memmove(src, src + righthalf, lefthalf);
flush_dcache_page(rq->in[inpages - 1]);
}
for (; rq->outputsize; rq->pageofs_in = 0, cur += PAGE_SIZE, ni++) {
insz = min(PAGE_SIZE - rq->pageofs_in, rq->outputsize);
rq->outputsize -= insz;
if (!rq->in[ni])
continue;
kin = kmap_local_page(rq->in[ni]);
pi = 0;
do {
no = (rq->pageofs_out + cur + pi) >> PAGE_SHIFT;
po = (rq->pageofs_out + cur + pi) & ~PAGE_MASK;
DBG_BUGON(no >= nrpages_out);
cnt = min(insz - pi, PAGE_SIZE - po);
if (rq->out[no] == rq->in[ni]) {
memmove(kin + po,
kin + rq->pageofs_in + pi, cnt);
flush_dcache_page(rq->out[no]);
} else if (rq->out[no]) {
memcpy_to_page(rq->out[no], po,
kin + rq->pageofs_in + pi, cnt);
}
pi += cnt;
} while (pi < insz);
kunmap_local(kin);
}
kunmap_local(src);
DBG_BUGON(ni > nrpages_in);
return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion fs/erofs/decompressor_deflate.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ int __init z_erofs_deflate_init(void)
return 0;

out_failed:
pr_err("failed to allocate zlib workspace\n");
erofs_err(NULL, "failed to allocate zlib workspace");
z_erofs_deflate_exit();
return -ENOMEM;
}
Expand Down
6 changes: 4 additions & 2 deletions fs/erofs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,10 @@ static int erofs_fill_inode(struct inode *inode)

if (erofs_inode_is_data_compressed(vi->datalayout)) {
#ifdef CONFIG_EROFS_FS_ZIP
if (!erofs_is_fscache_mode(inode->i_sb) &&
inode->i_sb->s_blocksize_bits == PAGE_SHIFT) {
if (!erofs_is_fscache_mode(inode->i_sb)) {
DO_ONCE_LITE_IF(inode->i_sb->s_blocksize != PAGE_SIZE,
erofs_info, inode->i_sb,
"EXPERIMENTAL EROFS subpage compressed block support in use. Use at your own risk!");
inode->i_mapping->a_ops = &z_erofs_aops;
err = 0;
goto out_unlock;
Expand Down
10 changes: 8 additions & 2 deletions fs/erofs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ void _erofs_err(struct super_block *sb, const char *func, const char *fmt, ...)
vaf.fmt = fmt;
vaf.va = &args;

pr_err("(device %s): %s: %pV", sb->s_id, func, &vaf);
if (sb)
pr_err("(device %s): %s: %pV", sb->s_id, func, &vaf);
else
pr_err("%s: %pV", func, &vaf);
va_end(args);
}

Expand All @@ -41,7 +44,10 @@ void _erofs_info(struct super_block *sb, const char *func, const char *fmt, ...)
vaf.fmt = fmt;
vaf.va = &args;

pr_info("(device %s): %pV", sb->s_id, &vaf);
if (sb)
pr_info("(device %s): %pV", sb->s_id, &vaf);
else
pr_info("%pV", &vaf);
va_end(args);
}

Expand Down
Loading

0 comments on commit 0507d25

Please sign in to comment.