Skip to content
Permalink
Browse files
erofs: support tail-packing inline compressed data
Currently, we have already support tail-packing inline for
uncompressed file, let's also support it for compressed file to
decrease tail extent I/O and save more space.

Signed-off-by: Yue Hu <huyue2@yulong.com>
  • Loading branch information
Yue Hu authored and intel-lab-lkp committed Dec 15, 2021
1 parent 469407a commit 669be7ff96dfecf5377f5717882b8e958eb16f0b
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 65 deletions.
@@ -12,7 +12,7 @@ struct z_erofs_decompress_req {
struct super_block *sb;
struct page **in, **out;

unsigned short pageofs_out;
unsigned short pageofs_in, pageofs_out;
unsigned int inputsize, outputsize;

/* indicate the algorithm will be used for decompression */
@@ -186,19 +186,18 @@ static int z_erofs_lz4_decompress_mem(struct z_erofs_decompress_req *rq,
u8 *out)
{
unsigned int inputmargin;
u8 *headpage, *src;
u8 *headpage, *src, *in;
bool support_0padding;
int ret, maptype;

DBG_BUGON(*rq->in == NULL);
headpage = kmap_atomic(*rq->in);
inputmargin = 0;
support_0padding = false;
support_0padding = erofs_sb_has_zero_padding(EROFS_SB(rq->sb)) ? true :
false;

/* decompression inplace is only safe when zero_padding is enabled */
if (erofs_sb_has_zero_padding(EROFS_SB(rq->sb))) {
support_0padding = true;

if (rq->inputsize >= PAGE_SIZE && support_0padding) {
while (!headpage[inputmargin & ~PAGE_MASK])
if (!(++inputmargin & ~PAGE_MASK))
break;
@@ -215,20 +214,22 @@ static int z_erofs_lz4_decompress_mem(struct z_erofs_decompress_req *rq,
if (IS_ERR(src))
return PTR_ERR(src);

in = src + rq->pageofs_in + inputmargin;

/* legacy format could compress extra data in a pcluster. */
if (rq->partial_decoding || !support_0padding)
ret = LZ4_decompress_safe_partial(src + inputmargin, out,
rq->inputsize, rq->outputsize, rq->outputsize);
ret = LZ4_decompress_safe_partial(in, out, rq->inputsize,
rq->outputsize, rq->outputsize);
else
ret = LZ4_decompress_safe(src + inputmargin, out,
rq->inputsize, rq->outputsize);
ret = LZ4_decompress_safe(in, out, rq->inputsize,
rq->outputsize);

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);
16, 1, in, rq->inputsize, true);
print_hex_dump(KERN_DEBUG, "[out]: ", DUMP_PREFIX_OFFSET,
16, 1, out, rq->outputsize, true);

@@ -299,7 +300,7 @@ static int z_erofs_shifted_transform(struct z_erofs_decompress_req *rq,
{
const unsigned int nrpages_out =
PAGE_ALIGN(rq->pageofs_out + rq->outputsize) >> PAGE_SHIFT;
const unsigned int righthalf = PAGE_SIZE - rq->pageofs_out;
unsigned int righthalf = PAGE_SIZE - rq->pageofs_out;
unsigned char *src, *dst;

if (nrpages_out > 2) {
@@ -312,20 +313,25 @@ static int z_erofs_shifted_transform(struct z_erofs_decompress_req *rq,
return 0;
}

if (nrpages_out == 1 && rq->outputsize < righthalf)
righthalf = rq->outputsize;

src = kmap_atomic(*rq->in);
if (rq->out[0]) {
dst = kmap_atomic(rq->out[0]);
memcpy(dst + rq->pageofs_out, src, righthalf);
memcpy(dst + rq->pageofs_out, src + rq->pageofs_in, righthalf);
kunmap_atomic(dst);
}

if (nrpages_out == 2) {
DBG_BUGON(!rq->out[1]);
if (rq->out[1] == *rq->in) {
memmove(src, src + righthalf, rq->pageofs_out);
memmove(src, src + rq->pageofs_in + righthalf,
rq->pageofs_out);
} else {
dst = kmap_atomic(rq->out[1]);
memcpy(dst, src + righthalf, rq->pageofs_out);
memcpy(dst, src + rq->pageofs_in + righthalf,
rq->pageofs_out);
kunmap_atomic(dst);
}
}
@@ -23,13 +23,15 @@
#define EROFS_FEATURE_INCOMPAT_CHUNKED_FILE 0x00000004
#define EROFS_FEATURE_INCOMPAT_DEVICE_TABLE 0x00000008
#define EROFS_FEATURE_INCOMPAT_COMPR_HEAD2 0x00000008
#define EROFS_FEATURE_INCOMPAT_ZTAILPACKING 0x00000010
#define EROFS_ALL_FEATURE_INCOMPAT \
(EROFS_FEATURE_INCOMPAT_ZERO_PADDING | \
EROFS_FEATURE_INCOMPAT_COMPR_CFGS | \
EROFS_FEATURE_INCOMPAT_BIG_PCLUSTER | \
EROFS_FEATURE_INCOMPAT_CHUNKED_FILE | \
EROFS_FEATURE_INCOMPAT_DEVICE_TABLE | \
EROFS_FEATURE_INCOMPAT_COMPR_HEAD2)
EROFS_FEATURE_INCOMPAT_COMPR_HEAD2 | \
EROFS_FEATURE_INCOMPAT_ZTAILPACKING)

#define EROFS_SB_EXTSLOT_SIZE 16

@@ -292,13 +294,17 @@ struct z_erofs_lzma_cfgs {
* (4B) + 2B + (4B) if compacted 2B is on.
* bit 1 : HEAD1 big pcluster (0 - off; 1 - on)
* bit 2 : HEAD2 big pcluster (0 - off; 1 - on)
* bit 3 : tailpacking inline pcluster (0 - off; 1 - on)
*/
#define Z_EROFS_ADVISE_COMPACTED_2B 0x0001
#define Z_EROFS_ADVISE_BIG_PCLUSTER_1 0x0002
#define Z_EROFS_ADVISE_BIG_PCLUSTER_2 0x0004
#define Z_EROFS_ADVISE_INLINE_PCLUSTER 0x0008

struct z_erofs_map_header {
__le32 h_reserved1;
__le16 h_reserved1;
/* record the size of tailpacking data */
__le16 h_idata_size;
__le16 h_advise;
/*
* bit 0-3 : algorithm type of head 1 (logical cluster type 01);
@@ -274,6 +274,7 @@ EROFS_FEATURE_FUNCS(big_pcluster, incompat, INCOMPAT_BIG_PCLUSTER)
EROFS_FEATURE_FUNCS(chunked_file, incompat, INCOMPAT_CHUNKED_FILE)
EROFS_FEATURE_FUNCS(device_table, incompat, INCOMPAT_DEVICE_TABLE)
EROFS_FEATURE_FUNCS(compr_head2, incompat, INCOMPAT_COMPR_HEAD2)
EROFS_FEATURE_FUNCS(ztailpacking, incompat, INCOMPAT_ZTAILPACKING)
EROFS_FEATURE_FUNCS(sb_chksum, compat, COMPAT_SB_CHKSUM)

/* atomic flag definitions */
@@ -308,6 +309,9 @@ struct erofs_inode {
unsigned short z_advise;
unsigned char z_algorithmtype[2];
unsigned char z_logical_clusterbits;
unsigned short z_idata_size;
unsigned long z_idata_headlcn;
unsigned long z_idataoff;
};
#endif /* CONFIG_EROFS_FS_ZIP */
};
@@ -421,6 +425,8 @@ struct erofs_map_blocks {
#define EROFS_GET_BLOCKS_FIEMAP 0x0002
/* Used to map the whole extent if non-negligible data is requested for LZMA */
#define EROFS_GET_BLOCKS_READMORE 0x0004
/* Used to map tail extent for tailpacking inline pcluster */
#define EROFS_GET_BLOCKS_FINDTAIL 0x0008

enum {
Z_EROFS_COMPRESSION_SHIFTED = Z_EROFS_COMPRESSION_MAX,
@@ -411,6 +411,9 @@ static int erofs_read_superblock(struct super_block *sb)

/* handle multiple devices */
ret = erofs_init_devices(sb, dsb);

if (erofs_sb_has_ztailpacking(sbi))
erofs_info(sb, "EXPERIMENTAL compression inline data feature in use. Use at your own risk!");
out:
kunmap(page);
put_page(page);
@@ -229,7 +229,8 @@ static DEFINE_MUTEX(z_pagemap_global_lock);
static void preload_compressed_pages(struct z_erofs_collector *clt,
struct address_space *mc,
enum z_erofs_cache_alloctype type,
struct page **pagepool)
struct page **pagepool,
struct page *mpage)
{
struct z_erofs_pcluster *pcl = clt->pcl;
bool standalone = true;
@@ -243,6 +244,21 @@ static void preload_compressed_pages(struct z_erofs_collector *clt,

pages = pcl->compressed_pages;
index = pcl->obj.index;

if (z_erofs_pcluster_is_inline(pcl)) {
if (mpage->index != index) {
mpage = erofs_get_meta_page(mc->host->i_sb, index);
if (IS_ERR(mpage)) {
erofs_err(mc->host->i_sb,
"failed to get meta page, err %ld",
PTR_ERR(mpage));
return;
}
}
WRITE_ONCE(pcl->compressed_pages[0], mpage);
goto out;
}

for (; index < pcl->obj.index + pcl->pclusterpages; ++index, ++pages) {
struct page *page;
compressed_page_t t;
@@ -282,6 +298,7 @@ static void preload_compressed_pages(struct z_erofs_collector *clt,
erofs_pagepool_add(pagepool, newpage);
}

out:
/*
* don't do inplace I/O if all compressed pages are available in
* managed cache since it can be moved to the bypass queue instead.
@@ -473,6 +490,8 @@ static int z_erofs_register_collection(struct z_erofs_collector *clt,
if (IS_ERR(pcl))
return PTR_ERR(pcl);

pcl->inline_size = map->m_flags & EROFS_MAP_META ? map->m_plen : 0;

atomic_set(&pcl->obj.refcount, 1);
pcl->obj.index = map->m_pa >> PAGE_SHIFT;
pcl->algorithmformat = map->m_algorithmformat;
@@ -486,6 +505,8 @@ static int z_erofs_register_collection(struct z_erofs_collector *clt,

cl = z_erofs_primarycollection(pcl);
cl->pageofs = map->m_la & ~PAGE_MASK;
cl->mpageofs = map->m_flags & EROFS_MAP_META ?
map->m_pa & ~PAGE_MASK : 0;

/*
* lock all primary followed works before visible to others
@@ -494,7 +515,10 @@ static int z_erofs_register_collection(struct z_erofs_collector *clt,
mutex_init(&cl->lock);
DBG_BUGON(!mutex_trylock(&cl->lock));

grp = erofs_insert_workgroup(inode->i_sb, &pcl->obj);
grp = &pcl->obj;
if (!(map->m_flags & EROFS_MAP_META))
grp = erofs_insert_workgroup(inode->i_sb, &pcl->obj);

if (IS_ERR(grp)) {
err = PTR_ERR(grp);
goto err_out;
@@ -523,7 +547,7 @@ static int z_erofs_collector_begin(struct z_erofs_collector *clt,
struct inode *inode,
struct erofs_map_blocks *map)
{
struct erofs_workgroup *grp;
struct erofs_workgroup *grp = NULL;
int ret;

DBG_BUGON(clt->cl);
@@ -532,12 +556,10 @@ static int z_erofs_collector_begin(struct z_erofs_collector *clt,
DBG_BUGON(clt->owned_head == Z_EROFS_PCLUSTER_NIL);
DBG_BUGON(clt->owned_head == Z_EROFS_PCLUSTER_TAIL_CLOSED);

if (!PAGE_ALIGNED(map->m_pa)) {
DBG_BUGON(1);
return -EINVAL;
}
if (!(map->m_flags & EROFS_MAP_META))
grp = erofs_find_workgroup(inode->i_sb,
map->m_pa >> PAGE_SHIFT);

grp = erofs_find_workgroup(inode->i_sb, map->m_pa >> PAGE_SHIFT);
if (grp) {
clt->pcl = container_of(grp, struct z_erofs_pcluster, obj);
} else {
@@ -688,7 +710,7 @@ static int z_erofs_do_read_page(struct z_erofs_decompress_frontend *fe,
cache_strategy = DONTALLOC;

preload_compressed_pages(clt, MNGD_MAPPING(sbi),
cache_strategy, pagepool);
cache_strategy, pagepool, map->mpage);

hitted:
/*
@@ -978,11 +1000,14 @@ static int z_erofs_decompress_pcluster(struct super_block *sb,
partial = true;
}

inputsize = pcl->pclusterpages * PAGE_SIZE;
inputsize = pcl->inline_size ? pcl->inline_size :
pcl->pclusterpages * PAGE_SIZE;

err = z_erofs_decompress(&(struct z_erofs_decompress_req) {
.sb = sb,
.in = compressed_pages,
.out = pages,
.pageofs_in = cl->mpageofs,
.pageofs_out = cl->pageofs,
.inputsize = inputsize,
.outputsize = outputsize,
@@ -993,6 +1018,14 @@ static int z_erofs_decompress_pcluster(struct super_block *sb,

out:
/* must handle all compressed pages before ending pages */
if (z_erofs_pcluster_is_inline(pcl)) {
page = compressed_pages[0];

if (PageLocked(page))
unlock_page(page);
WRITE_ONCE(page, NULL);
}

for (i = 0; i < pcl->pclusterpages; ++i) {
page = compressed_pages[i];

@@ -1288,6 +1321,13 @@ static void z_erofs_submit_queue(struct super_block *sb,

pcl = container_of(owned_head, struct z_erofs_pcluster, next);

/* close the main owned chain at first */
owned_head = cmpxchg(&pcl->next, Z_EROFS_PCLUSTER_TAIL,
Z_EROFS_PCLUSTER_TAIL_CLOSED);

if (z_erofs_pcluster_is_inline(pcl))
goto noio_submission;

/* no device id here, thus it will always succeed */
mdev = (struct erofs_map_dev) {
.m_pa = blknr_to_addr(pcl->obj.index),
@@ -1297,10 +1337,6 @@ static void z_erofs_submit_queue(struct super_block *sb,
cur = erofs_blknr(mdev.m_pa);
end = cur + pcl->pclusterpages;

/* close the main owned chain at first */
owned_head = cmpxchg(&pcl->next, Z_EROFS_PCLUSTER_TAIL,
Z_EROFS_PCLUSTER_TAIL_CLOSED);

do {
struct page *page;

@@ -1339,10 +1375,12 @@ static void z_erofs_submit_queue(struct super_block *sb,
bypass = false;
} while (++cur < end);

if (!bypass)
if (!bypass) {
qtail[JQ_SUBMIT] = &pcl->next;
else
} else {
noio_submission:
move_to_bypass_jobqueue(pcl, qtail, owned_head);
}
} while (owned_head != Z_EROFS_PCLUSTER_TAIL);

if (bio)
@@ -28,6 +28,9 @@ struct z_erofs_collection {
/* I: page offset of start position of decompression */
unsigned short pageofs;

/* I: page offset of start position of compression for inline case */
unsigned short mpageofs;

/* L: maximum relative page index in pagevec[] */
unsigned short nr_pages;

@@ -65,6 +68,9 @@ struct z_erofs_pcluster {
/* I: physical cluster size in pages */
unsigned short pclusterpages;

/* I: tailpacking inline physical cluster size */
unsigned short inline_size;

/* I: compression algorithm format */
unsigned char algorithmformat;

@@ -174,6 +180,11 @@ static inline void z_erofs_onlinepage_endio(struct page *page)
erofs_dbg("%s, page %p value %x", __func__, page, atomic_read(u.o));
}

static inline bool z_erofs_pcluster_is_inline(struct z_erofs_pcluster *pcl)
{
return !!pcl->inline_size;
}

#define Z_EROFS_VMAP_ONSTACK_PAGES \
min_t(unsigned int, THREAD_SIZE / 8 / sizeof(struct page *), 96U)
#define Z_EROFS_VMAP_GLOBAL_PAGES 2048

0 comments on commit 669be7f

Please sign in to comment.