mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [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®