* [PATCH v2 0/3] erofs: bound file-backed mount nesting and use a bio_set for requests
@ 2026-11-03 6:29 Daejun Park via B4 Relay
2026-11-03 6:29 ` [PATCH v2 1/3] erofs: apply the stacked fs check to extra file-backed devices Daejun Park via B4 Relay
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Daejun Park via B4 Relay @ 2026-11-03 6:29 UTC (permalink / raw)
To: Gao Xiang, Chao Yu
Cc: Yue Hu, Jingbo Xu, Sandeep Dhavale, Hongbo Li, Chunhai Guo,
linux-erofs, linux-kernel, Daejun Park, Sashiko
File-backed EROFS mounts cap each request at 16 bvecs. A larger
readahead window then takes several backing reads. This series
allocates file-backed requests from a bio_set to hold up to
BIO_MAX_VECS bvecs per request.
All file-backed mounts share that bio_set mempool. Patches 1 and 2
first close two ways for file-backed mounts to nest past the stacked
fs check: extra devices, and files shared by inode_share. Patch 3 then
switches request allocation to the bio_set. Without the mm readahead
alignment patch, larger backing reads slow some buffered reads.
Tested in a QEMU guest with KASAN:
- Images and device= blobs shared by inode_share are rejected. So is
an image after a remount clears inode_share.
- Unshared files still mount.
---
Changes in v2:
- New patch 2 for inode_share, reported by Sashiko. It checks the
file, not the option, which a remount can clear.
- Patch 3 no longer says "does not fail", nor that a backing read
cannot reach the pool again in the same task.
---
Daejun Park (3):
erofs: apply the stacked fs check to extra file-backed devices
erofs: treat files shared by inode_share as stacked
erofs: allocate file-backed mount requests from a bio_set
fs/erofs/fileio.c | 47 +++++++++++++++++++++++++++++++++++------------
fs/erofs/internal.h | 4 ++++
fs/erofs/super.c | 30 +++++++++++++++++++++++++++---
3 files changed, 66 insertions(+), 15 deletions(-)
---
base-commit: 209108a5456171ab430e25b54b2dfe083455bfb6
change-id: 20261029-erofs-fileio-bioset-713570b89d89
Best regards,
--
Daejun Park <daejun7.park@samsung.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 1/3] erofs: apply the stacked fs check to extra file-backed devices
2026-11-03 6:29 [PATCH v2 0/3] erofs: bound file-backed mount nesting and use a bio_set for requests Daejun Park via B4 Relay
@ 2026-11-03 6:29 ` Daejun Park via B4 Relay
2026-11-03 6:29 ` [PATCH v2 2/3] erofs: treat files shared by inode_share as stacked Daejun Park via B4 Relay
2026-11-03 6:29 ` [PATCH v2 3/3] erofs: allocate file-backed mount requests from a bio_set Daejun Park via B4 Relay
2 siblings, 0 replies; 5+ messages in thread
From: Daejun Park via B4 Relay @ 2026-11-03 6:29 UTC (permalink / raw)
To: Gao Xiang, Chao Yu
Cc: Yue Hu, Jingbo Xu, Sandeep Dhavale, Hongbo Li, Chunhai Guo,
linux-erofs, linux-kernel, Daejun Park
From: Daejun Park <daejun7.park@samsung.com>
erofs_fc_fill_super() rejects a file-backed mount whose image is on a
file-backed EROFS or on a filesystem with s_stack_depth set. This
bounds the nesting of reads. Extra devices given by device= are not
checked. File-backed mounts can still nest through them without limit.
Apply the same check to extra devices.
Fixes: fb176750266a ("erofs: add file-backed mount support")
Reviewed-by: Gao Xiang <xiang@kernel.org>
Signed-off-by: Daejun Park <daejun7.park@samsung.com>
---
fs/erofs/super.c | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/fs/erofs/super.c b/fs/erofs/super.c
index ce05557bd9..ee01a5ef85 100644
--- a/fs/erofs/super.c
+++ b/fs/erofs/super.c
@@ -122,6 +122,14 @@ void *erofs_read_metadata(struct super_block *sb, struct erofs_buf *buf,
return buffer;
}
+/* see the comment in erofs_fc_fill_super() */
+static bool erofs_is_stacked_file(struct file *file)
+{
+ struct super_block *sb = file_inode(file)->i_sb;
+
+ return (sb->s_op == &erofs_sops && !sb->s_bdev) || sb->s_stack_depth;
+}
+
static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb,
struct erofs_device_info *dif, erofs_off_t *pos)
{
@@ -161,6 +169,11 @@ static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb,
} else if (!S_ISREG(file_inode(file)->i_mode)) {
fput(file);
return -EINVAL;
+ } else if (erofs_is_stacked_file(file)) {
+ erofs_err(sb, "%s: file-backed devices cannot be on stacked fses",
+ dif->path);
+ fput(file);
+ return -EINVAL;
}
if (!dif->dax_dev && test_opt(&sbi->opt, DAX_ALWAYS)) {
erofs_info(sb, "DAX unsupported by %s. Turning off DAX.",
@@ -701,9 +714,7 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
* It MUST change if another fs plans to support them, which
* may also require adjusting FILESYSTEM_MAX_STACK_DEPTH.
*/
- inode = file_inode(sbi->dif0.file);
- if ((inode->i_sb->s_op == &erofs_sops &&
- !inode->i_sb->s_bdev) || inode->i_sb->s_stack_depth) {
+ if (erofs_is_stacked_file(sbi->dif0.file)) {
erofs_err(sb, "file-backed mounts cannot be applied to stacked fses");
return -ENOTBLK;
}
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 2/3] erofs: treat files shared by inode_share as stacked
2026-11-03 6:29 [PATCH v2 0/3] erofs: bound file-backed mount nesting and use a bio_set for requests Daejun Park via B4 Relay
2026-11-03 6:29 ` [PATCH v2 1/3] erofs: apply the stacked fs check to extra file-backed devices Daejun Park via B4 Relay
@ 2026-11-03 6:29 ` Daejun Park via B4 Relay
2026-11-04 9:48 ` Gao Xiang
2026-11-03 6:29 ` [PATCH v2 3/3] erofs: allocate file-backed mount requests from a bio_set Daejun Park via B4 Relay
2 siblings, 1 reply; 5+ messages in thread
From: Daejun Park via B4 Relay @ 2026-11-03 6:29 UTC (permalink / raw)
To: Gao Xiang, Chao Yu
Cc: Yue Hu, Jingbo Xu, Sandeep Dhavale, Hongbo Li, Chunhai Guo,
linux-erofs, linux-kernel, Sashiko, Daejun Park
From: Daejun Park <daejun7.park@samsung.com>
A file-backed mount whose image or extra device is on a block-backed
EROFS passes erofs_is_stacked_file(). If inode_share shares that file,
erofs_real_inode() may pick an inode of a file-backed EROFS in the same
domain to read it. File-backed reads then nest in the same task.
Treat files that use erofs_ishare_fops as stacked.
Fixes: 34096ba919fd ("erofs: support unencoded inodes for page cache share")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Link: https://sashiko.dev/#/patchset/20261030-erofs-fileio-bioset-v1-1-caf47adbb3a3@samsung.com
Signed-off-by: Daejun Park <daejun7.park@samsung.com>
---
fs/erofs/super.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/fs/erofs/super.c b/fs/erofs/super.c
index ee01a5ef85..e92a7fa90a 100644
--- a/fs/erofs/super.c
+++ b/fs/erofs/super.c
@@ -125,9 +125,12 @@ void *erofs_read_metadata(struct super_block *sb, struct erofs_buf *buf,
/* see the comment in erofs_fc_fill_super() */
static bool erofs_is_stacked_file(struct file *file)
{
- struct super_block *sb = file_inode(file)->i_sb;
+ struct inode *inode = file_inode(file);
+ struct super_block *sb = inode->i_sb;
- return (sb->s_op == &erofs_sops && !sb->s_bdev) || sb->s_stack_depth;
+ return (sb->s_op == &erofs_sops && !sb->s_bdev) || sb->s_stack_depth ||
+ (IS_ENABLED(CONFIG_EROFS_FS_PAGE_CACHE_SHARE) &&
+ inode->i_fop == &erofs_ishare_fops);
}
static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb,
@@ -713,6 +716,9 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
* EROFS is the only fs supporting file-backed mounts for now.
* It MUST change if another fs plans to support them, which
* may also require adjusting FILESYSTEM_MAX_STACK_DEPTH.
+ * Files shared by inode_share may be read through a
+ * file-backed EROFS in the same domain, so treat them as
+ * stacked too.
*/
if (erofs_is_stacked_file(sbi->dif0.file)) {
erofs_err(sb, "file-backed mounts cannot be applied to stacked fses");
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 3/3] erofs: allocate file-backed mount requests from a bio_set
2026-11-03 6:29 [PATCH v2 0/3] erofs: bound file-backed mount nesting and use a bio_set for requests Daejun Park via B4 Relay
2026-11-03 6:29 ` [PATCH v2 1/3] erofs: apply the stacked fs check to extra file-backed devices Daejun Park via B4 Relay
2026-11-03 6:29 ` [PATCH v2 2/3] erofs: treat files shared by inode_share as stacked Daejun Park via B4 Relay
@ 2026-11-03 6:29 ` Daejun Park via B4 Relay
2 siblings, 0 replies; 5+ messages in thread
From: Daejun Park via B4 Relay @ 2026-11-03 6:29 UTC (permalink / raw)
To: Gao Xiang, Chao Yu
Cc: Yue Hu, Jingbo Xu, Sandeep Dhavale, Hongbo Li, Chunhai Guo,
linux-erofs, linux-kernel, Daejun Park
From: Daejun Park <daejun7.park@samsung.com>
Since commit 9f74ae8c9ac9 ("erofs: shorten bvecs[] for file-backed
mounts"), a file-backed request holds at most 16 bvecs. A 1 MiB
readahead window of order-0 folios then takes several backing reads.
Allocate requests from a bio_set, using the mempool that commit
considered. Each path submits its request before it allocates the
next one.
Signed-off-by: Daejun Park <daejun7.park@samsung.com>
---
A backing read through loop, FUSE, NFS or a fanotify listener may
allocate from this pool in another task, as fs_bio_set users do under
loop. NFS LOCALIO may allocate in the same task while the outer
request holds an element. Such a request waits for returned reserves.
Tested in a QEMU guest:
- read_ahead_kb=1024, 1 MiB read() calls on 256 files of 1 MiB:
0.119 s instead of 0.141 s on mm-new with "mm/readahead: align the
async window in page_cache_ra_order()" (median of 6). The other 15
workload and mode cases changed by -22% to +0.2%.
- The same reads on erofs dev without that mm patch: 0.170 s instead
of 0.137 s.
- failslab failing the readers' bio and biovec slab allocations: the
reserves served 48 to 499 bio allocations in each of 8 cases. All
data read back correctly.
---
fs/erofs/fileio.c | 47 +++++++++++++++++++++++++++++++++++------------
fs/erofs/internal.h | 4 ++++
fs/erofs/super.c | 7 +++++++
3 files changed, 46 insertions(+), 12 deletions(-)
diff --git a/fs/erofs/fileio.c b/fs/erofs/fileio.c
index e13293ec70..a0f745f0c1 100644
--- a/fs/erofs/fileio.c
+++ b/fs/erofs/fileio.c
@@ -7,13 +7,18 @@
#include <trace/events/erofs.h>
struct erofs_fileio_rq {
- struct bio_vec bvecs[16];
- struct bio bio;
struct kiocb iocb;
struct super_block *sb;
refcount_t ref;
+ /*
+ * must be last: the fields above are the front_pad of
+ * erofs_fileio_bioset, and inline bvecs follow the bio
+ */
+ struct bio bio;
};
+static struct bio_set erofs_fileio_bioset;
+
struct erofs_fileio {
struct erofs_map_blocks map;
struct erofs_map_dev dev;
@@ -38,9 +43,8 @@ static void erofs_fileio_ki_complete(struct kiocb *iocb, long ret)
rq->bio.bi_status = errno_to_blk_status(ret);
}
bio_endio(&rq->bio);
- bio_uninit(&rq->bio);
if (refcount_dec_and_test(&rq->ref))
- kfree(rq);
+ bio_put(&rq->bio);
}
static void erofs_fileio_rq_submit(struct erofs_fileio_rq *rq)
@@ -56,22 +60,25 @@ static void erofs_fileio_rq_submit(struct erofs_fileio_rq *rq)
if (test_opt(&EROFS_SB(rq->sb)->opt, DIRECT_IO) &&
rq->iocb.ki_filp->f_mode & FMODE_CAN_ODIRECT)
rq->iocb.ki_flags = IOCB_DIRECT;
- iov_iter_bvec(&iter, ITER_DEST, rq->bvecs, rq->bio.bi_vcnt,
+ iov_iter_bvec(&iter, ITER_DEST, rq->bio.bi_io_vec, rq->bio.bi_vcnt,
rq->bio.bi_iter.bi_size);
scoped_with_creds(rq->iocb.ki_filp->f_cred)
ret = vfs_iocb_iter_read(rq->iocb.ki_filp, &rq->iocb, &iter);
if (ret != -EIOCBQUEUED)
erofs_fileio_ki_complete(&rq->iocb, ret);
if (refcount_dec_and_test(&rq->ref))
- kfree(rq);
+ bio_put(&rq->bio);
}
static struct erofs_fileio_rq *erofs_fileio_rq_alloc(struct super_block *sb,
- struct file *file)
+ struct file *file, unsigned short nr_vecs)
{
- struct erofs_fileio_rq *rq = kzalloc_obj(*rq, GFP_KERNEL | __GFP_NOFAIL);
+ struct bio *bio = bio_alloc_bioset(NULL, nr_vecs, REQ_OP_READ,
+ GFP_NOIO, &erofs_fileio_bioset);
+ struct erofs_fileio_rq *rq =
+ container_of(bio, struct erofs_fileio_rq, bio);
- bio_init(&rq->bio, NULL, rq->bvecs, ARRAY_SIZE(rq->bvecs), REQ_OP_READ);
+ memset(rq, 0, offsetof(struct erofs_fileio_rq, bio));
rq->iocb.ki_filp = file;
rq->sb = sb;
refcount_set(&rq->ref, 2);
@@ -80,7 +87,8 @@ static struct erofs_fileio_rq *erofs_fileio_rq_alloc(struct super_block *sb,
struct bio *erofs_fileio_bio_alloc(struct erofs_map_dev *mdev)
{
- return &erofs_fileio_rq_alloc(mdev->m_sb, mdev->m_dif->file)->bio;
+ return &erofs_fileio_rq_alloc(mdev->m_sb, mdev->m_dif->file,
+ BIO_MAX_VECS)->bio;
}
void erofs_fileio_submit_bio(struct bio *bio)
@@ -108,11 +116,14 @@ static int erofs_fileio_iomap_read_folio_range(const struct iomap_iter *iter,
offset_in_folio(folio, iter->pos))) {
struct super_block *sb = iter_ctx->realinode->i_sb;
struct erofs_device_info *dif = iter->iomap.private;
+ unsigned short nr_vecs;
/* submit the existing range if there was one. */
if (ctx->read_ctx)
erofs_fileio_iomap_submit_read(iter, ctx);
- rq = erofs_fileio_rq_alloc(sb, dif->file);
+ nr_vecs = bio_max_segs(DIV_ROUND_UP(iomap_length(iter),
+ PAGE_SIZE));
+ rq = erofs_fileio_rq_alloc(sb, dif->file, nr_vecs);
rq->bio.bi_iter.bi_sector = sector;
bio_add_folio_nofail(&rq->bio, folio, plen,
offset_in_folio(folio, iter->pos));
@@ -161,10 +172,22 @@ int erofs_read_meta_folio(struct file *file, struct folio *folio)
}
}
- io.rq = erofs_fileio_rq_alloc(inode->i_sb, io.dev.m_dif->file);
+ io.rq = erofs_fileio_rq_alloc(inode->i_sb, io.dev.m_dif->file, 1);
io.rq->bio.bi_end_io = erofs_fileio_meta_endio;
io.rq->bio.bi_iter.bi_sector = pos >> 9;
bio_add_folio_nofail(&io.rq->bio, folio, len, 0);
erofs_fileio_rq_submit(io.rq);
return 0;
}
+
+int __init erofs_init_fileio(void)
+{
+ return bioset_init(&erofs_fileio_bioset, BIO_POOL_SIZE,
+ offsetof(struct erofs_fileio_rq, bio),
+ BIOSET_NEED_BVECS);
+}
+
+void erofs_exit_fileio(void)
+{
+ bioset_exit(&erofs_fileio_bioset);
+}
diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
index 924b37e06e..e6b85bde82 100644
--- a/fs/erofs/internal.h
+++ b/fs/erofs/internal.h
@@ -510,9 +510,13 @@ int z_erofs_parse_cfgs(struct super_block *sb, struct erofs_super_block *dsb);
#ifdef CONFIG_EROFS_FS_BACKED_BY_FILE
struct bio *erofs_fileio_bio_alloc(struct erofs_map_dev *mdev);
void erofs_fileio_submit_bio(struct bio *bio);
+int __init erofs_init_fileio(void);
+void erofs_exit_fileio(void);
#else
static inline struct bio *erofs_fileio_bio_alloc(struct erofs_map_dev *mdev) { return NULL; }
static inline void erofs_fileio_submit_bio(struct bio *bio) {}
+static inline int erofs_init_fileio(void) { return 0; }
+static inline void erofs_exit_fileio(void) {}
#endif
#ifdef CONFIG_EROFS_FS_PAGE_CACHE_SHARE
diff --git a/fs/erofs/super.c b/fs/erofs/super.c
index e92a7fa90a..17408aef92 100644
--- a/fs/erofs/super.c
+++ b/fs/erofs/super.c
@@ -1106,6 +1106,10 @@ static int __init erofs_module_init(void)
if (err)
goto ishare_err;
+ err = erofs_init_fileio();
+ if (err)
+ goto fileio_err;
+
err = register_filesystem(&erofs_fs_type);
if (err)
goto fs_err;
@@ -1113,6 +1117,8 @@ static int __init erofs_module_init(void)
return 0;
fs_err:
+ erofs_exit_fileio();
+fileio_err:
erofs_exit_ishare();
ishare_err:
erofs_exit_sysfs();
@@ -1128,6 +1134,7 @@ static int __init erofs_module_init(void)
static void __exit erofs_module_exit(void)
{
unregister_filesystem(&erofs_fs_type);
+ erofs_exit_fileio();
erofs_exit_ishare();
/* ensure all delayed rcu free inodes & pclusters are flushed */
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 2/3] erofs: treat files shared by inode_share as stacked
2026-11-03 6:29 ` [PATCH v2 2/3] erofs: treat files shared by inode_share as stacked Daejun Park via B4 Relay
@ 2026-11-04 9:48 ` Gao Xiang
0 siblings, 0 replies; 5+ messages in thread
From: Gao Xiang @ 2026-11-04 9:48 UTC (permalink / raw)
To: daejun7.park
Cc: Gao Xiang, Chao Yu, Yue Hu, Jingbo Xu, Sandeep Dhavale,
Hongbo Li, Chunhai Guo, linux-erofs, linux-kernel, Sashiko
On Tue, Nov 03, 2026 at 03:29:32PM +0900, Daejun Park via B4 Relay wrote:
> From: Daejun Park <daejun7.park@samsung.com>
>
> A file-backed mount whose image or extra device is on a block-backed
> EROFS passes erofs_is_stacked_file(). If inode_share shares that file,
> erofs_real_inode() may pick an inode of a file-backed EROFS in the same
> domain to read it. File-backed reads then nest in the same task.
>
> Treat files that use erofs_ishare_fops as stacked.
>
> Fixes: 34096ba919fd ("erofs: support unencoded inodes for page cache share")
> Reported-by: Sashiko <sashiko-bot@kernel.org>
> Link: https://sashiko.dev/#/patchset/20261030-erofs-fileio-bioset-v1-1-caf47adbb3a3@samsung.com
> Signed-off-by: Daejun Park <daejun7.park@samsung.com>
Reviewed-by: Gao Xiang <xiang@kernel.org>
I will address the first two patches upstream as fixes in these two weeks,
and then review the remaining one as a new feature later.
Thanks,
Gao Xiang
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-04 9:48 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-03 6:29 [PATCH v2 0/3] erofs: bound file-backed mount nesting and use a bio_set for requests Daejun Park via B4 Relay
2026-11-03 6:29 ` [PATCH v2 1/3] erofs: apply the stacked fs check to extra file-backed devices Daejun Park via B4 Relay
2026-11-03 6:29 ` [PATCH v2 2/3] erofs: treat files shared by inode_share as stacked Daejun Park via B4 Relay
2026-11-04 9:48 ` Gao Xiang
2026-11-03 6:29 ` [PATCH v2 3/3] erofs: allocate file-backed mount requests from a bio_set Daejun Park via B4 Relay
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®