From 6a5a27974771d0c446a9359b5d09172ee8c95a8b Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Sun, 2 Jan 2022 12:00:13 +0800 Subject: [PATCH] erofs: introduce meta buffer operations ANBZ: #228 commit fdf80a4793021c2f27953b3075f401a497519ba4 upstream. In order to support subpage and folio for all uncompressed files, introduce meta buffer descriptors, which can be effectively stored on stack, in place of meta page operations. This converts the uncompressed data path to meta buffers. Link: https://lore.kernel.org/r/20220102040017.51352-2-hsiangkao@linux.alibaba.com Reviewed-by: Liu Bo Reviewed-by: Chao Yu [ Gao Xiang: add anolis dax_read_one_pfn adaption. ] Signed-off-by: Gao Xiang Acked-by: Joseph Qi --- fs/erofs/data.c | 96 ++++++++++++++++++++++++++++++++++----------- fs/erofs/internal.h | 13 ++++++ 2 files changed, 87 insertions(+), 22 deletions(-) diff --git a/fs/erofs/data.c b/fs/erofs/data.c index 2b6e2ddb3fd1a7..78f749cd0393a9 100644 --- a/fs/erofs/data.c +++ b/fs/erofs/data.c @@ -33,7 +33,7 @@ static void erofs_readendio(struct bio *bio) bio_put(bio); } -struct page *erofs_get_meta_page(struct super_block *sb, erofs_blk_t blkaddr) +static struct page *erofs_read_meta_page(struct super_block *sb, pgoff_t index) { struct address_space *mapping; struct page *page; @@ -43,21 +43,77 @@ struct page *erofs_get_meta_page(struct super_block *sb, erofs_blk_t blkaddr) mapping = EROFS_SB(sb)->bootstrap->f_inode->i_mapping; nofs_flag = memalloc_nofs_save(); - page = read_cache_page(mapping, blkaddr, + page = read_cache_page(mapping, index, (filler_t *)mapping->a_ops->readpage, EROFS_SB(sb)->bootstrap); memalloc_nofs_restore(nofs_flag); } else { mapping = sb->s_bdev->bd_inode->i_mapping; - page = read_cache_page_gfp(mapping, blkaddr, + page = read_cache_page_gfp(mapping, index, mapping_gfp_constraint(mapping, ~__GFP_FS)); } + return page; +} + +struct page *erofs_get_meta_page(struct super_block *sb, erofs_blk_t blkaddr) +{ + struct page *page = erofs_read_meta_page(sb, blkaddr); + /* should already be PageUptodate */ if (!IS_ERR(page)) lock_page(page); return page; } +void erofs_unmap_metabuf(struct erofs_buf *buf) +{ + if (buf->kmap_type == EROFS_KMAP) + kunmap(buf->page); + else if (buf->kmap_type == EROFS_KMAP_ATOMIC) + kunmap_atomic(buf->base); + buf->base = NULL; + buf->kmap_type = EROFS_NO_KMAP; +} + +void erofs_put_metabuf(struct erofs_buf *buf) +{ + if (!buf->page) + return; + erofs_unmap_metabuf(buf); + put_page(buf->page); + buf->page = NULL; +} + +void *erofs_read_metabuf(struct erofs_buf *buf, struct super_block *sb, + erofs_blk_t blkaddr, enum erofs_kmap_type type) +{ + erofs_off_t offset = blknr_to_addr(blkaddr); + pgoff_t index = offset >> PAGE_SHIFT; + struct page *page = buf->page; + + if (!page || page->index != index) { + erofs_put_metabuf(buf); + page = erofs_read_meta_page(sb, index); + if (IS_ERR(page)) + return page; + /* should already be PageUptodate, no need to lock page */ + buf->page = page; + } + if (buf->kmap_type == EROFS_NO_KMAP) { + if (type == EROFS_KMAP) + buf->base = kmap(page); + else if (type == EROFS_KMAP_ATOMIC) + buf->base = kmap_atomic(page); + buf->kmap_type = type; + } else if (buf->kmap_type != type) { + DBG_BUGON(1); + return ERR_PTR(-EFAULT); + } + if (type == EROFS_NO_KMAP) + return NULL; + return buf->base + (offset & ~PAGE_MASK); +} + static int erofs_map_blocks_flatmode(struct inode *inode, struct erofs_map_blocks *map, int flags) @@ -67,7 +123,7 @@ static int erofs_map_blocks_flatmode(struct inode *inode, struct erofs_inode *vi = EROFS_I(inode); bool tailendpacking = (vi->datalayout == EROFS_INODE_FLAT_INLINE); - nblocks = DIV_ROUND_UP(inode->i_size, PAGE_SIZE); + nblocks = DIV_ROUND_UP(inode->i_size, EROFS_BLKSIZ); lastblk = nblocks - tailendpacking; /* there is no hole in flatmode */ @@ -108,10 +164,11 @@ int erofs_map_blocks(struct inode *inode, struct super_block *sb = inode->i_sb; struct erofs_inode *vi = EROFS_I(inode); struct erofs_inode_chunk_index *idx; - struct page *page; + struct erofs_buf buf = __EROFS_BUF_INITIALIZER; u64 chunknr; unsigned int unit; erofs_off_t pos; + void *kaddr; int err = 0; trace_erofs_map_blocks_enter(inode, map, flags); @@ -137,9 +194,9 @@ int erofs_map_blocks(struct inode *inode, pos = ALIGN(iloc(EROFS_SB(sb), vi->nid) + vi->inode_isize + vi->xattr_isize, unit) + unit * chunknr; - page = erofs_get_meta_page(inode->i_sb, erofs_blknr(pos)); - if (IS_ERR(page)) { - err = PTR_ERR(page); + kaddr = erofs_read_metabuf(&buf, sb, erofs_blknr(pos), EROFS_KMAP); + if (IS_ERR(kaddr)) { + err = PTR_ERR(kaddr); goto out; } map->m_la = chunknr << vi->chunkbits; @@ -148,7 +205,7 @@ int erofs_map_blocks(struct inode *inode, /* handle block map */ if (!(vi->chunkformat & EROFS_CHUNK_FORMAT_INDEXES)) { - __le32 *blkaddr = page_address(page) + erofs_blkoff(pos); + __le32 *blkaddr = kaddr + erofs_blkoff(pos); if (le32_to_cpu(*blkaddr) == EROFS_NULL_ADDR) { map->m_flags = 0; @@ -159,7 +216,7 @@ int erofs_map_blocks(struct inode *inode, goto out_unlock; } /* parse chunk indexes */ - idx = page_address(page) + erofs_blkoff(pos); + idx = kaddr + erofs_blkoff(pos); switch (le32_to_cpu(idx->blkaddr)) { case EROFS_NULL_ADDR: map->m_flags = 0; @@ -172,8 +229,7 @@ int erofs_map_blocks(struct inode *inode, break; } out_unlock: - unlock_page(page); - put_page(page); + erofs_put_metabuf(&buf); out: if (!err) map->m_llen = map->m_plen; @@ -290,18 +346,16 @@ static inline struct bio *erofs_read_raw_page(struct bio *bio, /* deal with inline page */ if (map.m_flags & EROFS_MAP_META) { void *vsrc, *vto; - struct page *ipage; + struct erofs_buf buf = __EROFS_BUF_INITIALIZER; DBG_BUGON(map.m_plen > PAGE_SIZE); - ipage = erofs_get_meta_page(sb, blknr); - - if (IS_ERR(ipage)) { - err = PTR_ERR(ipage); + vsrc = erofs_read_metabuf(&buf, inode->i_sb, + blknr, EROFS_KMAP_ATOMIC); + if (IS_ERR(vsrc)) { + err = PTR_ERR(vsrc); goto err_out; } - - vsrc = kmap_atomic(ipage); vto = kmap_atomic(page); memcpy(vto, vsrc + blkoff, map.m_plen); memset(vto + map.m_plen, 0, PAGE_SIZE - map.m_plen); @@ -310,10 +364,8 @@ static inline struct bio *erofs_read_raw_page(struct bio *bio, flush_dcache_page(page); SetPageUptodate(page); - /* TODO: could we unlock the page earlier? */ - unlock_page(ipage); - put_page(ipage); + erofs_put_metabuf(&buf); /* imply err = 0, see erofs_map_blocks */ goto has_updated; } diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h index cdf976ce23af18..5594fc8d5b2870 100644 --- a/fs/erofs/internal.h +++ b/fs/erofs/internal.h @@ -225,6 +225,19 @@ static inline int erofs_wait_on_workgroup_freezed(struct erofs_workgroup *grp) #error erofs cannot be used in this platform #endif +enum erofs_kmap_type { + EROFS_NO_KMAP, /* don't map the buffer */ + EROFS_KMAP, /* use kmap() to map the buffer */ + EROFS_KMAP_ATOMIC, /* use kmap_atomic() to map the buffer */ +}; + +struct erofs_buf { + struct page *page; + void *base; + enum erofs_kmap_type kmap_type; +}; +#define __EROFS_BUF_INITIALIZER ((struct erofs_buf){ .page = NULL }) + #define ROOT_NID(sb) ((sb)->root_nid) #define erofs_blknr(addr) ((addr) / EROFS_BLKSIZ)