From: Gao Xiang hsiangkao@linux.alibaba.com
anolis inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IB5UKT
Reference: https://gitee.com/anolis/cloud-kernel/commit/72920d6cd9da7f1cdb45f1c21025a90...
--------------------------------
ANBZ: #1666
commit dfeab2e95a75a424adf39992ac62dcb9e9517d4a upstream.
In order to support multi-layer container images, add multiple device feature to EROFS. Two ways are available to use for now:
- Devices can be mapped into 32-bit global block address space; - Device ID can be specified with the chunk indexes format.
Note that it assumes no extent would cross device boundary and mkfs should take care of it seriously.
In the future, a dedicated device manager could be introduced then thus extra devices can be automatically scanned by UUID as well.
Link: https://lore.kernel.org/r/20211014081010.43485-1-hsiangkao@linux.alibaba.com Reviewed-by: Chao Yu chao@kernel.org Reviewed-by: Liu Bo bo.liu@linux.alibaba.com Signed-off-by: Gao Xiang hsiangkao@linux.alibaba.com [ Huang Jianan: remove dax and iomap related code based on 5.10. ] Signed-off-by: Huang Jianan jnhuang@linux.alibaba.com Reviewed-by: Gao Xiang hsiangkao@linux.alibaba.com Reviewed-by: Jeffle Xu jefflexu@linux.alibaba.com Signed-off-by: Zizhi Wo wozizhi@huawei.com Signed-off-by: Baokun Li libaokun1@huawei.com --- Documentation/filesystems/erofs.rst | 12 ++- fs/erofs/Kconfig | 26 +++-- fs/erofs/data.c | 68 ++++++++++--- fs/erofs/erofs_fs.h | 25 ++++- fs/erofs/internal.h | 34 ++++++- fs/erofs/super.c | 153 ++++++++++++++++++++++++++-- fs/erofs/zdata.c | 20 +++- 7 files changed, 291 insertions(+), 47 deletions(-)
diff --git a/Documentation/filesystems/erofs.rst b/Documentation/filesystems/erofs.rst index ed2d9b33a94f..693d7a32845a 100644 --- a/Documentation/filesystems/erofs.rst +++ b/Documentation/filesystems/erofs.rst @@ -19,9 +19,10 @@ It is designed as a better filesystem solution for the following scenarios: immutable and bit-for-bit identical to the official golden image for their releases due to security and other considerations and
- - hope to save some extra storage space with guaranteed end-to-end performance - by using reduced metadata and transparent file compression, especially - for those embedded devices with limited memory (ex, smartphone); + - hope to minimize extra storage space with guaranteed end-to-end performance + by using compact layout, transparent file compression and direct access, + especially for those embedded devices with limited memory and high-density + hosts with numerous containers;
Here is the main features of EROFS:
@@ -51,7 +52,9 @@ Here is the main features of EROFS: - Support POSIX.1e ACLs by using xattrs;
- Support transparent file compression as an option: - LZ4 algorithm with 4 KB fixed-sized output compression for high performance. + LZ4 algorithm with 4 KB fixed-sized output compression for high performance; + + - Multiple device support for multi-layer container images.
The following git tree provides the file system user-space tools under development (ex, formatting tool mkfs.erofs): @@ -84,6 +87,7 @@ cache_strategy=%s Select a strategy for cached decompression from now on: It still does in-place I/O decompression for the rest compressed physical clusters. ========== ============================================= +device=%s Specify a path to an extra device to be used together. =================== =========================================================
On-disk details diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig index f68447fbd246..4dc00a2320e4 100644 --- a/fs/erofs/Kconfig +++ b/fs/erofs/Kconfig @@ -5,16 +5,22 @@ config EROFS_FS depends on BLOCK select LIBCRC32C help - EROFS (Enhanced Read-Only File System) is a lightweight - read-only file system with modern designs (eg. page-sized - blocks, inline xattrs/data, etc.) for scenarios which need - high-performance read-only requirements, e.g. Android OS - for mobile phones and LIVECDs. - - It also provides fixed-sized output compression support, - which improves storage density, keeps relatively higher - compression ratios, which is more useful to achieve high - performance for embedded devices with limited memory. + EROFS (Enhanced Read-Only File System) is a lightweight read-only + file system with modern designs (e.g. no buffer heads, inline + xattrs/data, chunk-based deduplication, multiple devices, etc.) for + scenarios which need high-performance read-only solutions, e.g. + smartphones with Android OS, LiveCDs and high-density hosts with + numerous containers; + + It also provides fixed-sized output compression support in order to + improve storage density as well as keep relatively higher compression + ratios and implements in-place decompression to reuse the file page + for compressed data temporarily with proper strategies, which is + quite useful to ensure guaranteed end-to-end runtime decompression + performance under extremely memory pressure without extra cost. + + See the documentation at file:Documentation/filesystems/erofs.rst + for more details.
If unsure, say N.
diff --git a/fs/erofs/data.c b/fs/erofs/data.c index deed631dad0e..0e897cfe96ca 100644 --- a/fs/erofs/data.c +++ b/fs/erofs/data.c @@ -112,6 +112,7 @@ static int erofs_map_blocks(struct inode *inode, erofs_off_t pos; int err = 0;
+ map->m_deviceid = 0; if (map->m_la >= inode->i_size) { /* leave out-of-bound access unmapped */ map->m_flags = 0; @@ -158,14 +159,8 @@ static int erofs_map_blocks(struct inode *inode, map->m_flags = 0; break; default: - /* only one device is supported for now */ - if (idx->device_id) { - erofs_err(sb, "invalid device id %u @ %llu for nid %llu", - le16_to_cpu(idx->device_id), - chunknr, vi->nid); - err = -EFSCORRUPTED; - goto out_unlock; - } + map->m_deviceid = le16_to_cpu(idx->device_id) & + EROFS_SB(sb)->device_id_mask; map->m_pa = blknr_to_addr(le32_to_cpu(idx->blkaddr)); map->m_flags = EROFS_MAP_MAPPED; break; @@ -178,6 +173,46 @@ static int erofs_map_blocks(struct inode *inode, return err; }
+int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map) +{ + struct erofs_dev_context *devs = EROFS_SB(sb)->devs; + struct erofs_device_info *dif; + int id; + + /* primary device by default */ + map->m_bdev = sb->s_bdev; + + if (map->m_deviceid) { + down_read(&devs->rwsem); + dif = idr_find(&devs->tree, map->m_deviceid - 1); + if (!dif) { + up_read(&devs->rwsem); + return -ENODEV; + } + map->m_bdev = dif->bdev; + up_read(&devs->rwsem); + } else if (devs->extra_devices) { + down_read(&devs->rwsem); + idr_for_each_entry(&devs->tree, dif, id) { + erofs_off_t startoff, length; + + if (!dif->mapped_blkaddr) + continue; + startoff = blknr_to_addr(dif->mapped_blkaddr); + length = blknr_to_addr(dif->blocks); + + if (map->m_pa >= startoff && + map->m_pa < startoff + length) { + map->m_pa -= startoff; + map->m_bdev = dif->bdev; + break; + } + } + up_read(&devs->rwsem); + } + return 0; +} + static inline struct bio *erofs_read_raw_page(struct bio *bio, struct address_space *mapping, struct page *page, @@ -210,6 +245,7 @@ static inline struct bio *erofs_read_raw_page(struct bio *bio, struct erofs_map_blocks map = { .m_la = blknr_to_addr(current_block), }; + struct erofs_map_dev mdev; erofs_blk_t blknr; unsigned int blkoff;
@@ -217,6 +253,14 @@ static inline struct bio *erofs_read_raw_page(struct bio *bio, if (err) goto err_out;
+ mdev = (struct erofs_map_dev) { + .m_deviceid = map.m_deviceid, + .m_pa = map.m_pa, + }; + err = erofs_map_dev(sb, &mdev); + if (err) + goto err_out; + /* zero out the holed page */ if (!(map.m_flags & EROFS_MAP_MAPPED)) { zero_user_segment(page, 0, PAGE_SIZE); @@ -229,8 +273,8 @@ static inline struct bio *erofs_read_raw_page(struct bio *bio, /* for RAW access mode, m_plen must be equal to m_llen */ DBG_BUGON(map.m_plen != map.m_llen);
- blknr = erofs_blknr(map.m_pa); - blkoff = erofs_blkoff(map.m_pa); + blknr = erofs_blknr(mdev.m_pa); + blkoff = erofs_blkoff(mdev.m_pa);
/* deal with inline page */ if (map.m_flags & EROFS_MAP_META) { @@ -239,7 +283,7 @@ static inline struct bio *erofs_read_raw_page(struct bio *bio,
DBG_BUGON(map.m_plen > PAGE_SIZE);
- ipage = erofs_get_meta_page(inode->i_sb, blknr); + ipage = erofs_get_meta_page(sb, blknr);
if (IS_ERR(ipage)) { err = PTR_ERR(ipage); @@ -275,7 +319,7 @@ static inline struct bio *erofs_read_raw_page(struct bio *bio, bio = bio_alloc(GFP_NOIO, nblocks);
bio->bi_end_io = erofs_readendio; - bio_set_dev(bio, sb->s_bdev); + bio_set_dev(bio, mdev.m_bdev); bio->bi_iter.bi_sector = (sector_t)blknr << LOG_SECTORS_PER_BLOCK; bio->bi_opf = REQ_OP_READ | (ra ? REQ_RAHEAD : 0); diff --git a/fs/erofs/erofs_fs.h b/fs/erofs/erofs_fs.h index a13f17d33da8..b15a62bf2c46 100644 --- a/fs/erofs/erofs_fs.h +++ b/fs/erofs/erofs_fs.h @@ -19,9 +19,24 @@ */ #define EROFS_FEATURE_INCOMPAT_LZ4_0PADDING 0x00000001 #define EROFS_FEATURE_INCOMPAT_CHUNKED_FILE 0x00000004 +#define EROFS_FEATURE_INCOMPAT_DEVICE_TABLE 0x00000008 #define EROFS_ALL_FEATURE_INCOMPAT \ (EROFS_FEATURE_INCOMPAT_LZ4_0PADDING | \ - EROFS_FEATURE_INCOMPAT_CHUNKED_FILE) + EROFS_FEATURE_INCOMPAT_CHUNKED_FILE | \ + EROFS_FEATURE_INCOMPAT_DEVICE_TABLE) + +#define EROFS_SB_EXTSLOT_SIZE 16 + +struct erofs_deviceslot { + union { + u8 uuid[16]; /* used for device manager later */ + u8 userdata[64]; /* digest(sha256), etc. */ + } u; + __le32 blocks; /* total fs blocks of this device */ + __le32 mapped_blkaddr; /* map starting at mapped_blkaddr */ + u8 reserved[56]; +}; +#define EROFS_DEVT_SLOT_SIZE sizeof(struct erofs_deviceslot)
/* 128-byte erofs on-disk super block */ struct erofs_super_block { @@ -42,7 +57,10 @@ struct erofs_super_block { __u8 uuid[16]; /* 128-bit uuid for volume */ __u8 volume_name[16]; /* volume name */ __le32 feature_incompat; - __u8 reserved2[44]; + __le16 reserved2; + __le16 extra_devices; /* # of devices besides the primary device */ + __le16 devt_slotoff; /* startoff = devt_slotoff * devt_slotsize */ + __u8 reserved3[38]; };
/* @@ -226,7 +244,7 @@ static inline unsigned int erofs_xattr_entry_size(struct erofs_xattr_entry *e) /* 8-byte inode chunk indexes */ struct erofs_inode_chunk_index { __le16 advise; /* always 0, don't care for now */ - __le16 device_id; /* back-end storage id, always 0 for now */ + __le16 device_id; /* back-end storage id (with bits masked) */ __le32 blkaddr; /* start block address of this inode chunk */ };
@@ -354,6 +372,7 @@ static inline void erofs_check_ondisk_layout_definitions(void) /* keep in sync between 2 index structures for better extendibility */ BUILD_BUG_ON(sizeof(struct erofs_inode_chunk_index) != sizeof(struct z_erofs_vle_decompressed_index)); + BUILD_BUG_ON(sizeof(struct erofs_deviceslot) != 128);
BUILD_BUG_ON(BIT(Z_EROFS_VLE_DI_CLUSTER_TYPE_BITS) < Z_EROFS_VLE_CLUSTER_TYPE_MAX - 1); diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h index eeb7adc500c5..954a1cfb9509 100644 --- a/fs/erofs/internal.h +++ b/fs/erofs/internal.h @@ -13,6 +13,7 @@ #include <linux/pagemap.h> #include <linux/bio.h> #include <linux/buffer_head.h> +#include <linux/idr.h> #include <linux/magic.h> #include <linux/slab.h> #include <linux/vmalloc.h> @@ -46,6 +47,14 @@ typedef u64 erofs_off_t; /* data type for filesystem-wide blocks number */ typedef u32 erofs_blk_t;
+struct erofs_device_info { + char *path; + struct block_device *bdev; + + u32 blocks; + u32 mapped_blkaddr; +}; + struct erofs_mount_opts { #ifdef CONFIG_EROFS_FS_ZIP /* current strategy of how to use managed cache */ @@ -57,13 +66,20 @@ struct erofs_mount_opts { unsigned int mount_opt; };
+struct erofs_dev_context { + struct idr tree; + struct rw_semaphore rwsem; + + unsigned int extra_devices; +}; + struct erofs_fs_context { struct erofs_mount_opts opt; + struct erofs_dev_context *devs; };
struct erofs_sb_info { struct erofs_mount_opts opt; /* options */ - #ifdef CONFIG_EROFS_FS_ZIP /* list for all registered superblocks, mainly for shrinker */ struct list_head list; @@ -77,11 +93,15 @@ struct erofs_sb_info { /* pseudo inode to manage cached pages */ struct inode *managed_cache; #endif /* CONFIG_EROFS_FS_ZIP */ - u32 blocks; + struct erofs_dev_context *devs; + u64 total_blocks; + u32 primarydevice_blocks; + u32 meta_blkaddr; #ifdef CONFIG_EROFS_FS_XATTR u32 xattr_blkaddr; #endif + u16 device_id_mask; /* valid bits of device id to be used */
/* inode slot unit size in bit shift */ unsigned char islotbits; @@ -198,6 +218,7 @@ static inline bool erofs_sb_has_##name(struct erofs_sb_info *sbi) \ }
EROFS_FEATURE_FUNCS(lz4_0padding, incompat, INCOMPAT_LZ4_0PADDING) +EROFS_FEATURE_FUNCS(device_table, incompat, INCOMPAT_DEVICE_TABLE) EROFS_FEATURE_FUNCS(sb_chksum, compat, COMPAT_SB_CHKSUM)
/* atomic flag definitions */ @@ -317,6 +338,7 @@ struct erofs_map_blocks { erofs_off_t m_pa, m_la; u64 m_plen, m_llen;
+ unsigned short m_deviceid; unsigned int m_flags;
struct page *mpage; @@ -341,8 +363,16 @@ static inline int z_erofs_map_blocks_iter(struct inode *inode, } #endif /* !CONFIG_EROFS_FS_ZIP */
+struct erofs_map_dev { + struct block_device *m_bdev; + + erofs_off_t m_pa; + unsigned int m_deviceid; +}; + /* data.c */ struct page *erofs_get_meta_page(struct super_block *sb, erofs_blk_t blkaddr); +int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *dev);
/* inode.c */ static inline unsigned long erofs_inode_hash(erofs_nid_t nid) diff --git a/fs/erofs/super.c b/fs/erofs/super.c index 9747fb0d6ca2..8ba942170b65 100644 --- a/fs/erofs/super.c +++ b/fs/erofs/super.c @@ -11,6 +11,7 @@ #include <linux/crc32c.h> #include <linux/fs_context.h> #include <linux/fs_parser.h> +#include <linux/blkdev.h> #include "xattr.h"
#define CREATE_TRACE_POINTS @@ -121,6 +122,78 @@ static bool check_layout_compatibility(struct super_block *sb, return true; }
+static int erofs_init_devices(struct super_block *sb, + struct erofs_super_block *dsb) +{ + struct erofs_sb_info *sbi = EROFS_SB(sb); + unsigned int ondisk_extradevs; + erofs_off_t pos; + struct page *page = NULL; + struct erofs_device_info *dif; + struct erofs_deviceslot *dis; + void *ptr; + int id, err = 0; + + sbi->total_blocks = sbi->primarydevice_blocks; + if (!erofs_sb_has_device_table(sbi)) + ondisk_extradevs = 0; + else + ondisk_extradevs = le16_to_cpu(dsb->extra_devices); + + if (ondisk_extradevs != sbi->devs->extra_devices) { + erofs_err(sb, "extra devices don't match (ondisk %u, given %u)", + ondisk_extradevs, sbi->devs->extra_devices); + return -EINVAL; + } + if (!ondisk_extradevs) + return 0; + + sbi->device_id_mask = roundup_pow_of_two(ondisk_extradevs + 1) - 1; + pos = le16_to_cpu(dsb->devt_slotoff) * EROFS_DEVT_SLOT_SIZE; + down_read(&sbi->devs->rwsem); + idr_for_each_entry(&sbi->devs->tree, dif, id) { + erofs_blk_t blk = erofs_blknr(pos); + struct block_device *bdev; + + if (!page || page->index != blk) { + if (page) { + kunmap(page); + unlock_page(page); + put_page(page); + } + + page = erofs_get_meta_page(sb, blk); + if (IS_ERR(page)) { + up_read(&sbi->devs->rwsem); + return PTR_ERR(page); + } + ptr = kmap(page); + } + dis = ptr + erofs_blkoff(pos); + + bdev = blkdev_get_by_path(dif->path, + FMODE_READ | FMODE_EXCL, + sb->s_type); + if (IS_ERR(bdev)) { + err = PTR_ERR(bdev); + goto err_out; + } + dif->bdev = bdev; + dif->blocks = le32_to_cpu(dis->blocks); + dif->mapped_blkaddr = le32_to_cpu(dis->mapped_blkaddr); + sbi->total_blocks += dif->blocks; + pos += EROFS_DEVT_SLOT_SIZE; + } +err_out: + up_read(&sbi->devs->rwsem); + if (page) { + kunmap(page); + unlock_page(page); + put_page(page); + } + return err; +} + static int erofs_read_superblock(struct super_block *sb) { struct erofs_sb_info *sbi; @@ -166,7 +239,7 @@ static int erofs_read_superblock(struct super_block *sb) if (!check_layout_compatibility(sb, dsb)) goto out;
- sbi->blocks = le32_to_cpu(dsb->blocks); + sbi->primarydevice_blocks = le32_to_cpu(dsb->blocks); sbi->meta_blkaddr = le32_to_cpu(dsb->meta_blkaddr); #ifdef CONFIG_EROFS_FS_XATTR sbi->xattr_blkaddr = le32_to_cpu(dsb->xattr_blkaddr); @@ -187,7 +260,9 @@ static int erofs_read_superblock(struct super_block *sb) ret = -EFSCORRUPTED; goto out; } - ret = 0; + + /* handle multiple devices */ + ret = erofs_init_devices(sb, dsb); out: kunmap(page); put_page(page); @@ -213,6 +288,7 @@ enum { Opt_user_xattr, Opt_acl, Opt_cache_strategy, + Opt_device, Opt_err };
@@ -228,15 +304,17 @@ static const struct fs_parameter_spec erofs_fs_parameters[] = { fsparam_flag_no("acl", Opt_acl), fsparam_enum("cache_strategy", Opt_cache_strategy, erofs_param_cache_strategy), + fsparam_string("device", Opt_device), {} };
static int erofs_fc_parse_param(struct fs_context *fc, struct fs_parameter *param) { - struct erofs_fs_context *ctx __maybe_unused = fc->fs_private; + struct erofs_fs_context *ctx = fc->fs_private; struct fs_parse_result result; - int opt; + struct erofs_device_info *dif; + int opt, ret;
opt = fs_parse(fc, erofs_fs_parameters, param, &result); if (opt < 0) @@ -270,6 +348,25 @@ static int erofs_fc_parse_param(struct fs_context *fc, errorfc(fc, "compression not supported, cache_strategy ignored"); #endif break; + case Opt_device: + dif = kzalloc(sizeof(*dif), GFP_KERNEL); + if (!dif) + return -ENOMEM; + dif->path = kstrdup(param->string, GFP_KERNEL); + if (!dif->path) { + kfree(dif); + return -ENOMEM; + } + down_write(&ctx->devs->rwsem); + ret = idr_alloc(&ctx->devs->tree, dif, 0, 0, GFP_KERNEL); + up_write(&ctx->devs->rwsem); + if (ret < 0) { + kfree(dif->path); + kfree(dif); + return ret; + } + ++ctx->devs->extra_devices; + break; default: return -ENOPARAM; } @@ -355,6 +452,9 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_fs_info = sbi; sbi->opt = ctx->opt; + sbi->devs = ctx->devs; + ctx->devs = NULL; + err = erofs_read_superblock(sb); if (err) return err; @@ -425,9 +525,32 @@ static int erofs_fc_reconfigure(struct fs_context *fc) return 0; }
+static int erofs_release_device_info(int id, void *ptr, void *data) +{ + struct erofs_device_info *dif = ptr; + + if (dif->bdev) + blkdev_put(dif->bdev, FMODE_READ | FMODE_EXCL); + kfree(dif->path); + kfree(dif); + return 0; +} + +static void erofs_free_dev_context(struct erofs_dev_context *devs) +{ + if (!devs) + return; + idr_for_each(&devs->tree, &erofs_release_device_info, NULL); + idr_destroy(&devs->tree); + kfree(devs); +} + static void erofs_fc_free(struct fs_context *fc) { - kfree(fc->fs_private); + struct erofs_fs_context *ctx = fc->fs_private; + + erofs_free_dev_context(ctx->devs); + kfree(ctx); }
static const struct fs_context_operations erofs_context_ops = { @@ -439,13 +562,20 @@ static const struct fs_context_operations erofs_context_ops = {
static int erofs_init_fs_context(struct fs_context *fc) { - fc->fs_private = kzalloc(sizeof(struct erofs_fs_context), GFP_KERNEL); - if (!fc->fs_private) - return -ENOMEM; + struct erofs_fs_context *ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
- /* set default mount options */ - erofs_default_options(fc->fs_private); + if (!ctx) + return -ENOMEM; + ctx->devs = kzalloc(sizeof(struct erofs_dev_context), GFP_KERNEL); + if (!ctx->devs) { + kfree(ctx); + return -ENOMEM; + } + fc->fs_private = ctx;
+ idr_init(&ctx->devs->tree); + init_rwsem(&ctx->devs->rwsem); + erofs_default_options(ctx); fc->ops = &erofs_context_ops; return 0; } @@ -465,6 +595,7 @@ static void erofs_kill_sb(struct super_block *sb) sbi = EROFS_SB(sb); if (!sbi) return; + erofs_free_dev_context(sbi->devs); kfree(sbi); sb->s_fs_info = NULL; } @@ -551,7 +682,7 @@ static int erofs_statfs(struct dentry *dentry, struct kstatfs *buf)
buf->f_type = sb->s_magic; buf->f_bsize = EROFS_BLKSIZ; - buf->f_blocks = sbi->blocks; + buf->f_blocks = sbi->total_blocks; buf->f_bfree = buf->f_bavail = 0;
buf->f_files = ULLONG_MAX; diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index 17b7c8ecf9a5..eeb286935f8e 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -1162,8 +1162,9 @@ static void z_erofs_submit_queue(struct super_block *sb, struct z_erofs_decompressqueue *q[NR_JOBQUEUES]; void *bi_private; z_erofs_next_pcluster_t owned_head = f->clt.owned_head; - /* since bio will be NULL, no need to initialize last_index */ + /* bio is NULL initially, so no need to initialize last_{index,bdev} */ pgoff_t last_index; + struct block_device *last_bdev; unsigned int nr_bios = 0; struct bio *bio = NULL;
@@ -1175,6 +1176,7 @@ static void z_erofs_submit_queue(struct super_block *sb, q[JQ_SUBMIT]->head = owned_head;
do { + struct erofs_map_dev mdev; struct z_erofs_pcluster *pcl; pgoff_t cur, end; unsigned int i = 0; @@ -1186,7 +1188,13 @@ static void z_erofs_submit_queue(struct super_block *sb,
pcl = container_of(owned_head, struct z_erofs_pcluster, next);
- cur = pcl->obj.index; + /* no device id here, thus it will always succeed */ + mdev = (struct erofs_map_dev) { + .m_pa = blknr_to_addr(pcl->obj.index), + }; + (void)erofs_map_dev(sb, &mdev); + + cur = erofs_blknr(mdev.m_pa); end = cur + BIT(pcl->clusterbits);
/* close the main owned chain at first */ @@ -1202,7 +1210,8 @@ static void z_erofs_submit_queue(struct super_block *sb, if (!page) continue;
- if (bio && cur != last_index + 1) { + if (bio && (cur != last_index + 1 || + last_bdev != mdev.m_bdev)) { submit_bio_retry: submit_bio(bio); bio = NULL; @@ -1210,9 +1219,10 @@ static void z_erofs_submit_queue(struct super_block *sb,
if (!bio) { bio = bio_alloc(GFP_NOIO, BIO_MAX_PAGES); - bio->bi_end_io = z_erofs_decompressqueue_endio; - bio_set_dev(bio, sb->s_bdev); + + bio_set_dev(bio, mdev.m_bdev); + last_bdev = mdev.m_bdev; bio->bi_iter.bi_sector = (sector_t)cur << LOG_SECTORS_PER_BLOCK; bio->bi_private = bi_private;