* [PATCH v2 1/4] erofs: add file-backed mount support
@ 2024-08-30 3:28 Gao Xiang
2024-08-30 3:28 ` [PATCH v2 2/4] erofs: support unencoded inodes for fileio Gao Xiang
` (7 more replies)
0 siblings, 8 replies; 30+ messages in thread
From: Gao Xiang @ 2024-08-30 3:28 UTC (permalink / raw)
To: linux-erofs; +Cc: LKML, Gao Xiang
It actually has been around for years: For containers and other sandbox
use cases, there will be thousands (and even more) of authenticated
(sub)images running on the same host, unlike OS images.
Of course, all scenarios can use the same EROFS on-disk format, but
bdev-backed mounts just work well for OS images since golden data is
dumped into real block devices. However, it's somewhat hard for
container runtimes to manage and isolate so many unnecessary virtual
block devices safely and efficiently [1]: they just look like a burden
to orchestrators and file-backed mounts are preferred indeed. There
were already enough attempts such as Incremental FS, the original
ComposeFS and PuzzleFS acting in the same way for immutable fses. As
for current EROFS users, ComposeFS, containerd and Android APEXs will
be directly benefited from it.
On the other hand, previous experimental feature "erofs over fscache"
was once also intended to provide a similar solution (inspired by
Incremental FS discussion [2]), but the following facts show file-backed
mounts will be a better approach:
- Fscache infrastructure has recently been moved into new Netfslib
which is an unexpected dependency to EROFS really, although it
originally claims "it could be used for caching other things such as
ISO9660 filesystems too." [3]
- It takes an unexpectedly long time to upstream Fscache/Cachefiles
enhancements. For example, the failover feature took more than
one year, and the deamonless feature is still far behind now;
- Ongoing HSM "fanotify pre-content hooks" [4] together with this will
perfectly supersede "erofs over fscache" in a simpler way since
developers (mainly containerd folks) could leverage their existing
caching mechanism entirely in userspace instead of strictly following
the predefined in-kernel caching tree hierarchy.
After "fanotify pre-content hooks" lands upstream to provide the same
functionality, "erofs over fscache" will be removed then (as an EROFS
internal improvement and EROFS will not have to bother with on-demand
fetching and/or caching improvements anymore.)
[1] https://github.com/containers/storage/pull/2039
[2] https://lore.kernel.org/r/CAOQ4uxjbVxnubaPjVaGYiSwoGDTdpWbB=w_AeM6YM=zVixsUfQ@mail.gmail.com
[3] https://docs.kernel.org/filesystems/caching/fscache.html
[4] https://lore.kernel.org/r/cover.1723670362.git.josef@toxicpanda.com
Closes: https://github.com/containers/composefs/issues/144
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
---
v2:
- should use kill_anon_super();
- add O_LARGEFILE to support large files.
fs/erofs/Kconfig | 17 ++++++++++
fs/erofs/data.c | 35 ++++++++++++---------
fs/erofs/inode.c | 5 ++-
fs/erofs/internal.h | 11 +++++--
fs/erofs/super.c | 76 +++++++++++++++++++++++++++++----------------
5 files changed, 100 insertions(+), 44 deletions(-)
diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig
index 7dcdce660cac..1428d0530e1c 100644
--- a/fs/erofs/Kconfig
+++ b/fs/erofs/Kconfig
@@ -74,6 +74,23 @@ config EROFS_FS_SECURITY
If you are not using a security module, say N.
+config EROFS_FS_BACKED_BY_FILE
+ bool "File-backed EROFS filesystem support"
+ depends on EROFS_FS
+ default y
+ help
+ This allows EROFS to use filesystem image files directly, without
+ the intercession of loopback block devices or likewise. It is
+ particularly useful for container images with numerous blobs and
+ other sandboxes, where loop devices behave intricately. It can also
+ be used to simplify error-prone lifetime management of unnecessary
+ virtual block devices.
+
+ Note that this feature, along with ongoing fanotify pre-content
+ hooks, will eventually replace "EROFS over fscache."
+
+ If you don't want to enable this feature, say N.
+
config EROFS_FS_ZIP
bool "EROFS Data Compression Support"
depends on EROFS_FS
diff --git a/fs/erofs/data.c b/fs/erofs/data.c
index 1b7eba38ba1e..0fb31c588ae0 100644
--- a/fs/erofs/data.c
+++ b/fs/erofs/data.c
@@ -59,8 +59,12 @@ void *erofs_bread(struct erofs_buf *buf, erofs_off_t offset,
void erofs_init_metabuf(struct erofs_buf *buf, struct super_block *sb)
{
- if (erofs_is_fscache_mode(sb))
- buf->mapping = EROFS_SB(sb)->s_fscache->inode->i_mapping;
+ struct erofs_sb_info *sbi = EROFS_SB(sb);
+
+ if (erofs_is_fileio_mode(sbi))
+ buf->mapping = file_inode(sbi->fdev)->i_mapping;
+ else if (erofs_is_fscache_mode(sb))
+ buf->mapping = sbi->s_fscache->inode->i_mapping;
else
buf->mapping = sb->s_bdev->bd_mapping;
}
@@ -189,10 +193,22 @@ int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map)
return err;
}
+static void erofs_fill_from_devinfo(struct erofs_map_dev *map,
+ struct erofs_device_info *dif)
+{
+ map->m_bdev = NULL;
+ if (dif->file && S_ISBLK(file_inode(dif->file)->i_mode))
+ map->m_bdev = file_bdev(dif->file);
+ map->m_daxdev = dif->dax_dev;
+ map->m_dax_part_off = dif->dax_part_off;
+ map->m_fscache = dif->fscache;
+}
+
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;
+ erofs_off_t startoff, length;
int id;
map->m_bdev = sb->s_bdev;
@@ -212,29 +228,20 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)
up_read(&devs->rwsem);
return 0;
}
- map->m_bdev = dif->bdev_file ? file_bdev(dif->bdev_file) : NULL;
- map->m_daxdev = dif->dax_dev;
- map->m_dax_part_off = dif->dax_part_off;
- map->m_fscache = dif->fscache;
+ erofs_fill_from_devinfo(map, dif);
up_read(&devs->rwsem);
} else if (devs->extra_devices && !devs->flatdev) {
down_read(&devs->rwsem);
idr_for_each_entry(&devs->tree, dif, id) {
- erofs_off_t startoff, length;
-
if (!dif->mapped_blkaddr)
continue;
+
startoff = erofs_pos(sb, dif->mapped_blkaddr);
length = erofs_pos(sb, dif->blocks);
-
if (map->m_pa >= startoff &&
map->m_pa < startoff + length) {
map->m_pa -= startoff;
- map->m_bdev = dif->bdev_file ?
- file_bdev(dif->bdev_file) : NULL;
- map->m_daxdev = dif->dax_dev;
- map->m_dax_part_off = dif->dax_part_off;
- map->m_fscache = dif->fscache;
+ erofs_fill_from_devinfo(map, dif);
break;
}
}
diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c
index 419432be3223..d05b9e59f122 100644
--- a/fs/erofs/inode.c
+++ b/fs/erofs/inode.c
@@ -258,7 +258,10 @@ static int erofs_fill_inode(struct inode *inode)
}
mapping_set_large_folios(inode->i_mapping);
- if (erofs_inode_is_data_compressed(vi->datalayout)) {
+ if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb))) {
+ /* XXX: data I/Os will be implemented in the following patches */
+ err = -EOPNOTSUPP;
+ } else if (erofs_inode_is_data_compressed(vi->datalayout)) {
#ifdef CONFIG_EROFS_FS_ZIP
DO_ONCE_LITE_IF(inode->i_blkbits != PAGE_SHIFT,
erofs_info, inode->i_sb,
diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
index 45dc15ebd870..9bf4fb1cfa09 100644
--- a/fs/erofs/internal.h
+++ b/fs/erofs/internal.h
@@ -49,7 +49,7 @@ typedef u32 erofs_blk_t;
struct erofs_device_info {
char *path;
struct erofs_fscache *fscache;
- struct file *bdev_file;
+ struct file *file;
struct dax_device *dax_dev;
u64 dax_part_off;
@@ -130,6 +130,7 @@ struct erofs_sb_info {
struct erofs_sb_lz4_info lz4;
#endif /* CONFIG_EROFS_FS_ZIP */
+ struct file *fdev;
struct inode *packed_inode;
struct erofs_dev_context *devs;
struct dax_device *dax_dev;
@@ -190,9 +191,15 @@ struct erofs_sb_info {
#define set_opt(opt, option) ((opt)->mount_opt |= EROFS_MOUNT_##option)
#define test_opt(opt, option) ((opt)->mount_opt & EROFS_MOUNT_##option)
+static inline bool erofs_is_fileio_mode(struct erofs_sb_info *sbi)
+{
+ return IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) && sbi->fdev;
+}
+
static inline bool erofs_is_fscache_mode(struct super_block *sb)
{
- return IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && !sb->s_bdev;
+ return IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) &&
+ !erofs_is_fileio_mode(EROFS_SB(sb)) && !sb->s_bdev;
}
enum {
diff --git a/fs/erofs/super.c b/fs/erofs/super.c
index aae3fd15899a..9a7e67eceed4 100644
--- a/fs/erofs/super.c
+++ b/fs/erofs/super.c
@@ -10,6 +10,7 @@
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
#include <linux/exportfs.h>
+#include <linux/backing-dev.h>
#include "xattr.h"
#define CREATE_TRACE_POINTS
@@ -161,7 +162,7 @@ static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb,
struct erofs_sb_info *sbi = EROFS_SB(sb);
struct erofs_fscache *fscache;
struct erofs_deviceslot *dis;
- struct file *bdev_file;
+ struct file *file;
dis = erofs_read_metabuf(buf, sb, *pos, EROFS_KMAP);
if (IS_ERR(dis))
@@ -183,13 +184,17 @@ static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb,
return PTR_ERR(fscache);
dif->fscache = fscache;
} else if (!sbi->devs->flatdev) {
- bdev_file = bdev_file_open_by_path(dif->path, BLK_OPEN_READ,
- sb->s_type, NULL);
- if (IS_ERR(bdev_file))
- return PTR_ERR(bdev_file);
- dif->bdev_file = bdev_file;
- dif->dax_dev = fs_dax_get_by_bdev(file_bdev(bdev_file),
- &dif->dax_part_off, NULL, NULL);
+ file = erofs_is_fileio_mode(sbi) ?
+ filp_open(dif->path, O_RDONLY | O_LARGEFILE, 0) :
+ bdev_file_open_by_path(dif->path,
+ BLK_OPEN_READ, sb->s_type, NULL);
+ if (IS_ERR(file))
+ return PTR_ERR(file);
+
+ dif->file = file;
+ if (!erofs_is_fileio_mode(sbi))
+ dif->dax_dev = fs_dax_get_by_bdev(file_bdev(file),
+ &dif->dax_part_off, NULL, NULL);
}
dif->blocks = le32_to_cpu(dis->blocks);
@@ -566,15 +571,16 @@ static void erofs_set_sysfs_name(struct super_block *sb)
{
struct erofs_sb_info *sbi = EROFS_SB(sb);
- if (erofs_is_fscache_mode(sb)) {
- if (sbi->domain_id)
- super_set_sysfs_name_generic(sb, "%s,%s",sbi->domain_id,
- sbi->fsid);
- else
- super_set_sysfs_name_generic(sb, "%s", sbi->fsid);
- return;
- }
- super_set_sysfs_name_id(sb);
+ if (sbi->domain_id)
+ super_set_sysfs_name_generic(sb, "%s,%s", sbi->domain_id,
+ sbi->fsid);
+ else if (sbi->fsid)
+ super_set_sysfs_name_generic(sb, "%s", sbi->fsid);
+ else if (erofs_is_fileio_mode(sbi))
+ super_set_sysfs_name_generic(sb, "%s",
+ bdi_dev_name(sb->s_bdi));
+ else
+ super_set_sysfs_name_id(sb);
}
static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
@@ -589,14 +595,15 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_op = &erofs_sops;
sbi->blkszbits = PAGE_SHIFT;
- if (erofs_is_fscache_mode(sb)) {
+ if (!sb->s_bdev) {
sb->s_blocksize = PAGE_SIZE;
sb->s_blocksize_bits = PAGE_SHIFT;
- err = erofs_fscache_register_fs(sb);
- if (err)
- return err;
-
+ if (erofs_is_fscache_mode(sb)) {
+ err = erofs_fscache_register_fs(sb);
+ if (err)
+ return err;
+ }
err = super_setup_bdi(sb);
if (err)
return err;
@@ -693,11 +700,24 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
static int erofs_fc_get_tree(struct fs_context *fc)
{
struct erofs_sb_info *sbi = fc->s_fs_info;
+ int ret;
if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && sbi->fsid)
return get_tree_nodev(fc, erofs_fc_fill_super);
- return get_tree_bdev(fc, erofs_fc_fill_super);
+ ret = get_tree_bdev(fc, erofs_fc_fill_super);
+#ifdef CONFIG_EROFS_FS_BACKED_BY_FILE
+ if (ret == -ENOTBLK) {
+ if (!fc->source)
+ return invalf(fc, "No source specified");
+ sbi->fdev = filp_open(fc->source, O_RDONLY | O_LARGEFILE, 0);
+ if (IS_ERR(sbi->fdev))
+ return PTR_ERR(sbi->fdev);
+
+ return get_tree_nodev(fc, erofs_fc_fill_super);
+ }
+#endif
+ return ret;
}
static int erofs_fc_reconfigure(struct fs_context *fc)
@@ -727,8 +747,8 @@ static int erofs_release_device_info(int id, void *ptr, void *data)
struct erofs_device_info *dif = ptr;
fs_put_dax(dif->dax_dev, NULL);
- if (dif->bdev_file)
- fput(dif->bdev_file);
+ if (dif->file)
+ fput(dif->file);
erofs_fscache_unregister_cookie(dif->fscache);
dif->fscache = NULL;
kfree(dif->path);
@@ -791,7 +811,7 @@ static void erofs_kill_sb(struct super_block *sb)
{
struct erofs_sb_info *sbi = EROFS_SB(sb);
- if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && sbi->fsid)
+ if ((IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && sbi->fsid) || sbi->fdev)
kill_anon_super(sb);
else
kill_block_super(sb);
@@ -801,6 +821,8 @@ static void erofs_kill_sb(struct super_block *sb)
erofs_fscache_unregister_fs(sb);
kfree(sbi->fsid);
kfree(sbi->domain_id);
+ if (sbi->fdev)
+ fput(sbi->fdev);
kfree(sbi);
sb->s_fs_info = NULL;
}
@@ -903,7 +925,7 @@ static int erofs_statfs(struct dentry *dentry, struct kstatfs *buf)
buf->f_namelen = EROFS_NAME_LEN;
if (uuid_is_null(&sb->s_uuid))
- buf->f_fsid = u64_to_fsid(erofs_is_fscache_mode(sb) ? 0 :
+ buf->f_fsid = u64_to_fsid(!sb->s_bdev ? 0 :
huge_encode_dev(sb->s_bdev->bd_dev));
else
buf->f_fsid = uuid_to_fsid(sb->s_uuid.b);
--
2.43.5
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH v2 2/4] erofs: support unencoded inodes for fileio
2024-08-30 3:28 [PATCH v2 1/4] erofs: add file-backed mount support Gao Xiang
@ 2024-08-30 3:28 ` Gao Xiang
2024-08-31 0:19 ` Sandeep Dhavale
` (3 more replies)
2024-08-30 3:28 ` [PATCH v2 3/4] erofs: support compressed " Gao Xiang
` (6 subsequent siblings)
7 siblings, 4 replies; 30+ messages in thread
From: Gao Xiang @ 2024-08-30 3:28 UTC (permalink / raw)
To: linux-erofs; +Cc: LKML, Gao Xiang
Since EROFS only needs to handle read requests in simple contexts,
Just directly use vfs_iocb_iter_read() for data I/Os.
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
---
v2:
- fix redundant refcount which cause hanging on chunked inodes.
fs/erofs/Makefile | 1 +
fs/erofs/data.c | 50 +++++++++++-
fs/erofs/fileio.c | 181 ++++++++++++++++++++++++++++++++++++++++++++
fs/erofs/inode.c | 17 +++--
fs/erofs/internal.h | 7 +-
fs/erofs/zdata.c | 46 ++---------
6 files changed, 251 insertions(+), 51 deletions(-)
create mode 100644 fs/erofs/fileio.c
diff --git a/fs/erofs/Makefile b/fs/erofs/Makefile
index 097d672e6b14..4331d53c7109 100644
--- a/fs/erofs/Makefile
+++ b/fs/erofs/Makefile
@@ -7,4 +7,5 @@ erofs-$(CONFIG_EROFS_FS_ZIP) += decompressor.o zmap.o zdata.o zutil.o
erofs-$(CONFIG_EROFS_FS_ZIP_LZMA) += decompressor_lzma.o
erofs-$(CONFIG_EROFS_FS_ZIP_DEFLATE) += decompressor_deflate.o
erofs-$(CONFIG_EROFS_FS_ZIP_ZSTD) += decompressor_zstd.o
+erofs-$(CONFIG_EROFS_FS_BACKED_BY_FILE) += fileio.o
erofs-$(CONFIG_EROFS_FS_ONDEMAND) += fscache.o
diff --git a/fs/erofs/data.c b/fs/erofs/data.c
index 0fb31c588ae0..b4c07ce7a294 100644
--- a/fs/erofs/data.c
+++ b/fs/erofs/data.c
@@ -132,7 +132,7 @@ int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map)
if (map->m_la >= inode->i_size) {
/* leave out-of-bound access unmapped */
map->m_flags = 0;
- map->m_plen = 0;
+ map->m_plen = map->m_llen;
goto out;
}
@@ -197,8 +197,13 @@ static void erofs_fill_from_devinfo(struct erofs_map_dev *map,
struct erofs_device_info *dif)
{
map->m_bdev = NULL;
- if (dif->file && S_ISBLK(file_inode(dif->file)->i_mode))
- map->m_bdev = file_bdev(dif->file);
+ map->m_fp = NULL;
+ if (dif->file) {
+ if (S_ISBLK(file_inode(dif->file)->i_mode))
+ map->m_bdev = file_bdev(dif->file);
+ else
+ map->m_fp = dif->file;
+ }
map->m_daxdev = dif->dax_dev;
map->m_dax_part_off = dif->dax_part_off;
map->m_fscache = dif->fscache;
@@ -215,6 +220,7 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)
map->m_daxdev = EROFS_SB(sb)->dax_dev;
map->m_dax_part_off = EROFS_SB(sb)->dax_part_off;
map->m_fscache = EROFS_SB(sb)->s_fscache;
+ map->m_fp = EROFS_SB(sb)->fdev;
if (map->m_deviceid) {
down_read(&devs->rwsem);
@@ -250,6 +256,42 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)
return 0;
}
+/*
+ * bit 30: I/O error occurred on this folio
+ * bit 0 - 29: remaining parts to complete this folio
+ */
+#define EROFS_ONLINEFOLIO_EIO (1 << 30)
+
+void erofs_onlinefolio_init(struct folio *folio)
+{
+ union {
+ atomic_t o;
+ void *v;
+ } u = { .o = ATOMIC_INIT(1) };
+
+ folio->private = u.v; /* valid only if file-backed folio is locked */
+}
+
+void erofs_onlinefolio_split(struct folio *folio)
+{
+ atomic_inc((atomic_t *)&folio->private);
+}
+
+void erofs_onlinefolio_end(struct folio *folio, int err)
+{
+ int orig, v;
+
+ do {
+ orig = atomic_read((atomic_t *)&folio->private);
+ v = (orig - 1) | (err ? EROFS_ONLINEFOLIO_EIO : 0);
+ } while (atomic_cmpxchg((atomic_t *)&folio->private, orig, v) != orig);
+
+ if (v & ~EROFS_ONLINEFOLIO_EIO)
+ return;
+ folio->private = 0;
+ folio_end_read(folio, !(v & EROFS_ONLINEFOLIO_EIO));
+}
+
static int erofs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
unsigned int flags, struct iomap *iomap, struct iomap *srcmap)
{
@@ -399,7 +441,7 @@ static ssize_t erofs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
}
/* for uncompressed (aligned) files and raw access for other files */
-const struct address_space_operations erofs_raw_access_aops = {
+const struct address_space_operations erofs_aops = {
.read_folio = erofs_read_folio,
.readahead = erofs_readahead,
.bmap = erofs_bmap,
diff --git a/fs/erofs/fileio.c b/fs/erofs/fileio.c
new file mode 100644
index 000000000000..eab52b8abd0b
--- /dev/null
+++ b/fs/erofs/fileio.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2024, Alibaba Cloud
+ */
+#include "internal.h"
+#include <trace/events/erofs.h>
+
+struct erofs_fileio_rq {
+ struct bio_vec bvecs[BIO_MAX_VECS];
+ struct bio bio;
+ struct kiocb iocb;
+};
+
+struct erofs_fileio {
+ struct erofs_map_blocks map;
+ struct erofs_map_dev dev;
+ struct erofs_fileio_rq *rq;
+};
+
+static void erofs_fileio_ki_complete(struct kiocb *iocb, long ret)
+{
+ struct erofs_fileio_rq *rq =
+ container_of(iocb, struct erofs_fileio_rq, iocb);
+ struct folio_iter fi;
+
+ DBG_BUGON(rq->bio.bi_end_io);
+ if (ret > 0) {
+ if (ret != rq->bio.bi_iter.bi_size) {
+ bio_advance(&rq->bio, ret);
+ zero_fill_bio(&rq->bio);
+ }
+ ret = 0;
+ }
+ bio_for_each_folio_all(fi, &rq->bio) {
+ DBG_BUGON(folio_test_uptodate(fi.folio));
+ erofs_onlinefolio_end(fi.folio, ret);
+ }
+ kfree(rq);
+}
+
+static void erofs_fileio_rq_submit(struct erofs_fileio_rq *rq)
+{
+ struct iov_iter iter;
+ int ret;
+
+ if (!rq)
+ return;
+ rq->iocb.ki_pos = rq->bio.bi_iter.bi_sector << 9;
+ rq->iocb.ki_ioprio = get_current_ioprio();
+ rq->iocb.ki_complete = erofs_fileio_ki_complete;
+ rq->iocb.ki_flags = (rq->iocb.ki_filp->f_mode & FMODE_CAN_ODIRECT) ?
+ IOCB_DIRECT : 0;
+ iov_iter_bvec(&iter, ITER_DEST, rq->bvecs, rq->bio.bi_vcnt,
+ rq->bio.bi_iter.bi_size);
+ ret = vfs_iocb_iter_read(rq->iocb.ki_filp, &rq->iocb, &iter);
+ if (ret != -EIOCBQUEUED)
+ erofs_fileio_ki_complete(&rq->iocb, ret);
+}
+
+static struct erofs_fileio_rq *erofs_fileio_rq_alloc(struct erofs_map_dev *mdev)
+{
+ struct erofs_fileio_rq *rq = kzalloc(sizeof(*rq), GFP_KERNEL);
+
+ if (!rq)
+ return NULL;
+ bio_init(&rq->bio, NULL, rq->bvecs, BIO_MAX_VECS, REQ_OP_READ);
+ rq->iocb.ki_filp = mdev->m_fp;
+ return rq;
+}
+
+static int erofs_fileio_scan_folio(struct erofs_fileio *io, struct folio *folio)
+{
+ struct inode *inode = folio_inode(folio);
+ struct erofs_map_blocks *map = &io->map;
+ unsigned int cur = 0, end = folio_size(folio), len, attached = 0;
+ loff_t pos = folio_pos(folio), ofs;
+ struct iov_iter iter;
+ struct bio_vec bv;
+ int err = 0;
+
+ erofs_onlinefolio_init(folio);
+ while (cur < end) {
+ if (!in_range(pos + cur, map->m_la, map->m_llen)) {
+ map->m_la = pos + cur;
+ map->m_llen = end - cur;
+ err = erofs_map_blocks(inode, map);
+ if (err)
+ break;
+ }
+
+ ofs = folio_pos(folio) + cur - map->m_la;
+ len = min_t(loff_t, map->m_llen - ofs, end - cur);
+ if (map->m_flags & EROFS_MAP_META) {
+ struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
+ void *src;
+
+ src = erofs_read_metabuf(&buf, inode->i_sb,
+ map->m_pa + ofs, EROFS_KMAP);
+ if (IS_ERR(src)) {
+ err = PTR_ERR(src);
+ break;
+ }
+ bvec_set_folio(&bv, folio, len, cur);
+ iov_iter_bvec(&iter, ITER_DEST, &bv, 1, len);
+ if (copy_to_iter(src, len, &iter) != len) {
+ erofs_put_metabuf(&buf);
+ err = -EIO;
+ break;
+ }
+ erofs_put_metabuf(&buf);
+ } else if (!(map->m_flags & EROFS_MAP_MAPPED)) {
+ folio_zero_segment(folio, cur, cur + len);
+ } else {
+ if (io->rq && (map->m_pa + ofs != io->dev.m_pa ||
+ map->m_deviceid != io->dev.m_deviceid)) {
+io_retry:
+ erofs_fileio_rq_submit(io->rq);
+ io->rq = NULL;
+ }
+
+ if (!io->rq) {
+ io->dev = (struct erofs_map_dev) {
+ .m_pa = io->map.m_pa + ofs,
+ .m_deviceid = io->map.m_deviceid,
+ };
+ err = erofs_map_dev(inode->i_sb, &io->dev);
+ if (err)
+ break;
+ io->rq = erofs_fileio_rq_alloc(&io->dev);
+ if (!io->rq) {
+ err = -ENOMEM;
+ break;
+ }
+ io->rq->bio.bi_iter.bi_sector = io->dev.m_pa >> 9;
+ attached = 0;
+ }
+ if (!attached++)
+ erofs_onlinefolio_split(folio);
+ if (!bio_add_folio(&io->rq->bio, folio, len, cur))
+ goto io_retry;
+ io->dev.m_pa += len;
+ }
+ cur += len;
+ }
+ erofs_onlinefolio_end(folio, err);
+ return err;
+}
+
+static int erofs_fileio_read_folio(struct file *file, struct folio *folio)
+{
+ struct erofs_fileio io = {};
+ int err;
+
+ trace_erofs_read_folio(folio, false);
+ err = erofs_fileio_scan_folio(&io, folio);
+ erofs_fileio_rq_submit(io.rq);
+ return err;
+}
+
+static void erofs_fileio_readahead(struct readahead_control *rac)
+{
+ struct inode *inode = rac->mapping->host;
+ struct erofs_fileio io = {};
+ struct folio *folio;
+ int err;
+
+ trace_erofs_readpages(inode, readahead_index(rac),
+ readahead_count(rac), false);
+ while ((folio = readahead_folio(rac))) {
+ err = erofs_fileio_scan_folio(&io, folio);
+ if (err && err != -EINTR)
+ erofs_err(inode->i_sb, "readahead error at folio %lu @ nid %llu",
+ folio->index, EROFS_I(inode)->nid);
+ }
+ erofs_fileio_rq_submit(io.rq);
+}
+
+const struct address_space_operations erofs_fileio_aops = {
+ .read_folio = erofs_fileio_read_folio,
+ .readahead = erofs_fileio_readahead,
+};
diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c
index d05b9e59f122..4a902e6e69a5 100644
--- a/fs/erofs/inode.c
+++ b/fs/erofs/inode.c
@@ -258,11 +258,14 @@ static int erofs_fill_inode(struct inode *inode)
}
mapping_set_large_folios(inode->i_mapping);
- if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb))) {
- /* XXX: data I/Os will be implemented in the following patches */
- err = -EOPNOTSUPP;
- } else if (erofs_inode_is_data_compressed(vi->datalayout)) {
+ if (erofs_inode_is_data_compressed(vi->datalayout)) {
#ifdef CONFIG_EROFS_FS_ZIP
+#ifdef CONFIG_EROFS_FS_BACKED_BY_FILE
+ if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb))) {
+ err = -EOPNOTSUPP;
+ goto out_unlock;
+ }
+#endif
DO_ONCE_LITE_IF(inode->i_blkbits != PAGE_SHIFT,
erofs_info, inode->i_sb,
"EXPERIMENTAL EROFS subpage compressed block support in use. Use at your own risk!");
@@ -271,10 +274,14 @@ static int erofs_fill_inode(struct inode *inode)
err = -EOPNOTSUPP;
#endif
} else {
- inode->i_mapping->a_ops = &erofs_raw_access_aops;
+ inode->i_mapping->a_ops = &erofs_aops;
#ifdef CONFIG_EROFS_FS_ONDEMAND
if (erofs_is_fscache_mode(inode->i_sb))
inode->i_mapping->a_ops = &erofs_fscache_access_aops;
+#endif
+#ifdef CONFIG_EROFS_FS_BACKED_BY_FILE
+ if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb)))
+ inode->i_mapping->a_ops = &erofs_fileio_aops;
#endif
}
out_unlock:
diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
index 9bf4fb1cfa09..9bc4dcfd06d7 100644
--- a/fs/erofs/internal.h
+++ b/fs/erofs/internal.h
@@ -372,6 +372,7 @@ struct erofs_map_dev {
struct erofs_fscache *m_fscache;
struct block_device *m_bdev;
struct dax_device *m_daxdev;
+ struct file *m_fp;
u64 m_dax_part_off;
erofs_off_t m_pa;
@@ -380,7 +381,8 @@ struct erofs_map_dev {
extern const struct super_operations erofs_sops;
-extern const struct address_space_operations erofs_raw_access_aops;
+extern const struct address_space_operations erofs_aops;
+extern const struct address_space_operations erofs_fileio_aops;
extern const struct address_space_operations z_erofs_aops;
extern const struct address_space_operations erofs_fscache_access_aops;
@@ -411,6 +413,9 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *dev);
int erofs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
u64 start, u64 len);
int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map);
+void erofs_onlinefolio_init(struct folio *folio);
+void erofs_onlinefolio_split(struct folio *folio);
+void erofs_onlinefolio_end(struct folio *folio, int err);
struct inode *erofs_iget(struct super_block *sb, erofs_nid_t nid);
int erofs_getattr(struct mnt_idmap *idmap, const struct path *path,
struct kstat *stat, u32 request_mask,
diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
index 424f656cd765..350612f32ac6 100644
--- a/fs/erofs/zdata.c
+++ b/fs/erofs/zdata.c
@@ -122,42 +122,6 @@ static bool erofs_folio_is_managed(struct erofs_sb_info *sbi, struct folio *fo)
return fo->mapping == MNGD_MAPPING(sbi);
}
-/*
- * bit 30: I/O error occurred on this folio
- * bit 0 - 29: remaining parts to complete this folio
- */
-#define Z_EROFS_FOLIO_EIO (1 << 30)
-
-static void z_erofs_onlinefolio_init(struct folio *folio)
-{
- union {
- atomic_t o;
- void *v;
- } u = { .o = ATOMIC_INIT(1) };
-
- folio->private = u.v; /* valid only if file-backed folio is locked */
-}
-
-static void z_erofs_onlinefolio_split(struct folio *folio)
-{
- atomic_inc((atomic_t *)&folio->private);
-}
-
-static void z_erofs_onlinefolio_end(struct folio *folio, int err)
-{
- int orig, v;
-
- do {
- orig = atomic_read((atomic_t *)&folio->private);
- v = (orig - 1) | (err ? Z_EROFS_FOLIO_EIO : 0);
- } while (atomic_cmpxchg((atomic_t *)&folio->private, orig, v) != orig);
-
- if (v & ~Z_EROFS_FOLIO_EIO)
- return;
- folio->private = 0;
- folio_end_read(folio, !(v & Z_EROFS_FOLIO_EIO));
-}
-
#define Z_EROFS_ONSTACK_PAGES 32
/*
@@ -965,7 +929,7 @@ static int z_erofs_scan_folio(struct z_erofs_decompress_frontend *f,
int err = 0;
tight = (bs == PAGE_SIZE);
- z_erofs_onlinefolio_init(folio);
+ erofs_onlinefolio_init(folio);
do {
if (offset + end - 1 < map->m_la ||
offset + end - 1 >= map->m_la + map->m_llen) {
@@ -1024,7 +988,7 @@ static int z_erofs_scan_folio(struct z_erofs_decompress_frontend *f,
if (err)
break;
- z_erofs_onlinefolio_split(folio);
+ erofs_onlinefolio_split(folio);
if (f->pcl->pageofs_out != (map->m_la & ~PAGE_MASK))
f->pcl->multibases = true;
if (f->pcl->length < offset + end - map->m_la) {
@@ -1044,7 +1008,7 @@ static int z_erofs_scan_folio(struct z_erofs_decompress_frontend *f,
tight = (bs == PAGE_SIZE);
}
} while ((end = cur) > 0);
- z_erofs_onlinefolio_end(folio, err);
+ erofs_onlinefolio_end(folio, err);
return err;
}
@@ -1147,7 +1111,7 @@ static void z_erofs_fill_other_copies(struct z_erofs_decompress_backend *be,
cur += len;
}
kunmap_local(dst);
- z_erofs_onlinefolio_end(page_folio(bvi->bvec.page), err);
+ erofs_onlinefolio_end(page_folio(bvi->bvec.page), err);
list_del(p);
kfree(bvi);
}
@@ -1302,7 +1266,7 @@ static int z_erofs_decompress_pcluster(struct z_erofs_decompress_backend *be,
DBG_BUGON(z_erofs_page_is_invalidated(page));
if (!z_erofs_is_shortlived_page(page)) {
- z_erofs_onlinefolio_end(page_folio(page), err);
+ erofs_onlinefolio_end(page_folio(page), err);
continue;
}
if (pcl->algorithmformat != Z_EROFS_COMPRESSION_LZ4) {
--
2.43.5
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH v2 3/4] erofs: support compressed inodes for fileio
2024-08-30 3:28 [PATCH v2 1/4] erofs: add file-backed mount support Gao Xiang
2024-08-30 3:28 ` [PATCH v2 2/4] erofs: support unencoded inodes for fileio Gao Xiang
@ 2024-08-30 3:28 ` Gao Xiang
2024-09-01 20:47 ` Sandeep Dhavale
` (2 more replies)
2024-08-30 3:28 ` [PATCH v2 4/4] erofs: mark experimental fscache backend deprecated Gao Xiang
` (5 subsequent siblings)
7 siblings, 3 replies; 30+ messages in thread
From: Gao Xiang @ 2024-08-30 3:28 UTC (permalink / raw)
To: linux-erofs; +Cc: LKML, Gao Xiang
Use pseudo bios just like the previous fscache approach since
merged bio_vecs can be filled properly with unique interfaces.
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
---
fs/erofs/fileio.c | 25 +++++++++++++++++++++----
fs/erofs/inode.c | 6 ------
fs/erofs/internal.h | 8 ++++++++
fs/erofs/zdata.c | 27 +++++++++++++++++----------
4 files changed, 46 insertions(+), 20 deletions(-)
diff --git a/fs/erofs/fileio.c b/fs/erofs/fileio.c
index eab52b8abd0b..9e4b851d85c0 100644
--- a/fs/erofs/fileio.c
+++ b/fs/erofs/fileio.c
@@ -23,7 +23,6 @@ static void erofs_fileio_ki_complete(struct kiocb *iocb, long ret)
container_of(iocb, struct erofs_fileio_rq, iocb);
struct folio_iter fi;
- DBG_BUGON(rq->bio.bi_end_io);
if (ret > 0) {
if (ret != rq->bio.bi_iter.bi_size) {
bio_advance(&rq->bio, ret);
@@ -31,9 +30,13 @@ static void erofs_fileio_ki_complete(struct kiocb *iocb, long ret)
}
ret = 0;
}
- bio_for_each_folio_all(fi, &rq->bio) {
- DBG_BUGON(folio_test_uptodate(fi.folio));
- erofs_onlinefolio_end(fi.folio, ret);
+ if (rq->bio.bi_end_io) {
+ rq->bio.bi_end_io(&rq->bio);
+ } else {
+ bio_for_each_folio_all(fi, &rq->bio) {
+ DBG_BUGON(folio_test_uptodate(fi.folio));
+ erofs_onlinefolio_end(fi.folio, ret);
+ }
}
kfree(rq);
}
@@ -68,6 +71,20 @@ static struct erofs_fileio_rq *erofs_fileio_rq_alloc(struct erofs_map_dev *mdev)
return rq;
}
+struct bio *erofs_fileio_bio_alloc(struct erofs_map_dev *mdev)
+{
+ struct erofs_fileio_rq *rq;
+
+ rq = erofs_fileio_rq_alloc(mdev);
+ return rq ? &rq->bio : NULL;
+}
+
+void erofs_fileio_submit_bio(struct bio *bio)
+{
+ return erofs_fileio_rq_submit(container_of(bio, struct erofs_fileio_rq,
+ bio));
+}
+
static int erofs_fileio_scan_folio(struct erofs_fileio *io, struct folio *folio)
{
struct inode *inode = folio_inode(folio);
diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c
index 4a902e6e69a5..82259553d9f6 100644
--- a/fs/erofs/inode.c
+++ b/fs/erofs/inode.c
@@ -260,12 +260,6 @@ static int erofs_fill_inode(struct inode *inode)
mapping_set_large_folios(inode->i_mapping);
if (erofs_inode_is_data_compressed(vi->datalayout)) {
#ifdef CONFIG_EROFS_FS_ZIP
-#ifdef CONFIG_EROFS_FS_BACKED_BY_FILE
- if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb))) {
- err = -EOPNOTSUPP;
- goto out_unlock;
- }
-#endif
DO_ONCE_LITE_IF(inode->i_blkbits != PAGE_SHIFT,
erofs_info, inode->i_sb,
"EXPERIMENTAL EROFS subpage compressed block support in use. Use at your own risk!");
diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
index 9bc4dcfd06d7..4efd578d7c62 100644
--- a/fs/erofs/internal.h
+++ b/fs/erofs/internal.h
@@ -489,6 +489,14 @@ static inline void z_erofs_exit_subsystem(void) {}
static inline int erofs_init_managed_cache(struct super_block *sb) { return 0; }
#endif /* !CONFIG_EROFS_FS_ZIP */
+#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);
+#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) {}
+#endif
+
#ifdef CONFIG_EROFS_FS_ONDEMAND
int erofs_fscache_register_fs(struct super_block *sb);
void erofs_fscache_unregister_fs(struct super_block *sb);
diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
index 350612f32ac6..2271cb74ae3a 100644
--- a/fs/erofs/zdata.c
+++ b/fs/erofs/zdata.c
@@ -1618,10 +1618,12 @@ static void z_erofs_submit_queue(struct z_erofs_decompress_frontend *f,
if (bio && (cur != last_pa ||
bio->bi_bdev != mdev.m_bdev)) {
io_retry:
- if (!erofs_is_fscache_mode(sb))
- submit_bio(bio);
- else
+ if (erofs_is_fileio_mode(EROFS_SB(sb)))
+ erofs_fileio_submit_bio(bio);
+ else if (erofs_is_fscache_mode(sb))
erofs_fscache_submit_bio(bio);
+ else
+ submit_bio(bio);
if (memstall) {
psi_memstall_leave(&pflags);
@@ -1637,10 +1639,13 @@ static void z_erofs_submit_queue(struct z_erofs_decompress_frontend *f,
}
if (!bio) {
- bio = erofs_is_fscache_mode(sb) ?
- erofs_fscache_bio_alloc(&mdev) :
- bio_alloc(mdev.m_bdev, BIO_MAX_VECS,
- REQ_OP_READ, GFP_NOIO);
+ if (erofs_is_fileio_mode(EROFS_SB(sb)))
+ bio = erofs_fileio_bio_alloc(&mdev);
+ else if (erofs_is_fscache_mode(sb))
+ bio = erofs_fscache_bio_alloc(&mdev);
+ else
+ bio = bio_alloc(mdev.m_bdev, BIO_MAX_VECS,
+ REQ_OP_READ, GFP_NOIO);
bio->bi_end_io = z_erofs_endio;
bio->bi_iter.bi_sector = cur >> 9;
bio->bi_private = q[JQ_SUBMIT];
@@ -1667,10 +1672,12 @@ static void z_erofs_submit_queue(struct z_erofs_decompress_frontend *f,
} while (owned_head != Z_EROFS_PCLUSTER_TAIL);
if (bio) {
- if (!erofs_is_fscache_mode(sb))
- submit_bio(bio);
- else
+ if (erofs_is_fileio_mode(EROFS_SB(sb)))
+ erofs_fileio_submit_bio(bio);
+ else if (erofs_is_fscache_mode(sb))
erofs_fscache_submit_bio(bio);
+ else
+ submit_bio(bio);
if (memstall)
psi_memstall_leave(&pflags);
}
--
2.43.5
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH v2 4/4] erofs: mark experimental fscache backend deprecated
2024-08-30 3:28 [PATCH v2 1/4] erofs: add file-backed mount support Gao Xiang
2024-08-30 3:28 ` [PATCH v2 2/4] erofs: support unencoded inodes for fileio Gao Xiang
2024-08-30 3:28 ` [PATCH v2 3/4] erofs: support compressed " Gao Xiang
@ 2024-08-30 3:28 ` Gao Xiang
2024-09-01 20:48 ` Sandeep Dhavale
2024-09-05 9:03 ` Chao Yu
2024-08-31 0:30 ` [PATCH v2 1/4] erofs: add file-backed mount support Sandeep Dhavale
` (4 subsequent siblings)
7 siblings, 2 replies; 30+ messages in thread
From: Gao Xiang @ 2024-08-30 3:28 UTC (permalink / raw)
To: linux-erofs; +Cc: LKML, Gao Xiang
Although fscache is still described as "General Filesystem Caching" for
network filesystems and other things such as ISO9660 filesystems, it has
actually become a part of netfslib recently, which was unexpected at the
time when "EROFS over fscache" proposed (2021) since EROFS is entirely a
disk filesystem and the dependency is redundant.
Mark it deprecated and it will be removed after "fanotify pre-content
hooks" lands, which will provide the same functionality for EROFS.
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
---
fs/erofs/Kconfig | 5 ++++-
fs/erofs/super.c | 2 +-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig
index 1428d0530e1c..6ea60661fa55 100644
--- a/fs/erofs/Kconfig
+++ b/fs/erofs/Kconfig
@@ -145,7 +145,7 @@ config EROFS_FS_ZIP_ZSTD
If unsure, say N.
config EROFS_FS_ONDEMAND
- bool "EROFS fscache-based on-demand read support"
+ bool "EROFS fscache-based on-demand read support (deprecated)"
depends on EROFS_FS
select NETFS_SUPPORT
select FSCACHE
@@ -155,6 +155,9 @@ config EROFS_FS_ONDEMAND
This permits EROFS to use fscache-backed data blobs with on-demand
read support.
+ It is now deprecated and scheduled to be removed from the kernel
+ after fanotify pre-content hooks are landed.
+
If unsure, say N.
config EROFS_FS_PCPU_KTHREAD
diff --git a/fs/erofs/super.c b/fs/erofs/super.c
index 9a7e67eceed4..666873f745da 100644
--- a/fs/erofs/super.c
+++ b/fs/erofs/super.c
@@ -353,7 +353,7 @@ static int erofs_read_superblock(struct super_block *sb)
ret = erofs_scan_devices(sb, dsb);
if (erofs_is_fscache_mode(sb))
- erofs_info(sb, "EXPERIMENTAL fscache-based on-demand read feature in use. Use at your own risk!");
+ erofs_info(sb, "[deprecated] fscache-based on-demand read feature in use. Use at your own risk!");
out:
erofs_put_metabuf(&buf);
return ret;
--
2.43.5
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 2/4] erofs: support unencoded inodes for fileio
2024-08-30 3:28 ` [PATCH v2 2/4] erofs: support unencoded inodes for fileio Gao Xiang
@ 2024-08-31 0:19 ` Sandeep Dhavale
2024-08-31 2:54 ` Gao Xiang
2024-08-31 4:25 ` Sandeep Dhavale
` (2 subsequent siblings)
3 siblings, 1 reply; 30+ messages in thread
From: Sandeep Dhavale @ 2024-08-31 0:19 UTC (permalink / raw)
To: Gao Xiang; +Cc: linux-erofs, LKML
On Thu, Aug 29, 2024 at 8:29 PM Gao Xiang <hsiangkao@linux.alibaba.com> wrote:
>
> Since EROFS only needs to handle read requests in simple contexts,
> Just directly use vfs_iocb_iter_read() for data I/Os.
>
> Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
> ---
> v2:
> - fix redundant refcount which cause hanging on chunked inodes.
>
> fs/erofs/Makefile | 1 +
> fs/erofs/data.c | 50 +++++++++++-
> fs/erofs/fileio.c | 181 ++++++++++++++++++++++++++++++++++++++++++++
> fs/erofs/inode.c | 17 +++--
> fs/erofs/internal.h | 7 +-
> fs/erofs/zdata.c | 46 ++---------
> 6 files changed, 251 insertions(+), 51 deletions(-)
> create mode 100644 fs/erofs/fileio.c
>
> diff --git a/fs/erofs/Makefile b/fs/erofs/Makefile
> index 097d672e6b14..4331d53c7109 100644
> --- a/fs/erofs/Makefile
> +++ b/fs/erofs/Makefile
> @@ -7,4 +7,5 @@ erofs-$(CONFIG_EROFS_FS_ZIP) += decompressor.o zmap.o zdata.o zutil.o
> erofs-$(CONFIG_EROFS_FS_ZIP_LZMA) += decompressor_lzma.o
> erofs-$(CONFIG_EROFS_FS_ZIP_DEFLATE) += decompressor_deflate.o
> erofs-$(CONFIG_EROFS_FS_ZIP_ZSTD) += decompressor_zstd.o
> +erofs-$(CONFIG_EROFS_FS_BACKED_BY_FILE) += fileio.o
> erofs-$(CONFIG_EROFS_FS_ONDEMAND) += fscache.o
> diff --git a/fs/erofs/data.c b/fs/erofs/data.c
> index 0fb31c588ae0..b4c07ce7a294 100644
> --- a/fs/erofs/data.c
> +++ b/fs/erofs/data.c
> @@ -132,7 +132,7 @@ int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map)
> if (map->m_la >= inode->i_size) {
> /* leave out-of-bound access unmapped */
> map->m_flags = 0;
> - map->m_plen = 0;
> + map->m_plen = map->m_llen;
> goto out;
> }
>
> @@ -197,8 +197,13 @@ static void erofs_fill_from_devinfo(struct erofs_map_dev *map,
> struct erofs_device_info *dif)
> {
> map->m_bdev = NULL;
> - if (dif->file && S_ISBLK(file_inode(dif->file)->i_mode))
> - map->m_bdev = file_bdev(dif->file);
> + map->m_fp = NULL;
> + if (dif->file) {
> + if (S_ISBLK(file_inode(dif->file)->i_mode))
> + map->m_bdev = file_bdev(dif->file);
> + else
> + map->m_fp = dif->file;
> + }
> map->m_daxdev = dif->dax_dev;
> map->m_dax_part_off = dif->dax_part_off;
> map->m_fscache = dif->fscache;
> @@ -215,6 +220,7 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)
> map->m_daxdev = EROFS_SB(sb)->dax_dev;
> map->m_dax_part_off = EROFS_SB(sb)->dax_part_off;
> map->m_fscache = EROFS_SB(sb)->s_fscache;
> + map->m_fp = EROFS_SB(sb)->fdev;
>
> if (map->m_deviceid) {
> down_read(&devs->rwsem);
> @@ -250,6 +256,42 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)
> return 0;
> }
>
> +/*
> + * bit 30: I/O error occurred on this folio
> + * bit 0 - 29: remaining parts to complete this folio
> + */
> +#define EROFS_ONLINEFOLIO_EIO (1 << 30)
> +
> +void erofs_onlinefolio_init(struct folio *folio)
> +{
> + union {
> + atomic_t o;
> + void *v;
> + } u = { .o = ATOMIC_INIT(1) };
> +
> + folio->private = u.v; /* valid only if file-backed folio is locked */
> +}
> +
> +void erofs_onlinefolio_split(struct folio *folio)
> +{
> + atomic_inc((atomic_t *)&folio->private);
> +}
> +
> +void erofs_onlinefolio_end(struct folio *folio, int err)
> +{
> + int orig, v;
> +
> + do {
> + orig = atomic_read((atomic_t *)&folio->private);
> + v = (orig - 1) | (err ? EROFS_ONLINEFOLIO_EIO : 0);
> + } while (atomic_cmpxchg((atomic_t *)&folio->private, orig, v) != orig);
> +
> + if (v & ~EROFS_ONLINEFOLIO_EIO)
> + return;
> + folio->private = 0;
> + folio_end_read(folio, !(v & EROFS_ONLINEFOLIO_EIO));
> +}
> +
> static int erofs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
> unsigned int flags, struct iomap *iomap, struct iomap *srcmap)
> {
> @@ -399,7 +441,7 @@ static ssize_t erofs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
> }
>
> /* for uncompressed (aligned) files and raw access for other files */
> -const struct address_space_operations erofs_raw_access_aops = {
> +const struct address_space_operations erofs_aops = {
> .read_folio = erofs_read_folio,
> .readahead = erofs_readahead,
> .bmap = erofs_bmap,
> diff --git a/fs/erofs/fileio.c b/fs/erofs/fileio.c
> new file mode 100644
> index 000000000000..eab52b8abd0b
> --- /dev/null
> +++ b/fs/erofs/fileio.c
> @@ -0,0 +1,181 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2024, Alibaba Cloud
> + */
> +#include "internal.h"
> +#include <trace/events/erofs.h>
> +
> +struct erofs_fileio_rq {
> + struct bio_vec bvecs[BIO_MAX_VECS];
> + struct bio bio;
> + struct kiocb iocb;
> +};
> +
> +struct erofs_fileio {
> + struct erofs_map_blocks map;
> + struct erofs_map_dev dev;
> + struct erofs_fileio_rq *rq;
> +};
> +
> +static void erofs_fileio_ki_complete(struct kiocb *iocb, long ret)
> +{
> + struct erofs_fileio_rq *rq =
> + container_of(iocb, struct erofs_fileio_rq, iocb);
> + struct folio_iter fi;
> +
> + DBG_BUGON(rq->bio.bi_end_io);
> + if (ret > 0) {
> + if (ret != rq->bio.bi_iter.bi_size) {
> + bio_advance(&rq->bio, ret);
> + zero_fill_bio(&rq->bio);
> + }
> + ret = 0;
> + }
> + bio_for_each_folio_all(fi, &rq->bio) {
> + DBG_BUGON(folio_test_uptodate(fi.folio));
> + erofs_onlinefolio_end(fi.folio, ret);
> + }
> + kfree(rq);
> +}
> +
> +static void erofs_fileio_rq_submit(struct erofs_fileio_rq *rq)
> +{
> + struct iov_iter iter;
> + int ret;
> +
> + if (!rq)
> + return;
> + rq->iocb.ki_pos = rq->bio.bi_iter.bi_sector << 9;
> + rq->iocb.ki_ioprio = get_current_ioprio();
> + rq->iocb.ki_complete = erofs_fileio_ki_complete;
> + rq->iocb.ki_flags = (rq->iocb.ki_filp->f_mode & FMODE_CAN_ODIRECT) ?
> + IOCB_DIRECT : 0;
Hi Gao,
Does this mean, direct IO by default if the backing file supports it
(technically filesystem where image/backing file reside)?
Thanks,
Sandeep.
> + iov_iter_bvec(&iter, ITER_DEST, rq->bvecs, rq->bio.bi_vcnt,
> + rq->bio.bi_iter.bi_size);
> + ret = vfs_iocb_iter_read(rq->iocb.ki_filp, &rq->iocb, &iter);
> + if (ret != -EIOCBQUEUED)
> + erofs_fileio_ki_complete(&rq->iocb, ret);
> +}
> +
> +static struct erofs_fileio_rq *erofs_fileio_rq_alloc(struct erofs_map_dev *mdev)
> +{
> + struct erofs_fileio_rq *rq = kzalloc(sizeof(*rq), GFP_KERNEL);
> +
> + if (!rq)
> + return NULL;
> + bio_init(&rq->bio, NULL, rq->bvecs, BIO_MAX_VECS, REQ_OP_READ);
> + rq->iocb.ki_filp = mdev->m_fp;
> + return rq;
> +}
> +
> +static int erofs_fileio_scan_folio(struct erofs_fileio *io, struct folio *folio)
> +{
> + struct inode *inode = folio_inode(folio);
> + struct erofs_map_blocks *map = &io->map;
> + unsigned int cur = 0, end = folio_size(folio), len, attached = 0;
> + loff_t pos = folio_pos(folio), ofs;
> + struct iov_iter iter;
> + struct bio_vec bv;
> + int err = 0;
> +
> + erofs_onlinefolio_init(folio);
> + while (cur < end) {
> + if (!in_range(pos + cur, map->m_la, map->m_llen)) {
> + map->m_la = pos + cur;
> + map->m_llen = end - cur;
> + err = erofs_map_blocks(inode, map);
> + if (err)
> + break;
> + }
> +
> + ofs = folio_pos(folio) + cur - map->m_la;
> + len = min_t(loff_t, map->m_llen - ofs, end - cur);
> + if (map->m_flags & EROFS_MAP_META) {
> + struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
> + void *src;
> +
> + src = erofs_read_metabuf(&buf, inode->i_sb,
> + map->m_pa + ofs, EROFS_KMAP);
> + if (IS_ERR(src)) {
> + err = PTR_ERR(src);
> + break;
> + }
> + bvec_set_folio(&bv, folio, len, cur);
> + iov_iter_bvec(&iter, ITER_DEST, &bv, 1, len);
> + if (copy_to_iter(src, len, &iter) != len) {
> + erofs_put_metabuf(&buf);
> + err = -EIO;
> + break;
> + }
> + erofs_put_metabuf(&buf);
> + } else if (!(map->m_flags & EROFS_MAP_MAPPED)) {
> + folio_zero_segment(folio, cur, cur + len);
> + } else {
> + if (io->rq && (map->m_pa + ofs != io->dev.m_pa ||
> + map->m_deviceid != io->dev.m_deviceid)) {
> +io_retry:
> + erofs_fileio_rq_submit(io->rq);
> + io->rq = NULL;
> + }
> +
> + if (!io->rq) {
> + io->dev = (struct erofs_map_dev) {
> + .m_pa = io->map.m_pa + ofs,
> + .m_deviceid = io->map.m_deviceid,
> + };
> + err = erofs_map_dev(inode->i_sb, &io->dev);
> + if (err)
> + break;
> + io->rq = erofs_fileio_rq_alloc(&io->dev);
> + if (!io->rq) {
> + err = -ENOMEM;
> + break;
> + }
> + io->rq->bio.bi_iter.bi_sector = io->dev.m_pa >> 9;
> + attached = 0;
> + }
> + if (!attached++)
> + erofs_onlinefolio_split(folio);
> + if (!bio_add_folio(&io->rq->bio, folio, len, cur))
> + goto io_retry;
> + io->dev.m_pa += len;
> + }
> + cur += len;
> + }
> + erofs_onlinefolio_end(folio, err);
> + return err;
> +}
> +
> +static int erofs_fileio_read_folio(struct file *file, struct folio *folio)
> +{
> + struct erofs_fileio io = {};
> + int err;
> +
> + trace_erofs_read_folio(folio, false);
> + err = erofs_fileio_scan_folio(&io, folio);
> + erofs_fileio_rq_submit(io.rq);
> + return err;
> +}
> +
> +static void erofs_fileio_readahead(struct readahead_control *rac)
> +{
> + struct inode *inode = rac->mapping->host;
> + struct erofs_fileio io = {};
> + struct folio *folio;
> + int err;
> +
> + trace_erofs_readpages(inode, readahead_index(rac),
> + readahead_count(rac), false);
> + while ((folio = readahead_folio(rac))) {
> + err = erofs_fileio_scan_folio(&io, folio);
> + if (err && err != -EINTR)
> + erofs_err(inode->i_sb, "readahead error at folio %lu @ nid %llu",
> + folio->index, EROFS_I(inode)->nid);
> + }
> + erofs_fileio_rq_submit(io.rq);
> +}
> +
> +const struct address_space_operations erofs_fileio_aops = {
> + .read_folio = erofs_fileio_read_folio,
> + .readahead = erofs_fileio_readahead,
> +};
> diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c
> index d05b9e59f122..4a902e6e69a5 100644
> --- a/fs/erofs/inode.c
> +++ b/fs/erofs/inode.c
> @@ -258,11 +258,14 @@ static int erofs_fill_inode(struct inode *inode)
> }
>
> mapping_set_large_folios(inode->i_mapping);
> - if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb))) {
> - /* XXX: data I/Os will be implemented in the following patches */
> - err = -EOPNOTSUPP;
> - } else if (erofs_inode_is_data_compressed(vi->datalayout)) {
> + if (erofs_inode_is_data_compressed(vi->datalayout)) {
> #ifdef CONFIG_EROFS_FS_ZIP
> +#ifdef CONFIG_EROFS_FS_BACKED_BY_FILE
> + if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb))) {
> + err = -EOPNOTSUPP;
> + goto out_unlock;
> + }
> +#endif
> DO_ONCE_LITE_IF(inode->i_blkbits != PAGE_SHIFT,
> erofs_info, inode->i_sb,
> "EXPERIMENTAL EROFS subpage compressed block support in use. Use at your own risk!");
> @@ -271,10 +274,14 @@ static int erofs_fill_inode(struct inode *inode)
> err = -EOPNOTSUPP;
> #endif
> } else {
> - inode->i_mapping->a_ops = &erofs_raw_access_aops;
> + inode->i_mapping->a_ops = &erofs_aops;
> #ifdef CONFIG_EROFS_FS_ONDEMAND
> if (erofs_is_fscache_mode(inode->i_sb))
> inode->i_mapping->a_ops = &erofs_fscache_access_aops;
> +#endif
> +#ifdef CONFIG_EROFS_FS_BACKED_BY_FILE
> + if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb)))
> + inode->i_mapping->a_ops = &erofs_fileio_aops;
> #endif
> }
> out_unlock:
> diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
> index 9bf4fb1cfa09..9bc4dcfd06d7 100644
> --- a/fs/erofs/internal.h
> +++ b/fs/erofs/internal.h
> @@ -372,6 +372,7 @@ struct erofs_map_dev {
> struct erofs_fscache *m_fscache;
> struct block_device *m_bdev;
> struct dax_device *m_daxdev;
> + struct file *m_fp;
> u64 m_dax_part_off;
>
> erofs_off_t m_pa;
> @@ -380,7 +381,8 @@ struct erofs_map_dev {
>
> extern const struct super_operations erofs_sops;
>
> -extern const struct address_space_operations erofs_raw_access_aops;
> +extern const struct address_space_operations erofs_aops;
> +extern const struct address_space_operations erofs_fileio_aops;
> extern const struct address_space_operations z_erofs_aops;
> extern const struct address_space_operations erofs_fscache_access_aops;
>
> @@ -411,6 +413,9 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *dev);
> int erofs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
> u64 start, u64 len);
> int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map);
> +void erofs_onlinefolio_init(struct folio *folio);
> +void erofs_onlinefolio_split(struct folio *folio);
> +void erofs_onlinefolio_end(struct folio *folio, int err);
> struct inode *erofs_iget(struct super_block *sb, erofs_nid_t nid);
> int erofs_getattr(struct mnt_idmap *idmap, const struct path *path,
> struct kstat *stat, u32 request_mask,
> diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
> index 424f656cd765..350612f32ac6 100644
> --- a/fs/erofs/zdata.c
> +++ b/fs/erofs/zdata.c
> @@ -122,42 +122,6 @@ static bool erofs_folio_is_managed(struct erofs_sb_info *sbi, struct folio *fo)
> return fo->mapping == MNGD_MAPPING(sbi);
> }
>
> -/*
> - * bit 30: I/O error occurred on this folio
> - * bit 0 - 29: remaining parts to complete this folio
> - */
> -#define Z_EROFS_FOLIO_EIO (1 << 30)
> -
> -static void z_erofs_onlinefolio_init(struct folio *folio)
> -{
> - union {
> - atomic_t o;
> - void *v;
> - } u = { .o = ATOMIC_INIT(1) };
> -
> - folio->private = u.v; /* valid only if file-backed folio is locked */
> -}
> -
> -static void z_erofs_onlinefolio_split(struct folio *folio)
> -{
> - atomic_inc((atomic_t *)&folio->private);
> -}
> -
> -static void z_erofs_onlinefolio_end(struct folio *folio, int err)
> -{
> - int orig, v;
> -
> - do {
> - orig = atomic_read((atomic_t *)&folio->private);
> - v = (orig - 1) | (err ? Z_EROFS_FOLIO_EIO : 0);
> - } while (atomic_cmpxchg((atomic_t *)&folio->private, orig, v) != orig);
> -
> - if (v & ~Z_EROFS_FOLIO_EIO)
> - return;
> - folio->private = 0;
> - folio_end_read(folio, !(v & Z_EROFS_FOLIO_EIO));
> -}
> -
> #define Z_EROFS_ONSTACK_PAGES 32
>
> /*
> @@ -965,7 +929,7 @@ static int z_erofs_scan_folio(struct z_erofs_decompress_frontend *f,
> int err = 0;
>
> tight = (bs == PAGE_SIZE);
> - z_erofs_onlinefolio_init(folio);
> + erofs_onlinefolio_init(folio);
> do {
> if (offset + end - 1 < map->m_la ||
> offset + end - 1 >= map->m_la + map->m_llen) {
> @@ -1024,7 +988,7 @@ static int z_erofs_scan_folio(struct z_erofs_decompress_frontend *f,
> if (err)
> break;
>
> - z_erofs_onlinefolio_split(folio);
> + erofs_onlinefolio_split(folio);
> if (f->pcl->pageofs_out != (map->m_la & ~PAGE_MASK))
> f->pcl->multibases = true;
> if (f->pcl->length < offset + end - map->m_la) {
> @@ -1044,7 +1008,7 @@ static int z_erofs_scan_folio(struct z_erofs_decompress_frontend *f,
> tight = (bs == PAGE_SIZE);
> }
> } while ((end = cur) > 0);
> - z_erofs_onlinefolio_end(folio, err);
> + erofs_onlinefolio_end(folio, err);
> return err;
> }
>
> @@ -1147,7 +1111,7 @@ static void z_erofs_fill_other_copies(struct z_erofs_decompress_backend *be,
> cur += len;
> }
> kunmap_local(dst);
> - z_erofs_onlinefolio_end(page_folio(bvi->bvec.page), err);
> + erofs_onlinefolio_end(page_folio(bvi->bvec.page), err);
> list_del(p);
> kfree(bvi);
> }
> @@ -1302,7 +1266,7 @@ static int z_erofs_decompress_pcluster(struct z_erofs_decompress_backend *be,
>
> DBG_BUGON(z_erofs_page_is_invalidated(page));
> if (!z_erofs_is_shortlived_page(page)) {
> - z_erofs_onlinefolio_end(page_folio(page), err);
> + erofs_onlinefolio_end(page_folio(page), err);
> continue;
> }
> if (pcl->algorithmformat != Z_EROFS_COMPRESSION_LZ4) {
> --
> 2.43.5
>
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 1/4] erofs: add file-backed mount support
2024-08-30 3:28 [PATCH v2 1/4] erofs: add file-backed mount support Gao Xiang
` (2 preceding siblings ...)
2024-08-30 3:28 ` [PATCH v2 4/4] erofs: mark experimental fscache backend deprecated Gao Xiang
@ 2024-08-31 0:30 ` Sandeep Dhavale
2024-08-31 3:00 ` Gao Xiang
2024-09-01 20:46 ` Sandeep Dhavale
` (3 subsequent siblings)
7 siblings, 1 reply; 30+ messages in thread
From: Sandeep Dhavale @ 2024-08-31 0:30 UTC (permalink / raw)
To: Gao Xiang; +Cc: linux-erofs, LKML, kernel-team
On Thu, Aug 29, 2024 at 8:29 PM Gao Xiang <hsiangkao@linux.alibaba.com> wrote:
>
> It actually has been around for years: For containers and other sandbox
> use cases, there will be thousands (and even more) of authenticated
> (sub)images running on the same host, unlike OS images.
>
> Of course, all scenarios can use the same EROFS on-disk format, but
> bdev-backed mounts just work well for OS images since golden data is
> dumped into real block devices. However, it's somewhat hard for
> container runtimes to manage and isolate so many unnecessary virtual
> block devices safely and efficiently [1]: they just look like a burden
> to orchestrators and file-backed mounts are preferred indeed. There
> were already enough attempts such as Incremental FS, the original
> ComposeFS and PuzzleFS acting in the same way for immutable fses. As
> for current EROFS users, ComposeFS, containerd and Android APEXs will
> be directly benefited from it.
>
Hi Gao,
Thank you for the series! This is an interesting idea and will
definitely help the Android ecosystem for APEXes if we can remove the
loopback device. I will take a deeper look and provide comments soon.
Thanks,
Sandeep.
> On the other hand, previous experimental feature "erofs over fscache"
> was once also intended to provide a similar solution (inspired by
> Incremental FS discussion [2]), but the following facts show file-backed
> mounts will be a better approach:
> - Fscache infrastructure has recently been moved into new Netfslib
> which is an unexpected dependency to EROFS really, although it
> originally claims "it could be used for caching other things such as
> ISO9660 filesystems too." [3]
>
> - It takes an unexpectedly long time to upstream Fscache/Cachefiles
> enhancements. For example, the failover feature took more than
> one year, and the deamonless feature is still far behind now;
>
> - Ongoing HSM "fanotify pre-content hooks" [4] together with this will
> perfectly supersede "erofs over fscache" in a simpler way since
> developers (mainly containerd folks) could leverage their existing
> caching mechanism entirely in userspace instead of strictly following
> the predefined in-kernel caching tree hierarchy.
>
> After "fanotify pre-content hooks" lands upstream to provide the same
> functionality, "erofs over fscache" will be removed then (as an EROFS
> internal improvement and EROFS will not have to bother with on-demand
> fetching and/or caching improvements anymore.)
>
> [1] https://github.com/containers/storage/pull/2039
> [2] https://lore.kernel.org/r/CAOQ4uxjbVxnubaPjVaGYiSwoGDTdpWbB=w_AeM6YM=zVixsUfQ@mail.gmail.com
> [3] https://docs.kernel.org/filesystems/caching/fscache.html
> [4] https://lore.kernel.org/r/cover.1723670362.git.josef@toxicpanda.com
>
> Closes: https://github.com/containers/composefs/issues/144
> Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
> ---
> v2:
> - should use kill_anon_super();
> - add O_LARGEFILE to support large files.
>
> fs/erofs/Kconfig | 17 ++++++++++
> fs/erofs/data.c | 35 ++++++++++++---------
> fs/erofs/inode.c | 5 ++-
> fs/erofs/internal.h | 11 +++++--
> fs/erofs/super.c | 76 +++++++++++++++++++++++++++++----------------
> 5 files changed, 100 insertions(+), 44 deletions(-)
>
> diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig
> index 7dcdce660cac..1428d0530e1c 100644
> --- a/fs/erofs/Kconfig
> +++ b/fs/erofs/Kconfig
> @@ -74,6 +74,23 @@ config EROFS_FS_SECURITY
>
> If you are not using a security module, say N.
>
> +config EROFS_FS_BACKED_BY_FILE
> + bool "File-backed EROFS filesystem support"
> + depends on EROFS_FS
> + default y
> + help
> + This allows EROFS to use filesystem image files directly, without
> + the intercession of loopback block devices or likewise. It is
> + particularly useful for container images with numerous blobs and
> + other sandboxes, where loop devices behave intricately. It can also
> + be used to simplify error-prone lifetime management of unnecessary
> + virtual block devices.
> +
> + Note that this feature, along with ongoing fanotify pre-content
> + hooks, will eventually replace "EROFS over fscache."
> +
> + If you don't want to enable this feature, say N.
> +
> config EROFS_FS_ZIP
> bool "EROFS Data Compression Support"
> depends on EROFS_FS
> diff --git a/fs/erofs/data.c b/fs/erofs/data.c
> index 1b7eba38ba1e..0fb31c588ae0 100644
> --- a/fs/erofs/data.c
> +++ b/fs/erofs/data.c
> @@ -59,8 +59,12 @@ void *erofs_bread(struct erofs_buf *buf, erofs_off_t offset,
>
> void erofs_init_metabuf(struct erofs_buf *buf, struct super_block *sb)
> {
> - if (erofs_is_fscache_mode(sb))
> - buf->mapping = EROFS_SB(sb)->s_fscache->inode->i_mapping;
> + struct erofs_sb_info *sbi = EROFS_SB(sb);
> +
> + if (erofs_is_fileio_mode(sbi))
> + buf->mapping = file_inode(sbi->fdev)->i_mapping;
> + else if (erofs_is_fscache_mode(sb))
> + buf->mapping = sbi->s_fscache->inode->i_mapping;
> else
> buf->mapping = sb->s_bdev->bd_mapping;
> }
> @@ -189,10 +193,22 @@ int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map)
> return err;
> }
>
> +static void erofs_fill_from_devinfo(struct erofs_map_dev *map,
> + struct erofs_device_info *dif)
> +{
> + map->m_bdev = NULL;
> + if (dif->file && S_ISBLK(file_inode(dif->file)->i_mode))
> + map->m_bdev = file_bdev(dif->file);
> + map->m_daxdev = dif->dax_dev;
> + map->m_dax_part_off = dif->dax_part_off;
> + map->m_fscache = dif->fscache;
> +}
> +
> 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;
> + erofs_off_t startoff, length;
> int id;
>
> map->m_bdev = sb->s_bdev;
> @@ -212,29 +228,20 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)
> up_read(&devs->rwsem);
> return 0;
> }
> - map->m_bdev = dif->bdev_file ? file_bdev(dif->bdev_file) : NULL;
> - map->m_daxdev = dif->dax_dev;
> - map->m_dax_part_off = dif->dax_part_off;
> - map->m_fscache = dif->fscache;
> + erofs_fill_from_devinfo(map, dif);
> up_read(&devs->rwsem);
> } else if (devs->extra_devices && !devs->flatdev) {
> down_read(&devs->rwsem);
> idr_for_each_entry(&devs->tree, dif, id) {
> - erofs_off_t startoff, length;
> -
> if (!dif->mapped_blkaddr)
> continue;
> +
> startoff = erofs_pos(sb, dif->mapped_blkaddr);
> length = erofs_pos(sb, dif->blocks);
> -
> if (map->m_pa >= startoff &&
> map->m_pa < startoff + length) {
> map->m_pa -= startoff;
> - map->m_bdev = dif->bdev_file ?
> - file_bdev(dif->bdev_file) : NULL;
> - map->m_daxdev = dif->dax_dev;
> - map->m_dax_part_off = dif->dax_part_off;
> - map->m_fscache = dif->fscache;
> + erofs_fill_from_devinfo(map, dif);
> break;
> }
> }
> diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c
> index 419432be3223..d05b9e59f122 100644
> --- a/fs/erofs/inode.c
> +++ b/fs/erofs/inode.c
> @@ -258,7 +258,10 @@ static int erofs_fill_inode(struct inode *inode)
> }
>
> mapping_set_large_folios(inode->i_mapping);
> - if (erofs_inode_is_data_compressed(vi->datalayout)) {
> + if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb))) {
> + /* XXX: data I/Os will be implemented in the following patches */
> + err = -EOPNOTSUPP;
> + } else if (erofs_inode_is_data_compressed(vi->datalayout)) {
> #ifdef CONFIG_EROFS_FS_ZIP
> DO_ONCE_LITE_IF(inode->i_blkbits != PAGE_SHIFT,
> erofs_info, inode->i_sb,
> diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
> index 45dc15ebd870..9bf4fb1cfa09 100644
> --- a/fs/erofs/internal.h
> +++ b/fs/erofs/internal.h
> @@ -49,7 +49,7 @@ typedef u32 erofs_blk_t;
> struct erofs_device_info {
> char *path;
> struct erofs_fscache *fscache;
> - struct file *bdev_file;
> + struct file *file;
> struct dax_device *dax_dev;
> u64 dax_part_off;
>
> @@ -130,6 +130,7 @@ struct erofs_sb_info {
>
> struct erofs_sb_lz4_info lz4;
> #endif /* CONFIG_EROFS_FS_ZIP */
> + struct file *fdev;
> struct inode *packed_inode;
> struct erofs_dev_context *devs;
> struct dax_device *dax_dev;
> @@ -190,9 +191,15 @@ struct erofs_sb_info {
> #define set_opt(opt, option) ((opt)->mount_opt |= EROFS_MOUNT_##option)
> #define test_opt(opt, option) ((opt)->mount_opt & EROFS_MOUNT_##option)
>
> +static inline bool erofs_is_fileio_mode(struct erofs_sb_info *sbi)
> +{
> + return IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) && sbi->fdev;
> +}
> +
> static inline bool erofs_is_fscache_mode(struct super_block *sb)
> {
> - return IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && !sb->s_bdev;
> + return IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) &&
> + !erofs_is_fileio_mode(EROFS_SB(sb)) && !sb->s_bdev;
> }
>
> enum {
> diff --git a/fs/erofs/super.c b/fs/erofs/super.c
> index aae3fd15899a..9a7e67eceed4 100644
> --- a/fs/erofs/super.c
> +++ b/fs/erofs/super.c
> @@ -10,6 +10,7 @@
> #include <linux/fs_context.h>
> #include <linux/fs_parser.h>
> #include <linux/exportfs.h>
> +#include <linux/backing-dev.h>
> #include "xattr.h"
>
> #define CREATE_TRACE_POINTS
> @@ -161,7 +162,7 @@ static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb,
> struct erofs_sb_info *sbi = EROFS_SB(sb);
> struct erofs_fscache *fscache;
> struct erofs_deviceslot *dis;
> - struct file *bdev_file;
> + struct file *file;
>
> dis = erofs_read_metabuf(buf, sb, *pos, EROFS_KMAP);
> if (IS_ERR(dis))
> @@ -183,13 +184,17 @@ static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb,
> return PTR_ERR(fscache);
> dif->fscache = fscache;
> } else if (!sbi->devs->flatdev) {
> - bdev_file = bdev_file_open_by_path(dif->path, BLK_OPEN_READ,
> - sb->s_type, NULL);
> - if (IS_ERR(bdev_file))
> - return PTR_ERR(bdev_file);
> - dif->bdev_file = bdev_file;
> - dif->dax_dev = fs_dax_get_by_bdev(file_bdev(bdev_file),
> - &dif->dax_part_off, NULL, NULL);
> + file = erofs_is_fileio_mode(sbi) ?
> + filp_open(dif->path, O_RDONLY | O_LARGEFILE, 0) :
> + bdev_file_open_by_path(dif->path,
> + BLK_OPEN_READ, sb->s_type, NULL);
> + if (IS_ERR(file))
> + return PTR_ERR(file);
> +
> + dif->file = file;
> + if (!erofs_is_fileio_mode(sbi))
> + dif->dax_dev = fs_dax_get_by_bdev(file_bdev(file),
> + &dif->dax_part_off, NULL, NULL);
> }
>
> dif->blocks = le32_to_cpu(dis->blocks);
> @@ -566,15 +571,16 @@ static void erofs_set_sysfs_name(struct super_block *sb)
> {
> struct erofs_sb_info *sbi = EROFS_SB(sb);
>
> - if (erofs_is_fscache_mode(sb)) {
> - if (sbi->domain_id)
> - super_set_sysfs_name_generic(sb, "%s,%s",sbi->domain_id,
> - sbi->fsid);
> - else
> - super_set_sysfs_name_generic(sb, "%s", sbi->fsid);
> - return;
> - }
> - super_set_sysfs_name_id(sb);
> + if (sbi->domain_id)
> + super_set_sysfs_name_generic(sb, "%s,%s", sbi->domain_id,
> + sbi->fsid);
> + else if (sbi->fsid)
> + super_set_sysfs_name_generic(sb, "%s", sbi->fsid);
> + else if (erofs_is_fileio_mode(sbi))
> + super_set_sysfs_name_generic(sb, "%s",
> + bdi_dev_name(sb->s_bdi));
> + else
> + super_set_sysfs_name_id(sb);
> }
>
> static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
> @@ -589,14 +595,15 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
> sb->s_op = &erofs_sops;
>
> sbi->blkszbits = PAGE_SHIFT;
> - if (erofs_is_fscache_mode(sb)) {
> + if (!sb->s_bdev) {
> sb->s_blocksize = PAGE_SIZE;
> sb->s_blocksize_bits = PAGE_SHIFT;
>
> - err = erofs_fscache_register_fs(sb);
> - if (err)
> - return err;
> -
> + if (erofs_is_fscache_mode(sb)) {
> + err = erofs_fscache_register_fs(sb);
> + if (err)
> + return err;
> + }
> err = super_setup_bdi(sb);
> if (err)
> return err;
> @@ -693,11 +700,24 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
> static int erofs_fc_get_tree(struct fs_context *fc)
> {
> struct erofs_sb_info *sbi = fc->s_fs_info;
> + int ret;
>
> if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && sbi->fsid)
> return get_tree_nodev(fc, erofs_fc_fill_super);
>
> - return get_tree_bdev(fc, erofs_fc_fill_super);
> + ret = get_tree_bdev(fc, erofs_fc_fill_super);
> +#ifdef CONFIG_EROFS_FS_BACKED_BY_FILE
> + if (ret == -ENOTBLK) {
> + if (!fc->source)
> + return invalf(fc, "No source specified");
> + sbi->fdev = filp_open(fc->source, O_RDONLY | O_LARGEFILE, 0);
> + if (IS_ERR(sbi->fdev))
> + return PTR_ERR(sbi->fdev);
> +
> + return get_tree_nodev(fc, erofs_fc_fill_super);
> + }
> +#endif
> + return ret;
> }
>
> static int erofs_fc_reconfigure(struct fs_context *fc)
> @@ -727,8 +747,8 @@ static int erofs_release_device_info(int id, void *ptr, void *data)
> struct erofs_device_info *dif = ptr;
>
> fs_put_dax(dif->dax_dev, NULL);
> - if (dif->bdev_file)
> - fput(dif->bdev_file);
> + if (dif->file)
> + fput(dif->file);
> erofs_fscache_unregister_cookie(dif->fscache);
> dif->fscache = NULL;
> kfree(dif->path);
> @@ -791,7 +811,7 @@ static void erofs_kill_sb(struct super_block *sb)
> {
> struct erofs_sb_info *sbi = EROFS_SB(sb);
>
> - if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && sbi->fsid)
> + if ((IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && sbi->fsid) || sbi->fdev)
> kill_anon_super(sb);
> else
> kill_block_super(sb);
> @@ -801,6 +821,8 @@ static void erofs_kill_sb(struct super_block *sb)
> erofs_fscache_unregister_fs(sb);
> kfree(sbi->fsid);
> kfree(sbi->domain_id);
> + if (sbi->fdev)
> + fput(sbi->fdev);
> kfree(sbi);
> sb->s_fs_info = NULL;
> }
> @@ -903,7 +925,7 @@ static int erofs_statfs(struct dentry *dentry, struct kstatfs *buf)
> buf->f_namelen = EROFS_NAME_LEN;
>
> if (uuid_is_null(&sb->s_uuid))
> - buf->f_fsid = u64_to_fsid(erofs_is_fscache_mode(sb) ? 0 :
> + buf->f_fsid = u64_to_fsid(!sb->s_bdev ? 0 :
> huge_encode_dev(sb->s_bdev->bd_dev));
> else
> buf->f_fsid = uuid_to_fsid(sb->s_uuid.b);
> --
> 2.43.5
>
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 2/4] erofs: support unencoded inodes for fileio
2024-08-31 0:19 ` Sandeep Dhavale
@ 2024-08-31 2:54 ` Gao Xiang
0 siblings, 0 replies; 30+ messages in thread
From: Gao Xiang @ 2024-08-31 2:54 UTC (permalink / raw)
To: Sandeep Dhavale; +Cc: linux-erofs, LKML
On 2024/8/31 08:19, Sandeep Dhavale wrote:
> On Thu, Aug 29, 2024 at 8:29 PM Gao Xiang <hsiangkao@linux.alibaba.com> wrote:
>>
>> Since EROFS only needs to handle read requests in simple contexts,
>> Just directly use vfs_iocb_iter_read() for data I/Os.
>>
>> Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
>> ---
>> v2:
>> - fix redundant refcount which cause hanging on chunked inodes.
>>
>> fs/erofs/Makefile | 1 +
>> fs/erofs/data.c | 50 +++++++++++-
>> fs/erofs/fileio.c | 181 ++++++++++++++++++++++++++++++++++++++++++++
>> fs/erofs/inode.c | 17 +++--
>> fs/erofs/internal.h | 7 +-
>> fs/erofs/zdata.c | 46 ++---------
>> 6 files changed, 251 insertions(+), 51 deletions(-)
>> create mode 100644 fs/erofs/fileio.c
>>
>> diff --git a/fs/erofs/Makefile b/fs/erofs/Makefile
>> index 097d672e6b14..4331d53c7109 100644
>> --- a/fs/erofs/Makefile
>> +++ b/fs/erofs/Makefile
>> @@ -7,4 +7,5 @@ erofs-$(CONFIG_EROFS_FS_ZIP) += decompressor.o zmap.o zdata.o zutil.o
>> erofs-$(CONFIG_EROFS_FS_ZIP_LZMA) += decompressor_lzma.o
>> erofs-$(CONFIG_EROFS_FS_ZIP_DEFLATE) += decompressor_deflate.o
>> erofs-$(CONFIG_EROFS_FS_ZIP_ZSTD) += decompressor_zstd.o
>> +erofs-$(CONFIG_EROFS_FS_BACKED_BY_FILE) += fileio.o
>> erofs-$(CONFIG_EROFS_FS_ONDEMAND) += fscache.o
>> diff --git a/fs/erofs/data.c b/fs/erofs/data.c
>> index 0fb31c588ae0..b4c07ce7a294 100644
>> --- a/fs/erofs/data.c
>> +++ b/fs/erofs/data.c
>> @@ -132,7 +132,7 @@ int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map)
>> if (map->m_la >= inode->i_size) {
>> /* leave out-of-bound access unmapped */
>> map->m_flags = 0;
>> - map->m_plen = 0;
>> + map->m_plen = map->m_llen;
>> goto out;
>> }
>>
>> @@ -197,8 +197,13 @@ static void erofs_fill_from_devinfo(struct erofs_map_dev *map,
>> struct erofs_device_info *dif)
>> {
>> map->m_bdev = NULL;
>> - if (dif->file && S_ISBLK(file_inode(dif->file)->i_mode))
>> - map->m_bdev = file_bdev(dif->file);
>> + map->m_fp = NULL;
>> + if (dif->file) {
>> + if (S_ISBLK(file_inode(dif->file)->i_mode))
>> + map->m_bdev = file_bdev(dif->file);
>> + else
>> + map->m_fp = dif->file;
>> + }
>> map->m_daxdev = dif->dax_dev;
>> map->m_dax_part_off = dif->dax_part_off;
>> map->m_fscache = dif->fscache;
>> @@ -215,6 +220,7 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)
>> map->m_daxdev = EROFS_SB(sb)->dax_dev;
>> map->m_dax_part_off = EROFS_SB(sb)->dax_part_off;
>> map->m_fscache = EROFS_SB(sb)->s_fscache;
>> + map->m_fp = EROFS_SB(sb)->fdev;
>>
>> if (map->m_deviceid) {
>> down_read(&devs->rwsem);
>> @@ -250,6 +256,42 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)
>> return 0;
>> }
>>
>> +/*
>> + * bit 30: I/O error occurred on this folio
>> + * bit 0 - 29: remaining parts to complete this folio
>> + */
>> +#define EROFS_ONLINEFOLIO_EIO (1 << 30)
>> +
>> +void erofs_onlinefolio_init(struct folio *folio)
>> +{
>> + union {
>> + atomic_t o;
>> + void *v;
>> + } u = { .o = ATOMIC_INIT(1) };
>> +
>> + folio->private = u.v; /* valid only if file-backed folio is locked */
>> +}
>> +
>> +void erofs_onlinefolio_split(struct folio *folio)
>> +{
>> + atomic_inc((atomic_t *)&folio->private);
>> +}
>> +
>> +void erofs_onlinefolio_end(struct folio *folio, int err)
>> +{
>> + int orig, v;
>> +
>> + do {
>> + orig = atomic_read((atomic_t *)&folio->private);
>> + v = (orig - 1) | (err ? EROFS_ONLINEFOLIO_EIO : 0);
>> + } while (atomic_cmpxchg((atomic_t *)&folio->private, orig, v) != orig);
>> +
>> + if (v & ~EROFS_ONLINEFOLIO_EIO)
>> + return;
>> + folio->private = 0;
>> + folio_end_read(folio, !(v & EROFS_ONLINEFOLIO_EIO));
>> +}
>> +
>> static int erofs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
>> unsigned int flags, struct iomap *iomap, struct iomap *srcmap)
>> {
>> @@ -399,7 +441,7 @@ static ssize_t erofs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
>> }
>>
>> /* for uncompressed (aligned) files and raw access for other files */
>> -const struct address_space_operations erofs_raw_access_aops = {
>> +const struct address_space_operations erofs_aops = {
>> .read_folio = erofs_read_folio,
>> .readahead = erofs_readahead,
>> .bmap = erofs_bmap,
>> diff --git a/fs/erofs/fileio.c b/fs/erofs/fileio.c
>> new file mode 100644
>> index 000000000000..eab52b8abd0b
>> --- /dev/null
>> +++ b/fs/erofs/fileio.c
>> @@ -0,0 +1,181 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/*
>> + * Copyright (C) 2024, Alibaba Cloud
>> + */
>> +#include "internal.h"
>> +#include <trace/events/erofs.h>
>> +
>> +struct erofs_fileio_rq {
>> + struct bio_vec bvecs[BIO_MAX_VECS];
>> + struct bio bio;
>> + struct kiocb iocb;
>> +};
>> +
>> +struct erofs_fileio {
>> + struct erofs_map_blocks map;
>> + struct erofs_map_dev dev;
>> + struct erofs_fileio_rq *rq;
>> +};
>> +
>> +static void erofs_fileio_ki_complete(struct kiocb *iocb, long ret)
>> +{
>> + struct erofs_fileio_rq *rq =
>> + container_of(iocb, struct erofs_fileio_rq, iocb);
>> + struct folio_iter fi;
>> +
>> + DBG_BUGON(rq->bio.bi_end_io);
>> + if (ret > 0) {
>> + if (ret != rq->bio.bi_iter.bi_size) {
>> + bio_advance(&rq->bio, ret);
>> + zero_fill_bio(&rq->bio);
>> + }
>> + ret = 0;
>> + }
>> + bio_for_each_folio_all(fi, &rq->bio) {
>> + DBG_BUGON(folio_test_uptodate(fi.folio));
>> + erofs_onlinefolio_end(fi.folio, ret);
>> + }
>> + kfree(rq);
>> +}
>> +
>> +static void erofs_fileio_rq_submit(struct erofs_fileio_rq *rq)
>> +{
>> + struct iov_iter iter;
>> + int ret;
>> +
>> + if (!rq)
>> + return;
>> + rq->iocb.ki_pos = rq->bio.bi_iter.bi_sector << 9;
>> + rq->iocb.ki_ioprio = get_current_ioprio();
>> + rq->iocb.ki_complete = erofs_fileio_ki_complete;
>> + rq->iocb.ki_flags = (rq->iocb.ki_filp->f_mode & FMODE_CAN_ODIRECT) ?
>> + IOCB_DIRECT : 0;
> Hi Gao,
> Does this mean, direct IO by default if the backing file supports it
> (technically filesystem where image/backing file reside)?
Yes.
Thanks,
Gao Xiang
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 1/4] erofs: add file-backed mount support
2024-08-31 0:30 ` [PATCH v2 1/4] erofs: add file-backed mount support Sandeep Dhavale
@ 2024-08-31 3:00 ` Gao Xiang
0 siblings, 0 replies; 30+ messages in thread
From: Gao Xiang @ 2024-08-31 3:00 UTC (permalink / raw)
To: Sandeep Dhavale; +Cc: kernel-team, linux-erofs, LKML
Hi Sandeep,
On 2024/8/31 08:30, Sandeep Dhavale via Linux-erofs wrote:
> On Thu, Aug 29, 2024 at 8:29 PM Gao Xiang <hsiangkao@linux.alibaba.com> wrote:
>>
>> It actually has been around for years: For containers and other sandbox
>> use cases, there will be thousands (and even more) of authenticated
>> (sub)images running on the same host, unlike OS images.
>>
>> Of course, all scenarios can use the same EROFS on-disk format, but
>> bdev-backed mounts just work well for OS images since golden data is
>> dumped into real block devices. However, it's somewhat hard for
>> container runtimes to manage and isolate so many unnecessary virtual
>> block devices safely and efficiently [1]: they just look like a burden
>> to orchestrators and file-backed mounts are preferred indeed. There
>> were already enough attempts such as Incremental FS, the original
>> ComposeFS and PuzzleFS acting in the same way for immutable fses. As
>> for current EROFS users, ComposeFS, containerd and Android APEXs will
>> be directly benefited from it.
>>
> Hi Gao,
> Thank you for the series! This is an interesting idea and will
> definitely help the Android ecosystem for APEXes if we can remove the
> loopback device. I will take a deeper look and provide comments soon.
Yes, I've seen no issue so far, and it will be submited for 6.12
since there are many users which really need this and wait for it.
Thanks,
Gao Xiang
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 2/4] erofs: support unencoded inodes for fileio
2024-08-30 3:28 ` [PATCH v2 2/4] erofs: support unencoded inodes for fileio Gao Xiang
2024-08-31 0:19 ` Sandeep Dhavale
@ 2024-08-31 4:25 ` Sandeep Dhavale
2024-08-31 7:54 ` Gao Xiang
2024-09-04 6:56 ` Gao Xiang
2024-09-05 9:01 ` Chao Yu
3 siblings, 1 reply; 30+ messages in thread
From: Sandeep Dhavale @ 2024-08-31 4:25 UTC (permalink / raw)
To: Gao Xiang; +Cc: linux-erofs, LKML
On Thu, Aug 29, 2024 at 8:29 PM Gao Xiang <hsiangkao@linux.alibaba.com> wrote:
>
> Since EROFS only needs to handle read requests in simple contexts,
> Just directly use vfs_iocb_iter_read() for data I/Os.
>
> Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
> ---
> v2:
> - fix redundant refcount which cause hanging on chunked inodes.
>
> fs/erofs/Makefile | 1 +
> fs/erofs/data.c | 50 +++++++++++-
> fs/erofs/fileio.c | 181 ++++++++++++++++++++++++++++++++++++++++++++
> fs/erofs/inode.c | 17 +++--
> fs/erofs/internal.h | 7 +-
> fs/erofs/zdata.c | 46 ++---------
> 6 files changed, 251 insertions(+), 51 deletions(-)
> create mode 100644 fs/erofs/fileio.c
>
> diff --git a/fs/erofs/Makefile b/fs/erofs/Makefile
> index 097d672e6b14..4331d53c7109 100644
> --- a/fs/erofs/Makefile
> +++ b/fs/erofs/Makefile
> @@ -7,4 +7,5 @@ erofs-$(CONFIG_EROFS_FS_ZIP) += decompressor.o zmap.o zdata.o zutil.o
> erofs-$(CONFIG_EROFS_FS_ZIP_LZMA) += decompressor_lzma.o
> erofs-$(CONFIG_EROFS_FS_ZIP_DEFLATE) += decompressor_deflate.o
> erofs-$(CONFIG_EROFS_FS_ZIP_ZSTD) += decompressor_zstd.o
> +erofs-$(CONFIG_EROFS_FS_BACKED_BY_FILE) += fileio.o
> erofs-$(CONFIG_EROFS_FS_ONDEMAND) += fscache.o
> diff --git a/fs/erofs/data.c b/fs/erofs/data.c
> index 0fb31c588ae0..b4c07ce7a294 100644
> --- a/fs/erofs/data.c
> +++ b/fs/erofs/data.c
> @@ -132,7 +132,7 @@ int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map)
> if (map->m_la >= inode->i_size) {
> /* leave out-of-bound access unmapped */
> map->m_flags = 0;
> - map->m_plen = 0;
> + map->m_plen = map->m_llen;
Hi Gao,
Is this change intentional? At out label, we set this again as err is 0.
> goto out;
> }
>
> @@ -197,8 +197,13 @@ static void erofs_fill_from_devinfo(struct erofs_map_dev *map,
> struct erofs_device_info *dif)
> {
> map->m_bdev = NULL;
> - if (dif->file && S_ISBLK(file_inode(dif->file)->i_mode))
> - map->m_bdev = file_bdev(dif->file);
> + map->m_fp = NULL;
> + if (dif->file) {
> + if (S_ISBLK(file_inode(dif->file)->i_mode))
> + map->m_bdev = file_bdev(dif->file);
> + else
> + map->m_fp = dif->file;
> + }
> map->m_daxdev = dif->dax_dev;
> map->m_dax_part_off = dif->dax_part_off;
> map->m_fscache = dif->fscache;
> @@ -215,6 +220,7 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)
> map->m_daxdev = EROFS_SB(sb)->dax_dev;
> map->m_dax_part_off = EROFS_SB(sb)->dax_part_off;
> map->m_fscache = EROFS_SB(sb)->s_fscache;
> + map->m_fp = EROFS_SB(sb)->fdev;
>
> if (map->m_deviceid) {
> down_read(&devs->rwsem);
> @@ -250,6 +256,42 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)
> return 0;
> }
>
> +/*
> + * bit 30: I/O error occurred on this folio
> + * bit 0 - 29: remaining parts to complete this folio
> + */
> +#define EROFS_ONLINEFOLIO_EIO (1 << 30)
> +
> +void erofs_onlinefolio_init(struct folio *folio)
> +{
> + union {
> + atomic_t o;
> + void *v;
> + } u = { .o = ATOMIC_INIT(1) };
> +
> + folio->private = u.v; /* valid only if file-backed folio is locked */
> +}
> +
> +void erofs_onlinefolio_split(struct folio *folio)
> +{
> + atomic_inc((atomic_t *)&folio->private);
> +}
> +
> +void erofs_onlinefolio_end(struct folio *folio, int err)
> +{
> + int orig, v;
> +
> + do {
> + orig = atomic_read((atomic_t *)&folio->private);
> + v = (orig - 1) | (err ? EROFS_ONLINEFOLIO_EIO : 0);
> + } while (atomic_cmpxchg((atomic_t *)&folio->private, orig, v) != orig);
> +
> + if (v & ~EROFS_ONLINEFOLIO_EIO)
> + return;
> + folio->private = 0;
> + folio_end_read(folio, !(v & EROFS_ONLINEFOLIO_EIO));
> +}
> +
> static int erofs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
> unsigned int flags, struct iomap *iomap, struct iomap *srcmap)
> {
> @@ -399,7 +441,7 @@ static ssize_t erofs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
> }
>
> /* for uncompressed (aligned) files and raw access for other files */
> -const struct address_space_operations erofs_raw_access_aops = {
> +const struct address_space_operations erofs_aops = {
> .read_folio = erofs_read_folio,
> .readahead = erofs_readahead,
> .bmap = erofs_bmap,
> diff --git a/fs/erofs/fileio.c b/fs/erofs/fileio.c
> new file mode 100644
> index 000000000000..eab52b8abd0b
> --- /dev/null
> +++ b/fs/erofs/fileio.c
> @@ -0,0 +1,181 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2024, Alibaba Cloud
> + */
> +#include "internal.h"
> +#include <trace/events/erofs.h>
> +
> +struct erofs_fileio_rq {
> + struct bio_vec bvecs[BIO_MAX_VECS];
> + struct bio bio;
> + struct kiocb iocb;
> +};
> +
> +struct erofs_fileio {
> + struct erofs_map_blocks map;
> + struct erofs_map_dev dev;
> + struct erofs_fileio_rq *rq;
> +};
> +
> +static void erofs_fileio_ki_complete(struct kiocb *iocb, long ret)
> +{
> + struct erofs_fileio_rq *rq =
> + container_of(iocb, struct erofs_fileio_rq, iocb);
> + struct folio_iter fi;
> +
> + DBG_BUGON(rq->bio.bi_end_io);
> + if (ret > 0) {
> + if (ret != rq->bio.bi_iter.bi_size) {
> + bio_advance(&rq->bio, ret);
> + zero_fill_bio(&rq->bio);
> + }
> + ret = 0;
> + }
> + bio_for_each_folio_all(fi, &rq->bio) {
> + DBG_BUGON(folio_test_uptodate(fi.folio));
> + erofs_onlinefolio_end(fi.folio, ret);
> + }
> + kfree(rq);
> +}
> +
> +static void erofs_fileio_rq_submit(struct erofs_fileio_rq *rq)
> +{
> + struct iov_iter iter;
> + int ret;
> +
> + if (!rq)
> + return;
> + rq->iocb.ki_pos = rq->bio.bi_iter.bi_sector << 9;
> + rq->iocb.ki_ioprio = get_current_ioprio();
> + rq->iocb.ki_complete = erofs_fileio_ki_complete;
> + rq->iocb.ki_flags = (rq->iocb.ki_filp->f_mode & FMODE_CAN_ODIRECT) ?
> + IOCB_DIRECT : 0;
> + iov_iter_bvec(&iter, ITER_DEST, rq->bvecs, rq->bio.bi_vcnt,
> + rq->bio.bi_iter.bi_size);
> + ret = vfs_iocb_iter_read(rq->iocb.ki_filp, &rq->iocb, &iter);
> + if (ret != -EIOCBQUEUED)
> + erofs_fileio_ki_complete(&rq->iocb, ret);
> +}
> +
> +static struct erofs_fileio_rq *erofs_fileio_rq_alloc(struct erofs_map_dev *mdev)
> +{
> + struct erofs_fileio_rq *rq = kzalloc(sizeof(*rq), GFP_KERNEL);
> +
> + if (!rq)
> + return NULL;
> + bio_init(&rq->bio, NULL, rq->bvecs, BIO_MAX_VECS, REQ_OP_READ);
> + rq->iocb.ki_filp = mdev->m_fp;
> + return rq;
> +}
> +
> +static int erofs_fileio_scan_folio(struct erofs_fileio *io, struct folio *folio)
> +{
> + struct inode *inode = folio_inode(folio);
> + struct erofs_map_blocks *map = &io->map;
> + unsigned int cur = 0, end = folio_size(folio), len, attached = 0;
> + loff_t pos = folio_pos(folio), ofs;
> + struct iov_iter iter;
> + struct bio_vec bv;
> + int err = 0;
> +
> + erofs_onlinefolio_init(folio);
> + while (cur < end) {
> + if (!in_range(pos + cur, map->m_la, map->m_llen)) {
> + map->m_la = pos + cur;
> + map->m_llen = end - cur;
> + err = erofs_map_blocks(inode, map);
> + if (err)
> + break;
> + }
> +
> + ofs = folio_pos(folio) + cur - map->m_la;
> + len = min_t(loff_t, map->m_llen - ofs, end - cur);
> + if (map->m_flags & EROFS_MAP_META) {
> + struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
> + void *src;
> +
> + src = erofs_read_metabuf(&buf, inode->i_sb,
> + map->m_pa + ofs, EROFS_KMAP);
> + if (IS_ERR(src)) {
> + err = PTR_ERR(src);
> + break;
> + }
> + bvec_set_folio(&bv, folio, len, cur);
> + iov_iter_bvec(&iter, ITER_DEST, &bv, 1, len);
> + if (copy_to_iter(src, len, &iter) != len) {
> + erofs_put_metabuf(&buf);
> + err = -EIO;
> + break;
> + }
> + erofs_put_metabuf(&buf);
> + } else if (!(map->m_flags & EROFS_MAP_MAPPED)) {
> + folio_zero_segment(folio, cur, cur + len);
> + } else {
> + if (io->rq && (map->m_pa + ofs != io->dev.m_pa ||
> + map->m_deviceid != io->dev.m_deviceid)) {
> +io_retry:
> + erofs_fileio_rq_submit(io->rq);
> + io->rq = NULL;
> + }
> +
> + if (!io->rq) {
> + io->dev = (struct erofs_map_dev) {
> + .m_pa = io->map.m_pa + ofs,
> + .m_deviceid = io->map.m_deviceid,
> + };
> + err = erofs_map_dev(inode->i_sb, &io->dev);
> + if (err)
> + break;
> + io->rq = erofs_fileio_rq_alloc(&io->dev);
> + if (!io->rq) {
> + err = -ENOMEM;
> + break;
> + }
> + io->rq->bio.bi_iter.bi_sector = io->dev.m_pa >> 9;
> + attached = 0;
> + }
> + if (!attached++)
> + erofs_onlinefolio_split(folio);
> + if (!bio_add_folio(&io->rq->bio, folio, len, cur))
> + goto io_retry;
> + io->dev.m_pa += len;
> + }
> + cur += len;
> + }
> + erofs_onlinefolio_end(folio, err);
> + return err;
> +}
> +
> +static int erofs_fileio_read_folio(struct file *file, struct folio *folio)
> +{
> + struct erofs_fileio io = {};
> + int err;
> +
> + trace_erofs_read_folio(folio, false);
> + err = erofs_fileio_scan_folio(&io, folio);
> + erofs_fileio_rq_submit(io.rq);
> + return err;
> +}
> +
> +static void erofs_fileio_readahead(struct readahead_control *rac)
> +{
> + struct inode *inode = rac->mapping->host;
> + struct erofs_fileio io = {};
> + struct folio *folio;
> + int err;
> +
> + trace_erofs_readpages(inode, readahead_index(rac),
> + readahead_count(rac), false);
> + while ((folio = readahead_folio(rac))) {
> + err = erofs_fileio_scan_folio(&io, folio);
> + if (err && err != -EINTR)
> + erofs_err(inode->i_sb, "readahead error at folio %lu @ nid %llu",
> + folio->index, EROFS_I(inode)->nid);
> + }
> + erofs_fileio_rq_submit(io.rq);
> +}
> +
> +const struct address_space_operations erofs_fileio_aops = {
> + .read_folio = erofs_fileio_read_folio,
> + .readahead = erofs_fileio_readahead,
> +};
> diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c
> index d05b9e59f122..4a902e6e69a5 100644
> --- a/fs/erofs/inode.c
> +++ b/fs/erofs/inode.c
> @@ -258,11 +258,14 @@ static int erofs_fill_inode(struct inode *inode)
> }
>
> mapping_set_large_folios(inode->i_mapping);
> - if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb))) {
> - /* XXX: data I/Os will be implemented in the following patches */
> - err = -EOPNOTSUPP;
> - } else if (erofs_inode_is_data_compressed(vi->datalayout)) {
> + if (erofs_inode_is_data_compressed(vi->datalayout)) {
> #ifdef CONFIG_EROFS_FS_ZIP
> +#ifdef CONFIG_EROFS_FS_BACKED_BY_FILE
> + if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb))) {
> + err = -EOPNOTSUPP;
> + goto out_unlock;
> + }
> +#endif
> DO_ONCE_LITE_IF(inode->i_blkbits != PAGE_SHIFT,
> erofs_info, inode->i_sb,
> "EXPERIMENTAL EROFS subpage compressed block support in use. Use at your own risk!");
> @@ -271,10 +274,14 @@ static int erofs_fill_inode(struct inode *inode)
> err = -EOPNOTSUPP;
> #endif
> } else {
> - inode->i_mapping->a_ops = &erofs_raw_access_aops;
> + inode->i_mapping->a_ops = &erofs_aops;
> #ifdef CONFIG_EROFS_FS_ONDEMAND
> if (erofs_is_fscache_mode(inode->i_sb))
> inode->i_mapping->a_ops = &erofs_fscache_access_aops;
> +#endif
> +#ifdef CONFIG_EROFS_FS_BACKED_BY_FILE
> + if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb)))
> + inode->i_mapping->a_ops = &erofs_fileio_aops;
> #endif
> }
> out_unlock:
> diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
> index 9bf4fb1cfa09..9bc4dcfd06d7 100644
> --- a/fs/erofs/internal.h
> +++ b/fs/erofs/internal.h
> @@ -372,6 +372,7 @@ struct erofs_map_dev {
> struct erofs_fscache *m_fscache;
> struct block_device *m_bdev;
> struct dax_device *m_daxdev;
> + struct file *m_fp;
> u64 m_dax_part_off;
>
> erofs_off_t m_pa;
> @@ -380,7 +381,8 @@ struct erofs_map_dev {
>
> extern const struct super_operations erofs_sops;
>
> -extern const struct address_space_operations erofs_raw_access_aops;
> +extern const struct address_space_operations erofs_aops;
> +extern const struct address_space_operations erofs_fileio_aops;
> extern const struct address_space_operations z_erofs_aops;
> extern const struct address_space_operations erofs_fscache_access_aops;
>
> @@ -411,6 +413,9 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *dev);
> int erofs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
> u64 start, u64 len);
> int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map);
> +void erofs_onlinefolio_init(struct folio *folio);
> +void erofs_onlinefolio_split(struct folio *folio);
> +void erofs_onlinefolio_end(struct folio *folio, int err);
> struct inode *erofs_iget(struct super_block *sb, erofs_nid_t nid);
> int erofs_getattr(struct mnt_idmap *idmap, const struct path *path,
> struct kstat *stat, u32 request_mask,
> diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
> index 424f656cd765..350612f32ac6 100644
> --- a/fs/erofs/zdata.c
> +++ b/fs/erofs/zdata.c
> @@ -122,42 +122,6 @@ static bool erofs_folio_is_managed(struct erofs_sb_info *sbi, struct folio *fo)
> return fo->mapping == MNGD_MAPPING(sbi);
> }
>
> -/*
> - * bit 30: I/O error occurred on this folio
> - * bit 0 - 29: remaining parts to complete this folio
> - */
> -#define Z_EROFS_FOLIO_EIO (1 << 30)
> -
> -static void z_erofs_onlinefolio_init(struct folio *folio)
> -{
> - union {
> - atomic_t o;
> - void *v;
> - } u = { .o = ATOMIC_INIT(1) };
> -
> - folio->private = u.v; /* valid only if file-backed folio is locked */
> -}
> -
> -static void z_erofs_onlinefolio_split(struct folio *folio)
> -{
> - atomic_inc((atomic_t *)&folio->private);
> -}
> -
> -static void z_erofs_onlinefolio_end(struct folio *folio, int err)
> -{
> - int orig, v;
> -
> - do {
> - orig = atomic_read((atomic_t *)&folio->private);
> - v = (orig - 1) | (err ? Z_EROFS_FOLIO_EIO : 0);
> - } while (atomic_cmpxchg((atomic_t *)&folio->private, orig, v) != orig);
> -
> - if (v & ~Z_EROFS_FOLIO_EIO)
> - return;
> - folio->private = 0;
> - folio_end_read(folio, !(v & Z_EROFS_FOLIO_EIO));
> -}
> -
> #define Z_EROFS_ONSTACK_PAGES 32
>
> /*
> @@ -965,7 +929,7 @@ static int z_erofs_scan_folio(struct z_erofs_decompress_frontend *f,
> int err = 0;
>
> tight = (bs == PAGE_SIZE);
> - z_erofs_onlinefolio_init(folio);
> + erofs_onlinefolio_init(folio);
> do {
> if (offset + end - 1 < map->m_la ||
> offset + end - 1 >= map->m_la + map->m_llen) {
> @@ -1024,7 +988,7 @@ static int z_erofs_scan_folio(struct z_erofs_decompress_frontend *f,
> if (err)
> break;
>
> - z_erofs_onlinefolio_split(folio);
> + erofs_onlinefolio_split(folio);
> if (f->pcl->pageofs_out != (map->m_la & ~PAGE_MASK))
> f->pcl->multibases = true;
> if (f->pcl->length < offset + end - map->m_la) {
> @@ -1044,7 +1008,7 @@ static int z_erofs_scan_folio(struct z_erofs_decompress_frontend *f,
> tight = (bs == PAGE_SIZE);
> }
> } while ((end = cur) > 0);
> - z_erofs_onlinefolio_end(folio, err);
> + erofs_onlinefolio_end(folio, err);
> return err;
> }
>
> @@ -1147,7 +1111,7 @@ static void z_erofs_fill_other_copies(struct z_erofs_decompress_backend *be,
> cur += len;
> }
> kunmap_local(dst);
> - z_erofs_onlinefolio_end(page_folio(bvi->bvec.page), err);
> + erofs_onlinefolio_end(page_folio(bvi->bvec.page), err);
> list_del(p);
> kfree(bvi);
> }
> @@ -1302,7 +1266,7 @@ static int z_erofs_decompress_pcluster(struct z_erofs_decompress_backend *be,
>
> DBG_BUGON(z_erofs_page_is_invalidated(page));
> if (!z_erofs_is_shortlived_page(page)) {
> - z_erofs_onlinefolio_end(page_folio(page), err);
> + erofs_onlinefolio_end(page_folio(page), err);
> continue;
> }
> if (pcl->algorithmformat != Z_EROFS_COMPRESSION_LZ4) {
> --
> 2.43.5
>
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 2/4] erofs: support unencoded inodes for fileio
2024-08-31 4:25 ` Sandeep Dhavale
@ 2024-08-31 7:54 ` Gao Xiang
2024-09-01 20:47 ` Sandeep Dhavale
0 siblings, 1 reply; 30+ messages in thread
From: Gao Xiang @ 2024-08-31 7:54 UTC (permalink / raw)
To: Sandeep Dhavale; +Cc: linux-erofs, LKML
On 2024/8/31 12:25, Sandeep Dhavale wrote:
> On Thu, Aug 29, 2024 at 8:29 PM Gao Xiang <hsiangkao@linux.alibaba.com> wrote:
>>
>> Since EROFS only needs to handle read requests in simple contexts,
>> Just directly use vfs_iocb_iter_read() for data I/Os.
>>
>> Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
>> ---
>> v2:
>> - fix redundant refcount which cause hanging on chunked inodes.
>>
>> fs/erofs/Makefile | 1 +
>> fs/erofs/data.c | 50 +++++++++++-
>> fs/erofs/fileio.c | 181 ++++++++++++++++++++++++++++++++++++++++++++
>> fs/erofs/inode.c | 17 +++--
>> fs/erofs/internal.h | 7 +-
>> fs/erofs/zdata.c | 46 ++---------
>> 6 files changed, 251 insertions(+), 51 deletions(-)
>> create mode 100644 fs/erofs/fileio.c
>>
>> diff --git a/fs/erofs/Makefile b/fs/erofs/Makefile
>> index 097d672e6b14..4331d53c7109 100644
>> --- a/fs/erofs/Makefile
>> +++ b/fs/erofs/Makefile
>> @@ -7,4 +7,5 @@ erofs-$(CONFIG_EROFS_FS_ZIP) += decompressor.o zmap.o zdata.o zutil.o
>> erofs-$(CONFIG_EROFS_FS_ZIP_LZMA) += decompressor_lzma.o
>> erofs-$(CONFIG_EROFS_FS_ZIP_DEFLATE) += decompressor_deflate.o
>> erofs-$(CONFIG_EROFS_FS_ZIP_ZSTD) += decompressor_zstd.o
>> +erofs-$(CONFIG_EROFS_FS_BACKED_BY_FILE) += fileio.o
>> erofs-$(CONFIG_EROFS_FS_ONDEMAND) += fscache.o
>> diff --git a/fs/erofs/data.c b/fs/erofs/data.c
>> index 0fb31c588ae0..b4c07ce7a294 100644
>> --- a/fs/erofs/data.c
>> +++ b/fs/erofs/data.c
>> @@ -132,7 +132,7 @@ int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map)
>> if (map->m_la >= inode->i_size) {
>> /* leave out-of-bound access unmapped */
>> map->m_flags = 0;
>> - map->m_plen = 0;
>> + map->m_plen = map->m_llen;
> Hi Gao,
> Is this change intentional? At out label, we set this again as err is 0.
Yes, that is intentional, we will return the post-EOF extent as
m_plen == map->m_llen == passed in m_llen and an unmapped extent
so that the logic can be simplified and it's still compatible.
Thanks,
Gao Xiang
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 1/4] erofs: add file-backed mount support
2024-08-30 3:28 [PATCH v2 1/4] erofs: add file-backed mount support Gao Xiang
` (3 preceding siblings ...)
2024-08-31 0:30 ` [PATCH v2 1/4] erofs: add file-backed mount support Sandeep Dhavale
@ 2024-09-01 20:46 ` Sandeep Dhavale
2024-09-05 8:22 ` Chao Yu
` (2 subsequent siblings)
7 siblings, 0 replies; 30+ messages in thread
From: Sandeep Dhavale @ 2024-09-01 20:46 UTC (permalink / raw)
To: Gao Xiang; +Cc: linux-erofs, LKML, kernel-team
Reviewed-by: Sandeep Dhavale <dhavale@google.com>
Thanks,
Sandeep.
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 2/4] erofs: support unencoded inodes for fileio
2024-08-31 7:54 ` Gao Xiang
@ 2024-09-01 20:47 ` Sandeep Dhavale
0 siblings, 0 replies; 30+ messages in thread
From: Sandeep Dhavale @ 2024-09-01 20:47 UTC (permalink / raw)
To: Gao Xiang; +Cc: linux-erofs, LKML, kernel-team
Reviewed-by: Sandeep Dhavale <dhavale@google.com>
Thanks,
Sandeep.
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 3/4] erofs: support compressed inodes for fileio
2024-08-30 3:28 ` [PATCH v2 3/4] erofs: support compressed " Gao Xiang
@ 2024-09-01 20:47 ` Sandeep Dhavale
2024-09-05 9:01 ` Chao Yu
2024-09-05 9:46 ` Chao Yu
2 siblings, 0 replies; 30+ messages in thread
From: Sandeep Dhavale @ 2024-09-01 20:47 UTC (permalink / raw)
To: Gao Xiang; +Cc: linux-erofs, LKML
Reviewed-by: Sandeep Dhavale <dhavale@google.com>
Thanks,
Sandeep.
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 4/4] erofs: mark experimental fscache backend deprecated
2024-08-30 3:28 ` [PATCH v2 4/4] erofs: mark experimental fscache backend deprecated Gao Xiang
@ 2024-09-01 20:48 ` Sandeep Dhavale
2024-09-05 9:03 ` Chao Yu
1 sibling, 0 replies; 30+ messages in thread
From: Sandeep Dhavale @ 2024-09-01 20:48 UTC (permalink / raw)
To: Gao Xiang; +Cc: linux-erofs, LKML, kernel-team
Reviewed-by: Sandeep Dhavale <dhavale@google.com>
Thanks,
Sandeep.
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 2/4] erofs: support unencoded inodes for fileio
2024-08-30 3:28 ` [PATCH v2 2/4] erofs: support unencoded inodes for fileio Gao Xiang
2024-08-31 0:19 ` Sandeep Dhavale
2024-08-31 4:25 ` Sandeep Dhavale
@ 2024-09-04 6:56 ` Gao Xiang
2024-09-05 9:01 ` Chao Yu
3 siblings, 0 replies; 30+ messages in thread
From: Gao Xiang @ 2024-09-04 6:56 UTC (permalink / raw)
To: linux-erofs; +Cc: LKML
On 2024/8/30 11:28, Gao Xiang wrote:
> Since EROFS only needs to handle read requests in simple contexts,
> Just directly use vfs_iocb_iter_read() for data I/Os.
>
> Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Unmapped extent could actually split, already fixed as below:
diff --git a/fs/erofs/fileio.c b/fs/erofs/fileio.c
index 598b865ae25f..7f82238047e6 100644
--- a/fs/erofs/fileio.c
+++ b/fs/erofs/fileio.c
@@ -127,6 +127,7 @@ static int erofs_fileio_scan_folio(struct erofs_fileio *io, struct folio *folio)
erofs_put_metabuf(&buf);
} else if (!(map->m_flags & EROFS_MAP_MAPPED)) {
folio_zero_segment(folio, cur, cur + len);
+ attached = 0;
} else {
if (io->rq && (map->m_pa + ofs != io->dev.m_pa ||
map->m_deviceid != io->dev.m_deviceid)) {
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 1/4] erofs: add file-backed mount support
2024-08-30 3:28 [PATCH v2 1/4] erofs: add file-backed mount support Gao Xiang
` (4 preceding siblings ...)
2024-09-01 20:46 ` Sandeep Dhavale
@ 2024-09-05 8:22 ` Chao Yu
2024-09-05 9:30 ` [PATCH v3 2/4] erofs: support unencoded inodes for fileio Gao Xiang
2024-09-24 9:21 ` [PATCH v2 1/4] erofs: add file-backed mount support Geert Uytterhoeven
7 siblings, 0 replies; 30+ messages in thread
From: Chao Yu @ 2024-09-05 8:22 UTC (permalink / raw)
To: Gao Xiang, linux-erofs; +Cc: LKML
On 2024/8/30 11:28, Gao Xiang wrote:
> It actually has been around for years: For containers and other sandbox
> use cases, there will be thousands (and even more) of authenticated
> (sub)images running on the same host, unlike OS images.
>
> Of course, all scenarios can use the same EROFS on-disk format, but
> bdev-backed mounts just work well for OS images since golden data is
> dumped into real block devices. However, it's somewhat hard for
> container runtimes to manage and isolate so many unnecessary virtual
> block devices safely and efficiently [1]: they just look like a burden
> to orchestrators and file-backed mounts are preferred indeed. There
> were already enough attempts such as Incremental FS, the original
> ComposeFS and PuzzleFS acting in the same way for immutable fses. As
> for current EROFS users, ComposeFS, containerd and Android APEXs will
> be directly benefited from it.
>
> On the other hand, previous experimental feature "erofs over fscache"
> was once also intended to provide a similar solution (inspired by
> Incremental FS discussion [2]), but the following facts show file-backed
> mounts will be a better approach:
> - Fscache infrastructure has recently been moved into new Netfslib
> which is an unexpected dependency to EROFS really, although it
> originally claims "it could be used for caching other things such as
> ISO9660 filesystems too." [3]
>
> - It takes an unexpectedly long time to upstream Fscache/Cachefiles
> enhancements. For example, the failover feature took more than
> one year, and the deamonless feature is still far behind now;
>
> - Ongoing HSM "fanotify pre-content hooks" [4] together with this will
> perfectly supersede "erofs over fscache" in a simpler way since
> developers (mainly containerd folks) could leverage their existing
> caching mechanism entirely in userspace instead of strictly following
> the predefined in-kernel caching tree hierarchy.
>
> After "fanotify pre-content hooks" lands upstream to provide the same
> functionality, "erofs over fscache" will be removed then (as an EROFS
> internal improvement and EROFS will not have to bother with on-demand
> fetching and/or caching improvements anymore.)
>
> [1] https://github.com/containers/storage/pull/2039
> [2] https://lore.kernel.org/r/CAOQ4uxjbVxnubaPjVaGYiSwoGDTdpWbB=w_AeM6YM=zVixsUfQ@mail.gmail.com
> [3] https://docs.kernel.org/filesystems/caching/fscache.html
> [4] https://lore.kernel.org/r/cover.1723670362.git.josef@toxicpanda.com
>
> Closes: https://github.com/containers/composefs/issues/144
> Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Reviewed-by: Chao Yu <chao@kernel.org>
Thanks,
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 2/4] erofs: support unencoded inodes for fileio
2024-08-30 3:28 ` [PATCH v2 2/4] erofs: support unencoded inodes for fileio Gao Xiang
` (2 preceding siblings ...)
2024-09-04 6:56 ` Gao Xiang
@ 2024-09-05 9:01 ` Chao Yu
2024-09-05 9:13 ` Gao Xiang
3 siblings, 1 reply; 30+ messages in thread
From: Chao Yu @ 2024-09-05 9:01 UTC (permalink / raw)
To: Gao Xiang, linux-erofs; +Cc: LKML
On 2024/8/30 11:28, Gao Xiang wrote:
> Since EROFS only needs to handle read requests in simple contexts,
> Just directly use vfs_iocb_iter_read() for data I/Os.
>
> Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
> ---
> v2:
> - fix redundant refcount which cause hanging on chunked inodes.
>
> fs/erofs/Makefile | 1 +
> fs/erofs/data.c | 50 +++++++++++-
> fs/erofs/fileio.c | 181 ++++++++++++++++++++++++++++++++++++++++++++
> fs/erofs/inode.c | 17 +++--
> fs/erofs/internal.h | 7 +-
> fs/erofs/zdata.c | 46 ++---------
> 6 files changed, 251 insertions(+), 51 deletions(-)
> create mode 100644 fs/erofs/fileio.c
>
> diff --git a/fs/erofs/Makefile b/fs/erofs/Makefile
> index 097d672e6b14..4331d53c7109 100644
> --- a/fs/erofs/Makefile
> +++ b/fs/erofs/Makefile
> @@ -7,4 +7,5 @@ erofs-$(CONFIG_EROFS_FS_ZIP) += decompressor.o zmap.o zdata.o zutil.o
> erofs-$(CONFIG_EROFS_FS_ZIP_LZMA) += decompressor_lzma.o
> erofs-$(CONFIG_EROFS_FS_ZIP_DEFLATE) += decompressor_deflate.o
> erofs-$(CONFIG_EROFS_FS_ZIP_ZSTD) += decompressor_zstd.o
> +erofs-$(CONFIG_EROFS_FS_BACKED_BY_FILE) += fileio.o
> erofs-$(CONFIG_EROFS_FS_ONDEMAND) += fscache.o
> diff --git a/fs/erofs/data.c b/fs/erofs/data.c
> index 0fb31c588ae0..b4c07ce7a294 100644
> --- a/fs/erofs/data.c
> +++ b/fs/erofs/data.c
> @@ -132,7 +132,7 @@ int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map)
> if (map->m_la >= inode->i_size) {
> /* leave out-of-bound access unmapped */
> map->m_flags = 0;
> - map->m_plen = 0;
> + map->m_plen = map->m_llen;
> goto out;
> }
>
> @@ -197,8 +197,13 @@ static void erofs_fill_from_devinfo(struct erofs_map_dev *map,
> struct erofs_device_info *dif)
> {
> map->m_bdev = NULL;
> - if (dif->file && S_ISBLK(file_inode(dif->file)->i_mode))
> - map->m_bdev = file_bdev(dif->file);
> + map->m_fp = NULL;
> + if (dif->file) {
> + if (S_ISBLK(file_inode(dif->file)->i_mode))
> + map->m_bdev = file_bdev(dif->file);
> + else
> + map->m_fp = dif->file;
> + }
> map->m_daxdev = dif->dax_dev;
> map->m_dax_part_off = dif->dax_part_off;
> map->m_fscache = dif->fscache;
> @@ -215,6 +220,7 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)
> map->m_daxdev = EROFS_SB(sb)->dax_dev;
> map->m_dax_part_off = EROFS_SB(sb)->dax_part_off;
> map->m_fscache = EROFS_SB(sb)->s_fscache;
> + map->m_fp = EROFS_SB(sb)->fdev;
>
> if (map->m_deviceid) {
> down_read(&devs->rwsem);
> @@ -250,6 +256,42 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)
> return 0;
> }
>
> +/*
> + * bit 30: I/O error occurred on this folio
> + * bit 0 - 29: remaining parts to complete this folio
> + */
> +#define EROFS_ONLINEFOLIO_EIO (1 << 30)
> +
> +void erofs_onlinefolio_init(struct folio *folio)
> +{
> + union {
> + atomic_t o;
> + void *v;
> + } u = { .o = ATOMIC_INIT(1) };
> +
> + folio->private = u.v; /* valid only if file-backed folio is locked */
> +}
> +
> +void erofs_onlinefolio_split(struct folio *folio)
> +{
> + atomic_inc((atomic_t *)&folio->private);
> +}
> +
> +void erofs_onlinefolio_end(struct folio *folio, int err)
> +{
> + int orig, v;
> +
> + do {
> + orig = atomic_read((atomic_t *)&folio->private);
> + v = (orig - 1) | (err ? EROFS_ONLINEFOLIO_EIO : 0);
> + } while (atomic_cmpxchg((atomic_t *)&folio->private, orig, v) != orig);
> +
> + if (v & ~EROFS_ONLINEFOLIO_EIO)
> + return;
> + folio->private = 0;
> + folio_end_read(folio, !(v & EROFS_ONLINEFOLIO_EIO));
> +}
> +
> static int erofs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
> unsigned int flags, struct iomap *iomap, struct iomap *srcmap)
> {
> @@ -399,7 +441,7 @@ static ssize_t erofs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
> }
>
> /* for uncompressed (aligned) files and raw access for other files */
> -const struct address_space_operations erofs_raw_access_aops = {
> +const struct address_space_operations erofs_aops = {
> .read_folio = erofs_read_folio,
> .readahead = erofs_readahead,
> .bmap = erofs_bmap,
> diff --git a/fs/erofs/fileio.c b/fs/erofs/fileio.c
> new file mode 100644
> index 000000000000..eab52b8abd0b
> --- /dev/null
> +++ b/fs/erofs/fileio.c
> @@ -0,0 +1,181 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2024, Alibaba Cloud
> + */
> +#include "internal.h"
> +#include <trace/events/erofs.h>
> +
> +struct erofs_fileio_rq {
> + struct bio_vec bvecs[BIO_MAX_VECS];
> + struct bio bio;
> + struct kiocb iocb;
> +};
> +
> +struct erofs_fileio {
> + struct erofs_map_blocks map;
> + struct erofs_map_dev dev;
> + struct erofs_fileio_rq *rq;
> +};
> +
> +static void erofs_fileio_ki_complete(struct kiocb *iocb, long ret)
> +{
> + struct erofs_fileio_rq *rq =
> + container_of(iocb, struct erofs_fileio_rq, iocb);
> + struct folio_iter fi;
> +
> + DBG_BUGON(rq->bio.bi_end_io);
> + if (ret > 0) {
> + if (ret != rq->bio.bi_iter.bi_size) {
> + bio_advance(&rq->bio, ret);
> + zero_fill_bio(&rq->bio);
> + }
> + ret = 0;
> + }
> + bio_for_each_folio_all(fi, &rq->bio) {
> + DBG_BUGON(folio_test_uptodate(fi.folio));
> + erofs_onlinefolio_end(fi.folio, ret);
> + }
> + kfree(rq);
> +}
> +
> +static void erofs_fileio_rq_submit(struct erofs_fileio_rq *rq)
> +{
> + struct iov_iter iter;
> + int ret;
> +
> + if (!rq)
> + return;
> + rq->iocb.ki_pos = rq->bio.bi_iter.bi_sector << 9;
Trivial cleanup,
rq->iocb.ki_pos = rq->bio.bi_iter.bi_sector << SECTOR_SHIFT;
> + rq->iocb.ki_ioprio = get_current_ioprio();
> + rq->iocb.ki_complete = erofs_fileio_ki_complete;
> + rq->iocb.ki_flags = (rq->iocb.ki_filp->f_mode & FMODE_CAN_ODIRECT) ?
> + IOCB_DIRECT : 0;
> + iov_iter_bvec(&iter, ITER_DEST, rq->bvecs, rq->bio.bi_vcnt,
> + rq->bio.bi_iter.bi_size);
> + ret = vfs_iocb_iter_read(rq->iocb.ki_filp, &rq->iocb, &iter);
> + if (ret != -EIOCBQUEUED)
> + erofs_fileio_ki_complete(&rq->iocb, ret);
Shouldn't we pass return value to caller?
Thanks,
> +}
> +
> +static struct erofs_fileio_rq *erofs_fileio_rq_alloc(struct erofs_map_dev *mdev)
> +{
> + struct erofs_fileio_rq *rq = kzalloc(sizeof(*rq), GFP_KERNEL);
> +
> + if (!rq)
> + return NULL;
> + bio_init(&rq->bio, NULL, rq->bvecs, BIO_MAX_VECS, REQ_OP_READ);
> + rq->iocb.ki_filp = mdev->m_fp;
> + return rq;
> +}
> +
> +static int erofs_fileio_scan_folio(struct erofs_fileio *io, struct folio *folio)
> +{
> + struct inode *inode = folio_inode(folio);
> + struct erofs_map_blocks *map = &io->map;
> + unsigned int cur = 0, end = folio_size(folio), len, attached = 0;
> + loff_t pos = folio_pos(folio), ofs;
> + struct iov_iter iter;
> + struct bio_vec bv;
> + int err = 0;
> +
> + erofs_onlinefolio_init(folio);
> + while (cur < end) {
> + if (!in_range(pos + cur, map->m_la, map->m_llen)) {
> + map->m_la = pos + cur;
> + map->m_llen = end - cur;
> + err = erofs_map_blocks(inode, map);
> + if (err)
> + break;
> + }
> +
> + ofs = folio_pos(folio) + cur - map->m_la;
> + len = min_t(loff_t, map->m_llen - ofs, end - cur);
> + if (map->m_flags & EROFS_MAP_META) {
> + struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
> + void *src;
> +
> + src = erofs_read_metabuf(&buf, inode->i_sb,
> + map->m_pa + ofs, EROFS_KMAP);
> + if (IS_ERR(src)) {
> + err = PTR_ERR(src);
> + break;
> + }
> + bvec_set_folio(&bv, folio, len, cur);
> + iov_iter_bvec(&iter, ITER_DEST, &bv, 1, len);
> + if (copy_to_iter(src, len, &iter) != len) {
> + erofs_put_metabuf(&buf);
> + err = -EIO;
> + break;
> + }
> + erofs_put_metabuf(&buf);
> + } else if (!(map->m_flags & EROFS_MAP_MAPPED)) {
> + folio_zero_segment(folio, cur, cur + len);
> + } else {
> + if (io->rq && (map->m_pa + ofs != io->dev.m_pa ||
> + map->m_deviceid != io->dev.m_deviceid)) {
> +io_retry:
> + erofs_fileio_rq_submit(io->rq);
> + io->rq = NULL;
> + }
> +
> + if (!io->rq) {
> + io->dev = (struct erofs_map_dev) {
> + .m_pa = io->map.m_pa + ofs,
> + .m_deviceid = io->map.m_deviceid,
> + };
> + err = erofs_map_dev(inode->i_sb, &io->dev);
> + if (err)
> + break;
> + io->rq = erofs_fileio_rq_alloc(&io->dev);
> + if (!io->rq) {
> + err = -ENOMEM;
> + break;
> + }
> + io->rq->bio.bi_iter.bi_sector = io->dev.m_pa >> 9;
> + attached = 0;
> + }
> + if (!attached++)
> + erofs_onlinefolio_split(folio);
> + if (!bio_add_folio(&io->rq->bio, folio, len, cur))
> + goto io_retry;
> + io->dev.m_pa += len;
> + }
> + cur += len;
> + }
> + erofs_onlinefolio_end(folio, err);
> + return err;
> +}
> +
> +static int erofs_fileio_read_folio(struct file *file, struct folio *folio)
> +{
> + struct erofs_fileio io = {};
> + int err;
> +
> + trace_erofs_read_folio(folio, false);
> + err = erofs_fileio_scan_folio(&io, folio);
> + erofs_fileio_rq_submit(io.rq);
> + return err;
> +}
> +
> +static void erofs_fileio_readahead(struct readahead_control *rac)
> +{
> + struct inode *inode = rac->mapping->host;
> + struct erofs_fileio io = {};
> + struct folio *folio;
> + int err;
> +
> + trace_erofs_readpages(inode, readahead_index(rac),
> + readahead_count(rac), false);
> + while ((folio = readahead_folio(rac))) {
> + err = erofs_fileio_scan_folio(&io, folio);
> + if (err && err != -EINTR)
> + erofs_err(inode->i_sb, "readahead error at folio %lu @ nid %llu",
> + folio->index, EROFS_I(inode)->nid);
> + }
> + erofs_fileio_rq_submit(io.rq);
> +}
> +
> +const struct address_space_operations erofs_fileio_aops = {
> + .read_folio = erofs_fileio_read_folio,
> + .readahead = erofs_fileio_readahead,
> +};
> diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c
> index d05b9e59f122..4a902e6e69a5 100644
> --- a/fs/erofs/inode.c
> +++ b/fs/erofs/inode.c
> @@ -258,11 +258,14 @@ static int erofs_fill_inode(struct inode *inode)
> }
>
> mapping_set_large_folios(inode->i_mapping);
> - if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb))) {
> - /* XXX: data I/Os will be implemented in the following patches */
> - err = -EOPNOTSUPP;
> - } else if (erofs_inode_is_data_compressed(vi->datalayout)) {
> + if (erofs_inode_is_data_compressed(vi->datalayout)) {
> #ifdef CONFIG_EROFS_FS_ZIP
> +#ifdef CONFIG_EROFS_FS_BACKED_BY_FILE
> + if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb))) {
> + err = -EOPNOTSUPP;
> + goto out_unlock;
> + }
> +#endif
> DO_ONCE_LITE_IF(inode->i_blkbits != PAGE_SHIFT,
> erofs_info, inode->i_sb,
> "EXPERIMENTAL EROFS subpage compressed block support in use. Use at your own risk!");
> @@ -271,10 +274,14 @@ static int erofs_fill_inode(struct inode *inode)
> err = -EOPNOTSUPP;
> #endif
> } else {
> - inode->i_mapping->a_ops = &erofs_raw_access_aops;
> + inode->i_mapping->a_ops = &erofs_aops;
> #ifdef CONFIG_EROFS_FS_ONDEMAND
> if (erofs_is_fscache_mode(inode->i_sb))
> inode->i_mapping->a_ops = &erofs_fscache_access_aops;
> +#endif
> +#ifdef CONFIG_EROFS_FS_BACKED_BY_FILE
> + if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb)))
> + inode->i_mapping->a_ops = &erofs_fileio_aops;
> #endif
> }
> out_unlock:
> diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
> index 9bf4fb1cfa09..9bc4dcfd06d7 100644
> --- a/fs/erofs/internal.h
> +++ b/fs/erofs/internal.h
> @@ -372,6 +372,7 @@ struct erofs_map_dev {
> struct erofs_fscache *m_fscache;
> struct block_device *m_bdev;
> struct dax_device *m_daxdev;
> + struct file *m_fp;
> u64 m_dax_part_off;
>
> erofs_off_t m_pa;
> @@ -380,7 +381,8 @@ struct erofs_map_dev {
>
> extern const struct super_operations erofs_sops;
>
> -extern const struct address_space_operations erofs_raw_access_aops;
> +extern const struct address_space_operations erofs_aops;
> +extern const struct address_space_operations erofs_fileio_aops;
> extern const struct address_space_operations z_erofs_aops;
> extern const struct address_space_operations erofs_fscache_access_aops;
>
> @@ -411,6 +413,9 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *dev);
> int erofs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
> u64 start, u64 len);
> int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map);
> +void erofs_onlinefolio_init(struct folio *folio);
> +void erofs_onlinefolio_split(struct folio *folio);
> +void erofs_onlinefolio_end(struct folio *folio, int err);
> struct inode *erofs_iget(struct super_block *sb, erofs_nid_t nid);
> int erofs_getattr(struct mnt_idmap *idmap, const struct path *path,
> struct kstat *stat, u32 request_mask,
> diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
> index 424f656cd765..350612f32ac6 100644
> --- a/fs/erofs/zdata.c
> +++ b/fs/erofs/zdata.c
> @@ -122,42 +122,6 @@ static bool erofs_folio_is_managed(struct erofs_sb_info *sbi, struct folio *fo)
> return fo->mapping == MNGD_MAPPING(sbi);
> }
>
> -/*
> - * bit 30: I/O error occurred on this folio
> - * bit 0 - 29: remaining parts to complete this folio
> - */
> -#define Z_EROFS_FOLIO_EIO (1 << 30)
> -
> -static void z_erofs_onlinefolio_init(struct folio *folio)
> -{
> - union {
> - atomic_t o;
> - void *v;
> - } u = { .o = ATOMIC_INIT(1) };
> -
> - folio->private = u.v; /* valid only if file-backed folio is locked */
> -}
> -
> -static void z_erofs_onlinefolio_split(struct folio *folio)
> -{
> - atomic_inc((atomic_t *)&folio->private);
> -}
> -
> -static void z_erofs_onlinefolio_end(struct folio *folio, int err)
> -{
> - int orig, v;
> -
> - do {
> - orig = atomic_read((atomic_t *)&folio->private);
> - v = (orig - 1) | (err ? Z_EROFS_FOLIO_EIO : 0);
> - } while (atomic_cmpxchg((atomic_t *)&folio->private, orig, v) != orig);
> -
> - if (v & ~Z_EROFS_FOLIO_EIO)
> - return;
> - folio->private = 0;
> - folio_end_read(folio, !(v & Z_EROFS_FOLIO_EIO));
> -}
> -
> #define Z_EROFS_ONSTACK_PAGES 32
>
> /*
> @@ -965,7 +929,7 @@ static int z_erofs_scan_folio(struct z_erofs_decompress_frontend *f,
> int err = 0;
>
> tight = (bs == PAGE_SIZE);
> - z_erofs_onlinefolio_init(folio);
> + erofs_onlinefolio_init(folio);
> do {
> if (offset + end - 1 < map->m_la ||
> offset + end - 1 >= map->m_la + map->m_llen) {
> @@ -1024,7 +988,7 @@ static int z_erofs_scan_folio(struct z_erofs_decompress_frontend *f,
> if (err)
> break;
>
> - z_erofs_onlinefolio_split(folio);
> + erofs_onlinefolio_split(folio);
> if (f->pcl->pageofs_out != (map->m_la & ~PAGE_MASK))
> f->pcl->multibases = true;
> if (f->pcl->length < offset + end - map->m_la) {
> @@ -1044,7 +1008,7 @@ static int z_erofs_scan_folio(struct z_erofs_decompress_frontend *f,
> tight = (bs == PAGE_SIZE);
> }
> } while ((end = cur) > 0);
> - z_erofs_onlinefolio_end(folio, err);
> + erofs_onlinefolio_end(folio, err);
> return err;
> }
>
> @@ -1147,7 +1111,7 @@ static void z_erofs_fill_other_copies(struct z_erofs_decompress_backend *be,
> cur += len;
> }
> kunmap_local(dst);
> - z_erofs_onlinefolio_end(page_folio(bvi->bvec.page), err);
> + erofs_onlinefolio_end(page_folio(bvi->bvec.page), err);
> list_del(p);
> kfree(bvi);
> }
> @@ -1302,7 +1266,7 @@ static int z_erofs_decompress_pcluster(struct z_erofs_decompress_backend *be,
>
> DBG_BUGON(z_erofs_page_is_invalidated(page));
> if (!z_erofs_is_shortlived_page(page)) {
> - z_erofs_onlinefolio_end(page_folio(page), err);
> + erofs_onlinefolio_end(page_folio(page), err);
> continue;
> }
> if (pcl->algorithmformat != Z_EROFS_COMPRESSION_LZ4) {
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 3/4] erofs: support compressed inodes for fileio
2024-08-30 3:28 ` [PATCH v2 3/4] erofs: support compressed " Gao Xiang
2024-09-01 20:47 ` Sandeep Dhavale
@ 2024-09-05 9:01 ` Chao Yu
2024-09-05 9:14 ` Gao Xiang
2024-09-05 9:46 ` Chao Yu
2 siblings, 1 reply; 30+ messages in thread
From: Chao Yu @ 2024-09-05 9:01 UTC (permalink / raw)
To: Gao Xiang, linux-erofs; +Cc: LKML
On 2024/8/30 11:28, Gao Xiang wrote:
> Use pseudo bios just like the previous fscache approach since
> merged bio_vecs can be filled properly with unique interfaces.
>
> Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
> ---
> fs/erofs/fileio.c | 25 +++++++++++++++++++++----
> fs/erofs/inode.c | 6 ------
> fs/erofs/internal.h | 8 ++++++++
> fs/erofs/zdata.c | 27 +++++++++++++++++----------
> 4 files changed, 46 insertions(+), 20 deletions(-)
>
> diff --git a/fs/erofs/fileio.c b/fs/erofs/fileio.c
> index eab52b8abd0b..9e4b851d85c0 100644
> --- a/fs/erofs/fileio.c
> +++ b/fs/erofs/fileio.c
> @@ -23,7 +23,6 @@ static void erofs_fileio_ki_complete(struct kiocb *iocb, long ret)
> container_of(iocb, struct erofs_fileio_rq, iocb);
> struct folio_iter fi;
>
> - DBG_BUGON(rq->bio.bi_end_io);
> if (ret > 0) {
> if (ret != rq->bio.bi_iter.bi_size) {
> bio_advance(&rq->bio, ret);
> @@ -31,9 +30,13 @@ static void erofs_fileio_ki_complete(struct kiocb *iocb, long ret)
> }
> ret = 0;
> }
> - bio_for_each_folio_all(fi, &rq->bio) {
> - DBG_BUGON(folio_test_uptodate(fi.folio));
> - erofs_onlinefolio_end(fi.folio, ret);
> + if (rq->bio.bi_end_io) {
> + rq->bio.bi_end_io(&rq->bio);
> + } else {
> + bio_for_each_folio_all(fi, &rq->bio) {
> + DBG_BUGON(folio_test_uptodate(fi.folio));
> + erofs_onlinefolio_end(fi.folio, ret);
> + }
> }
> kfree(rq);
> }
> @@ -68,6 +71,20 @@ static struct erofs_fileio_rq *erofs_fileio_rq_alloc(struct erofs_map_dev *mdev)
> return rq;
> }
>
> +struct bio *erofs_fileio_bio_alloc(struct erofs_map_dev *mdev)
> +{
> + struct erofs_fileio_rq *rq;
> +
> + rq = erofs_fileio_rq_alloc(mdev);
> + return rq ? &rq->bio : NULL;
> +}
> +
> +void erofs_fileio_submit_bio(struct bio *bio)
> +{
> + return erofs_fileio_rq_submit(container_of(bio, struct erofs_fileio_rq,
> + bio));
> +}
> +
> static int erofs_fileio_scan_folio(struct erofs_fileio *io, struct folio *folio)
> {
> struct inode *inode = folio_inode(folio);
> diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c
> index 4a902e6e69a5..82259553d9f6 100644
> --- a/fs/erofs/inode.c
> +++ b/fs/erofs/inode.c
> @@ -260,12 +260,6 @@ static int erofs_fill_inode(struct inode *inode)
> mapping_set_large_folios(inode->i_mapping);
> if (erofs_inode_is_data_compressed(vi->datalayout)) {
> #ifdef CONFIG_EROFS_FS_ZIP
> -#ifdef CONFIG_EROFS_FS_BACKED_BY_FILE
> - if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb))) {
> - err = -EOPNOTSUPP;
> - goto out_unlock;
> - }
> -#endif
> DO_ONCE_LITE_IF(inode->i_blkbits != PAGE_SHIFT,
> erofs_info, inode->i_sb,
> "EXPERIMENTAL EROFS subpage compressed block support in use. Use at your own risk!");
> diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
> index 9bc4dcfd06d7..4efd578d7c62 100644
> --- a/fs/erofs/internal.h
> +++ b/fs/erofs/internal.h
> @@ -489,6 +489,14 @@ static inline void z_erofs_exit_subsystem(void) {}
> static inline int erofs_init_managed_cache(struct super_block *sb) { return 0; }
> #endif /* !CONFIG_EROFS_FS_ZIP */
>
> +#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);
> +#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) {}
> +#endif
> +
> #ifdef CONFIG_EROFS_FS_ONDEMAND
> int erofs_fscache_register_fs(struct super_block *sb);
> void erofs_fscache_unregister_fs(struct super_block *sb);
> diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
> index 350612f32ac6..2271cb74ae3a 100644
> --- a/fs/erofs/zdata.c
> +++ b/fs/erofs/zdata.c
> @@ -1618,10 +1618,12 @@ static void z_erofs_submit_queue(struct z_erofs_decompress_frontend *f,
> if (bio && (cur != last_pa ||
> bio->bi_bdev != mdev.m_bdev)) {
> io_retry:
> - if (!erofs_is_fscache_mode(sb))
> - submit_bio(bio);
> - else
> + if (erofs_is_fileio_mode(EROFS_SB(sb)))
> + erofs_fileio_submit_bio(bio);
> + else if (erofs_is_fscache_mode(sb))
> erofs_fscache_submit_bio(bio);
> + else
> + submit_bio(bio);
>
> if (memstall) {
> psi_memstall_leave(&pflags);
> @@ -1637,10 +1639,13 @@ static void z_erofs_submit_queue(struct z_erofs_decompress_frontend *f,
> }
>
> if (!bio) {
> - bio = erofs_is_fscache_mode(sb) ?
> - erofs_fscache_bio_alloc(&mdev) :
> - bio_alloc(mdev.m_bdev, BIO_MAX_VECS,
> - REQ_OP_READ, GFP_NOIO);
> + if (erofs_is_fileio_mode(EROFS_SB(sb)))
> + bio = erofs_fileio_bio_alloc(&mdev);
It seems erofs_fileio_bio_alloc() can fail, it needs to handle NULL bio
here?
Thanks,
> + else if (erofs_is_fscache_mode(sb))
> + bio = erofs_fscache_bio_alloc(&mdev);
> + else
> + bio = bio_alloc(mdev.m_bdev, BIO_MAX_VECS,
> + REQ_OP_READ, GFP_NOIO);
> bio->bi_end_io = z_erofs_endio;
> bio->bi_iter.bi_sector = cur >> 9;
> bio->bi_private = q[JQ_SUBMIT];
> @@ -1667,10 +1672,12 @@ static void z_erofs_submit_queue(struct z_erofs_decompress_frontend *f,
> } while (owned_head != Z_EROFS_PCLUSTER_TAIL);
>
> if (bio) {
> - if (!erofs_is_fscache_mode(sb))
> - submit_bio(bio);
> - else
> + if (erofs_is_fileio_mode(EROFS_SB(sb)))
> + erofs_fileio_submit_bio(bio);
> + else if (erofs_is_fscache_mode(sb))
> erofs_fscache_submit_bio(bio);
> + else
> + submit_bio(bio);
> if (memstall)
> psi_memstall_leave(&pflags);
> }
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 4/4] erofs: mark experimental fscache backend deprecated
2024-08-30 3:28 ` [PATCH v2 4/4] erofs: mark experimental fscache backend deprecated Gao Xiang
2024-09-01 20:48 ` Sandeep Dhavale
@ 2024-09-05 9:03 ` Chao Yu
1 sibling, 0 replies; 30+ messages in thread
From: Chao Yu @ 2024-09-05 9:03 UTC (permalink / raw)
To: Gao Xiang, linux-erofs; +Cc: LKML
On 2024/8/30 11:28, Gao Xiang wrote:
> Although fscache is still described as "General Filesystem Caching" for
> network filesystems and other things such as ISO9660 filesystems, it has
> actually become a part of netfslib recently, which was unexpected at the
> time when "EROFS over fscache" proposed (2021) since EROFS is entirely a
> disk filesystem and the dependency is redundant.
>
> Mark it deprecated and it will be removed after "fanotify pre-content
> hooks" lands, which will provide the same functionality for EROFS.
>
> Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Reviewed-by: Chao Yu <chao@kernel.org>
Thanks,
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 2/4] erofs: support unencoded inodes for fileio
2024-09-05 9:01 ` Chao Yu
@ 2024-09-05 9:13 ` Gao Xiang
0 siblings, 0 replies; 30+ messages in thread
From: Gao Xiang @ 2024-09-05 9:13 UTC (permalink / raw)
To: Chao Yu, linux-erofs; +Cc: LKML
Hi Chao,
On 2024/9/5 17:01, Chao Yu wrote:
> On 2024/8/30 11:28, Gao Xiang wrote:
..
>> +
>> +static void erofs_fileio_rq_submit(struct erofs_fileio_rq *rq)
>> +{
>> + struct iov_iter iter;
>> + int ret;
>> +
>> + if (!rq)
>> + return;
>> + rq->iocb.ki_pos = rq->bio.bi_iter.bi_sector << 9;
>
> Trivial cleanup,
>
> rq->iocb.ki_pos = rq->bio.bi_iter.bi_sector << SECTOR_SHIFT;
Will send a quick fix version.
>
>> + rq->iocb.ki_ioprio = get_current_ioprio();
>> + rq->iocb.ki_complete = erofs_fileio_ki_complete;
>> + rq->iocb.ki_flags = (rq->iocb.ki_filp->f_mode & FMODE_CAN_ODIRECT) ?
>> + IOCB_DIRECT : 0;
>> + iov_iter_bvec(&iter, ITER_DEST, rq->bvecs, rq->bio.bi_vcnt,
>> + rq->bio.bi_iter.bi_size);
>> + ret = vfs_iocb_iter_read(rq->iocb.ki_filp, &rq->iocb, &iter);
>> + if (ret != -EIOCBQUEUED)
>> + erofs_fileio_ki_complete(&rq->iocb, ret);
>
> Shouldn't we pass return value to caller?
I don't think it's needed. Since ki_complete will handle error cases
for both (a)sync I/Os.
Thanks,
Gao Xiang
>
> Thanks,
>
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 3/4] erofs: support compressed inodes for fileio
2024-09-05 9:01 ` Chao Yu
@ 2024-09-05 9:14 ` Gao Xiang
0 siblings, 0 replies; 30+ messages in thread
From: Gao Xiang @ 2024-09-05 9:14 UTC (permalink / raw)
To: Chao Yu, linux-erofs; +Cc: LKML
On 2024/9/5 17:01, Chao Yu wrote:
> On 2024/8/30 11:28, Gao Xiang wrote:
>> Use pseudo bios just like the previous fscache approach since
>> merged bio_vecs can be filled properly with unique interfaces.
>>
>> Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
>> ---
>> fs/erofs/fileio.c | 25 +++++++++++++++++++++----
>> fs/erofs/inode.c | 6 ------
>> fs/erofs/internal.h | 8 ++++++++
>> fs/erofs/zdata.c | 27 +++++++++++++++++----------
>> 4 files changed, 46 insertions(+), 20 deletions(-)
>>
>> diff --git a/fs/erofs/fileio.c b/fs/erofs/fileio.c
>> index eab52b8abd0b..9e4b851d85c0 100644
>> --- a/fs/erofs/fileio.c
>> +++ b/fs/erofs/fileio.c
>> @@ -23,7 +23,6 @@ static void erofs_fileio_ki_complete(struct kiocb *iocb, long ret)
>> container_of(iocb, struct erofs_fileio_rq, iocb);
>> struct folio_iter fi;
>> - DBG_BUGON(rq->bio.bi_end_io);
>> if (ret > 0) {
>> if (ret != rq->bio.bi_iter.bi_size) {
>> bio_advance(&rq->bio, ret);
>> @@ -31,9 +30,13 @@ static void erofs_fileio_ki_complete(struct kiocb *iocb, long ret)
>> }
>> ret = 0;
>> }
>> - bio_for_each_folio_all(fi, &rq->bio) {
>> - DBG_BUGON(folio_test_uptodate(fi.folio));
>> - erofs_onlinefolio_end(fi.folio, ret);
>> + if (rq->bio.bi_end_io) {
>> + rq->bio.bi_end_io(&rq->bio);
>> + } else {
>> + bio_for_each_folio_all(fi, &rq->bio) {
>> + DBG_BUGON(folio_test_uptodate(fi.folio));
>> + erofs_onlinefolio_end(fi.folio, ret);
>> + }
>> }
>> kfree(rq);
>> }
>> @@ -68,6 +71,20 @@ static struct erofs_fileio_rq *erofs_fileio_rq_alloc(struct erofs_map_dev *mdev)
>> return rq;
>> }
>> +struct bio *erofs_fileio_bio_alloc(struct erofs_map_dev *mdev)
>> +{
>> + struct erofs_fileio_rq *rq;
>> +
>> + rq = erofs_fileio_rq_alloc(mdev);
>> + return rq ? &rq->bio : NULL;
>> +}
>> +
>> +void erofs_fileio_submit_bio(struct bio *bio)
>> +{
>> + return erofs_fileio_rq_submit(container_of(bio, struct erofs_fileio_rq,
>> + bio));
>> +}
>> +
>> static int erofs_fileio_scan_folio(struct erofs_fileio *io, struct folio *folio)
>> {
>> struct inode *inode = folio_inode(folio);
>> diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c
>> index 4a902e6e69a5..82259553d9f6 100644
>> --- a/fs/erofs/inode.c
>> +++ b/fs/erofs/inode.c
>> @@ -260,12 +260,6 @@ static int erofs_fill_inode(struct inode *inode)
>> mapping_set_large_folios(inode->i_mapping);
>> if (erofs_inode_is_data_compressed(vi->datalayout)) {
>> #ifdef CONFIG_EROFS_FS_ZIP
>> -#ifdef CONFIG_EROFS_FS_BACKED_BY_FILE
>> - if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb))) {
>> - err = -EOPNOTSUPP;
>> - goto out_unlock;
>> - }
>> -#endif
>> DO_ONCE_LITE_IF(inode->i_blkbits != PAGE_SHIFT,
>> erofs_info, inode->i_sb,
>> "EXPERIMENTAL EROFS subpage compressed block support in use. Use at your own risk!");
>> diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
>> index 9bc4dcfd06d7..4efd578d7c62 100644
>> --- a/fs/erofs/internal.h
>> +++ b/fs/erofs/internal.h
>> @@ -489,6 +489,14 @@ static inline void z_erofs_exit_subsystem(void) {}
>> static inline int erofs_init_managed_cache(struct super_block *sb) { return 0; }
>> #endif /* !CONFIG_EROFS_FS_ZIP */
>> +#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);
>> +#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) {}
>> +#endif
>> +
>> #ifdef CONFIG_EROFS_FS_ONDEMAND
>> int erofs_fscache_register_fs(struct super_block *sb);
>> void erofs_fscache_unregister_fs(struct super_block *sb);
>> diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
>> index 350612f32ac6..2271cb74ae3a 100644
>> --- a/fs/erofs/zdata.c
>> +++ b/fs/erofs/zdata.c
>> @@ -1618,10 +1618,12 @@ static void z_erofs_submit_queue(struct z_erofs_decompress_frontend *f,
>> if (bio && (cur != last_pa ||
>> bio->bi_bdev != mdev.m_bdev)) {
>> io_retry:
>> - if (!erofs_is_fscache_mode(sb))
>> - submit_bio(bio);
>> - else
>> + if (erofs_is_fileio_mode(EROFS_SB(sb)))
>> + erofs_fileio_submit_bio(bio);
>> + else if (erofs_is_fscache_mode(sb))
>> erofs_fscache_submit_bio(bio);
>> + else
>> + submit_bio(bio);
>> if (memstall) {
>> psi_memstall_leave(&pflags);
>> @@ -1637,10 +1639,13 @@ static void z_erofs_submit_queue(struct z_erofs_decompress_frontend *f,
>> }
>> if (!bio) {
>> - bio = erofs_is_fscache_mode(sb) ?
>> - erofs_fscache_bio_alloc(&mdev) :
>> - bio_alloc(mdev.m_bdev, BIO_MAX_VECS,
>> - REQ_OP_READ, GFP_NOIO);
>> + if (erofs_is_fileio_mode(EROFS_SB(sb)))
>> + bio = erofs_fileio_bio_alloc(&mdev);
>
> It seems erofs_fileio_bio_alloc() can fail, it needs to handle NULL bio
> here?
I will mark it as __GFP_NOFAIL too in the previous patch.
Thanks,
Gao Xiang
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH v3 2/4] erofs: support unencoded inodes for fileio
2024-08-30 3:28 [PATCH v2 1/4] erofs: add file-backed mount support Gao Xiang
` (5 preceding siblings ...)
2024-09-05 8:22 ` Chao Yu
@ 2024-09-05 9:30 ` Gao Xiang
2024-09-05 9:44 ` Chao Yu
2024-09-24 9:21 ` [PATCH v2 1/4] erofs: add file-backed mount support Geert Uytterhoeven
7 siblings, 1 reply; 30+ messages in thread
From: Gao Xiang @ 2024-09-05 9:30 UTC (permalink / raw)
To: linux-erofs; +Cc: LKML, Gao Xiang, Sandeep Dhavale
Since EROFS only needs to handle read requests in simple contexts,
Just directly use vfs_iocb_iter_read() for data I/Os.
Reviewed-by: Sandeep Dhavale <dhavale@google.com>
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
---
changes since v2 (Chao):
- erofs_fileio_rq_alloc() always succeeds;
- use SECTOR_SHIFT constant.
fs/erofs/Makefile | 1 +
fs/erofs/data.c | 50 ++++++++++++-
fs/erofs/fileio.c | 177 ++++++++++++++++++++++++++++++++++++++++++++
fs/erofs/inode.c | 17 +++--
fs/erofs/internal.h | 7 +-
fs/erofs/zdata.c | 46 ++----------
6 files changed, 247 insertions(+), 51 deletions(-)
create mode 100644 fs/erofs/fileio.c
diff --git a/fs/erofs/Makefile b/fs/erofs/Makefile
index 097d672e6b14..4331d53c7109 100644
--- a/fs/erofs/Makefile
+++ b/fs/erofs/Makefile
@@ -7,4 +7,5 @@ erofs-$(CONFIG_EROFS_FS_ZIP) += decompressor.o zmap.o zdata.o zutil.o
erofs-$(CONFIG_EROFS_FS_ZIP_LZMA) += decompressor_lzma.o
erofs-$(CONFIG_EROFS_FS_ZIP_DEFLATE) += decompressor_deflate.o
erofs-$(CONFIG_EROFS_FS_ZIP_ZSTD) += decompressor_zstd.o
+erofs-$(CONFIG_EROFS_FS_BACKED_BY_FILE) += fileio.o
erofs-$(CONFIG_EROFS_FS_ONDEMAND) += fscache.o
diff --git a/fs/erofs/data.c b/fs/erofs/data.c
index 0fb31c588ae0..b4c07ce7a294 100644
--- a/fs/erofs/data.c
+++ b/fs/erofs/data.c
@@ -132,7 +132,7 @@ int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map)
if (map->m_la >= inode->i_size) {
/* leave out-of-bound access unmapped */
map->m_flags = 0;
- map->m_plen = 0;
+ map->m_plen = map->m_llen;
goto out;
}
@@ -197,8 +197,13 @@ static void erofs_fill_from_devinfo(struct erofs_map_dev *map,
struct erofs_device_info *dif)
{
map->m_bdev = NULL;
- if (dif->file && S_ISBLK(file_inode(dif->file)->i_mode))
- map->m_bdev = file_bdev(dif->file);
+ map->m_fp = NULL;
+ if (dif->file) {
+ if (S_ISBLK(file_inode(dif->file)->i_mode))
+ map->m_bdev = file_bdev(dif->file);
+ else
+ map->m_fp = dif->file;
+ }
map->m_daxdev = dif->dax_dev;
map->m_dax_part_off = dif->dax_part_off;
map->m_fscache = dif->fscache;
@@ -215,6 +220,7 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)
map->m_daxdev = EROFS_SB(sb)->dax_dev;
map->m_dax_part_off = EROFS_SB(sb)->dax_part_off;
map->m_fscache = EROFS_SB(sb)->s_fscache;
+ map->m_fp = EROFS_SB(sb)->fdev;
if (map->m_deviceid) {
down_read(&devs->rwsem);
@@ -250,6 +256,42 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)
return 0;
}
+/*
+ * bit 30: I/O error occurred on this folio
+ * bit 0 - 29: remaining parts to complete this folio
+ */
+#define EROFS_ONLINEFOLIO_EIO (1 << 30)
+
+void erofs_onlinefolio_init(struct folio *folio)
+{
+ union {
+ atomic_t o;
+ void *v;
+ } u = { .o = ATOMIC_INIT(1) };
+
+ folio->private = u.v; /* valid only if file-backed folio is locked */
+}
+
+void erofs_onlinefolio_split(struct folio *folio)
+{
+ atomic_inc((atomic_t *)&folio->private);
+}
+
+void erofs_onlinefolio_end(struct folio *folio, int err)
+{
+ int orig, v;
+
+ do {
+ orig = atomic_read((atomic_t *)&folio->private);
+ v = (orig - 1) | (err ? EROFS_ONLINEFOLIO_EIO : 0);
+ } while (atomic_cmpxchg((atomic_t *)&folio->private, orig, v) != orig);
+
+ if (v & ~EROFS_ONLINEFOLIO_EIO)
+ return;
+ folio->private = 0;
+ folio_end_read(folio, !(v & EROFS_ONLINEFOLIO_EIO));
+}
+
static int erofs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
unsigned int flags, struct iomap *iomap, struct iomap *srcmap)
{
@@ -399,7 +441,7 @@ static ssize_t erofs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
}
/* for uncompressed (aligned) files and raw access for other files */
-const struct address_space_operations erofs_raw_access_aops = {
+const struct address_space_operations erofs_aops = {
.read_folio = erofs_read_folio,
.readahead = erofs_readahead,
.bmap = erofs_bmap,
diff --git a/fs/erofs/fileio.c b/fs/erofs/fileio.c
new file mode 100644
index 000000000000..d2033d7b95e6
--- /dev/null
+++ b/fs/erofs/fileio.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2024, Alibaba Cloud
+ */
+#include "internal.h"
+#include <trace/events/erofs.h>
+
+struct erofs_fileio_rq {
+ struct bio_vec bvecs[BIO_MAX_VECS];
+ struct bio bio;
+ struct kiocb iocb;
+};
+
+struct erofs_fileio {
+ struct erofs_map_blocks map;
+ struct erofs_map_dev dev;
+ struct erofs_fileio_rq *rq;
+};
+
+static void erofs_fileio_ki_complete(struct kiocb *iocb, long ret)
+{
+ struct erofs_fileio_rq *rq =
+ container_of(iocb, struct erofs_fileio_rq, iocb);
+ struct folio_iter fi;
+
+ DBG_BUGON(rq->bio.bi_end_io);
+ if (ret > 0) {
+ if (ret != rq->bio.bi_iter.bi_size) {
+ bio_advance(&rq->bio, ret);
+ zero_fill_bio(&rq->bio);
+ }
+ ret = 0;
+ }
+ bio_for_each_folio_all(fi, &rq->bio) {
+ DBG_BUGON(folio_test_uptodate(fi.folio));
+ erofs_onlinefolio_end(fi.folio, ret);
+ }
+ kfree(rq);
+}
+
+static void erofs_fileio_rq_submit(struct erofs_fileio_rq *rq)
+{
+ struct iov_iter iter;
+ int ret;
+
+ if (!rq)
+ return;
+ rq->iocb.ki_pos = rq->bio.bi_iter.bi_sector << SECTOR_SHIFT;
+ rq->iocb.ki_ioprio = get_current_ioprio();
+ rq->iocb.ki_complete = erofs_fileio_ki_complete;
+ rq->iocb.ki_flags = (rq->iocb.ki_filp->f_mode & FMODE_CAN_ODIRECT) ?
+ IOCB_DIRECT : 0;
+ iov_iter_bvec(&iter, ITER_DEST, rq->bvecs, rq->bio.bi_vcnt,
+ rq->bio.bi_iter.bi_size);
+ ret = vfs_iocb_iter_read(rq->iocb.ki_filp, &rq->iocb, &iter);
+ if (ret != -EIOCBQUEUED)
+ erofs_fileio_ki_complete(&rq->iocb, ret);
+}
+
+static struct erofs_fileio_rq *erofs_fileio_rq_alloc(struct erofs_map_dev *mdev)
+{
+ struct erofs_fileio_rq *rq = kzalloc(sizeof(*rq),
+ GFP_KERNEL | __GFP_NOFAIL);
+
+ bio_init(&rq->bio, NULL, rq->bvecs, BIO_MAX_VECS, REQ_OP_READ);
+ rq->iocb.ki_filp = mdev->m_fp;
+ return rq;
+}
+
+static int erofs_fileio_scan_folio(struct erofs_fileio *io, struct folio *folio)
+{
+ struct inode *inode = folio_inode(folio);
+ struct erofs_map_blocks *map = &io->map;
+ unsigned int cur = 0, end = folio_size(folio), len, attached = 0;
+ loff_t pos = folio_pos(folio), ofs;
+ struct iov_iter iter;
+ struct bio_vec bv;
+ int err = 0;
+
+ erofs_onlinefolio_init(folio);
+ while (cur < end) {
+ if (!in_range(pos + cur, map->m_la, map->m_llen)) {
+ map->m_la = pos + cur;
+ map->m_llen = end - cur;
+ err = erofs_map_blocks(inode, map);
+ if (err)
+ break;
+ }
+
+ ofs = folio_pos(folio) + cur - map->m_la;
+ len = min_t(loff_t, map->m_llen - ofs, end - cur);
+ if (map->m_flags & EROFS_MAP_META) {
+ struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
+ void *src;
+
+ src = erofs_read_metabuf(&buf, inode->i_sb,
+ map->m_pa + ofs, EROFS_KMAP);
+ if (IS_ERR(src)) {
+ err = PTR_ERR(src);
+ break;
+ }
+ bvec_set_folio(&bv, folio, len, cur);
+ iov_iter_bvec(&iter, ITER_DEST, &bv, 1, len);
+ if (copy_to_iter(src, len, &iter) != len) {
+ erofs_put_metabuf(&buf);
+ err = -EIO;
+ break;
+ }
+ erofs_put_metabuf(&buf);
+ } else if (!(map->m_flags & EROFS_MAP_MAPPED)) {
+ folio_zero_segment(folio, cur, cur + len);
+ attached = 0;
+ } else {
+ if (io->rq && (map->m_pa + ofs != io->dev.m_pa ||
+ map->m_deviceid != io->dev.m_deviceid)) {
+io_retry:
+ erofs_fileio_rq_submit(io->rq);
+ io->rq = NULL;
+ }
+
+ if (!io->rq) {
+ io->dev = (struct erofs_map_dev) {
+ .m_pa = io->map.m_pa + ofs,
+ .m_deviceid = io->map.m_deviceid,
+ };
+ err = erofs_map_dev(inode->i_sb, &io->dev);
+ if (err)
+ break;
+ io->rq = erofs_fileio_rq_alloc(&io->dev);
+ io->rq->bio.bi_iter.bi_sector = io->dev.m_pa >> 9;
+ attached = 0;
+ }
+ if (!attached++)
+ erofs_onlinefolio_split(folio);
+ if (!bio_add_folio(&io->rq->bio, folio, len, cur))
+ goto io_retry;
+ io->dev.m_pa += len;
+ }
+ cur += len;
+ }
+ erofs_onlinefolio_end(folio, err);
+ return err;
+}
+
+static int erofs_fileio_read_folio(struct file *file, struct folio *folio)
+{
+ struct erofs_fileio io = {};
+ int err;
+
+ trace_erofs_read_folio(folio, true);
+ err = erofs_fileio_scan_folio(&io, folio);
+ erofs_fileio_rq_submit(io.rq);
+ return err;
+}
+
+static void erofs_fileio_readahead(struct readahead_control *rac)
+{
+ struct inode *inode = rac->mapping->host;
+ struct erofs_fileio io = {};
+ struct folio *folio;
+ int err;
+
+ trace_erofs_readpages(inode, readahead_index(rac),
+ readahead_count(rac), true);
+ while ((folio = readahead_folio(rac))) {
+ err = erofs_fileio_scan_folio(&io, folio);
+ if (err && err != -EINTR)
+ erofs_err(inode->i_sb, "readahead error at folio %lu @ nid %llu",
+ folio->index, EROFS_I(inode)->nid);
+ }
+ erofs_fileio_rq_submit(io.rq);
+}
+
+const struct address_space_operations erofs_fileio_aops = {
+ .read_folio = erofs_fileio_read_folio,
+ .readahead = erofs_fileio_readahead,
+};
diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c
index d05b9e59f122..4a902e6e69a5 100644
--- a/fs/erofs/inode.c
+++ b/fs/erofs/inode.c
@@ -258,11 +258,14 @@ static int erofs_fill_inode(struct inode *inode)
}
mapping_set_large_folios(inode->i_mapping);
- if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb))) {
- /* XXX: data I/Os will be implemented in the following patches */
- err = -EOPNOTSUPP;
- } else if (erofs_inode_is_data_compressed(vi->datalayout)) {
+ if (erofs_inode_is_data_compressed(vi->datalayout)) {
#ifdef CONFIG_EROFS_FS_ZIP
+#ifdef CONFIG_EROFS_FS_BACKED_BY_FILE
+ if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb))) {
+ err = -EOPNOTSUPP;
+ goto out_unlock;
+ }
+#endif
DO_ONCE_LITE_IF(inode->i_blkbits != PAGE_SHIFT,
erofs_info, inode->i_sb,
"EXPERIMENTAL EROFS subpage compressed block support in use. Use at your own risk!");
@@ -271,10 +274,14 @@ static int erofs_fill_inode(struct inode *inode)
err = -EOPNOTSUPP;
#endif
} else {
- inode->i_mapping->a_ops = &erofs_raw_access_aops;
+ inode->i_mapping->a_ops = &erofs_aops;
#ifdef CONFIG_EROFS_FS_ONDEMAND
if (erofs_is_fscache_mode(inode->i_sb))
inode->i_mapping->a_ops = &erofs_fscache_access_aops;
+#endif
+#ifdef CONFIG_EROFS_FS_BACKED_BY_FILE
+ if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb)))
+ inode->i_mapping->a_ops = &erofs_fileio_aops;
#endif
}
out_unlock:
diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
index 9bf4fb1cfa09..9bc4dcfd06d7 100644
--- a/fs/erofs/internal.h
+++ b/fs/erofs/internal.h
@@ -372,6 +372,7 @@ struct erofs_map_dev {
struct erofs_fscache *m_fscache;
struct block_device *m_bdev;
struct dax_device *m_daxdev;
+ struct file *m_fp;
u64 m_dax_part_off;
erofs_off_t m_pa;
@@ -380,7 +381,8 @@ struct erofs_map_dev {
extern const struct super_operations erofs_sops;
-extern const struct address_space_operations erofs_raw_access_aops;
+extern const struct address_space_operations erofs_aops;
+extern const struct address_space_operations erofs_fileio_aops;
extern const struct address_space_operations z_erofs_aops;
extern const struct address_space_operations erofs_fscache_access_aops;
@@ -411,6 +413,9 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *dev);
int erofs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
u64 start, u64 len);
int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map);
+void erofs_onlinefolio_init(struct folio *folio);
+void erofs_onlinefolio_split(struct folio *folio);
+void erofs_onlinefolio_end(struct folio *folio, int err);
struct inode *erofs_iget(struct super_block *sb, erofs_nid_t nid);
int erofs_getattr(struct mnt_idmap *idmap, const struct path *path,
struct kstat *stat, u32 request_mask,
diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
index 424f656cd765..350612f32ac6 100644
--- a/fs/erofs/zdata.c
+++ b/fs/erofs/zdata.c
@@ -122,42 +122,6 @@ static bool erofs_folio_is_managed(struct erofs_sb_info *sbi, struct folio *fo)
return fo->mapping == MNGD_MAPPING(sbi);
}
-/*
- * bit 30: I/O error occurred on this folio
- * bit 0 - 29: remaining parts to complete this folio
- */
-#define Z_EROFS_FOLIO_EIO (1 << 30)
-
-static void z_erofs_onlinefolio_init(struct folio *folio)
-{
- union {
- atomic_t o;
- void *v;
- } u = { .o = ATOMIC_INIT(1) };
-
- folio->private = u.v; /* valid only if file-backed folio is locked */
-}
-
-static void z_erofs_onlinefolio_split(struct folio *folio)
-{
- atomic_inc((atomic_t *)&folio->private);
-}
-
-static void z_erofs_onlinefolio_end(struct folio *folio, int err)
-{
- int orig, v;
-
- do {
- orig = atomic_read((atomic_t *)&folio->private);
- v = (orig - 1) | (err ? Z_EROFS_FOLIO_EIO : 0);
- } while (atomic_cmpxchg((atomic_t *)&folio->private, orig, v) != orig);
-
- if (v & ~Z_EROFS_FOLIO_EIO)
- return;
- folio->private = 0;
- folio_end_read(folio, !(v & Z_EROFS_FOLIO_EIO));
-}
-
#define Z_EROFS_ONSTACK_PAGES 32
/*
@@ -965,7 +929,7 @@ static int z_erofs_scan_folio(struct z_erofs_decompress_frontend *f,
int err = 0;
tight = (bs == PAGE_SIZE);
- z_erofs_onlinefolio_init(folio);
+ erofs_onlinefolio_init(folio);
do {
if (offset + end - 1 < map->m_la ||
offset + end - 1 >= map->m_la + map->m_llen) {
@@ -1024,7 +988,7 @@ static int z_erofs_scan_folio(struct z_erofs_decompress_frontend *f,
if (err)
break;
- z_erofs_onlinefolio_split(folio);
+ erofs_onlinefolio_split(folio);
if (f->pcl->pageofs_out != (map->m_la & ~PAGE_MASK))
f->pcl->multibases = true;
if (f->pcl->length < offset + end - map->m_la) {
@@ -1044,7 +1008,7 @@ static int z_erofs_scan_folio(struct z_erofs_decompress_frontend *f,
tight = (bs == PAGE_SIZE);
}
} while ((end = cur) > 0);
- z_erofs_onlinefolio_end(folio, err);
+ erofs_onlinefolio_end(folio, err);
return err;
}
@@ -1147,7 +1111,7 @@ static void z_erofs_fill_other_copies(struct z_erofs_decompress_backend *be,
cur += len;
}
kunmap_local(dst);
- z_erofs_onlinefolio_end(page_folio(bvi->bvec.page), err);
+ erofs_onlinefolio_end(page_folio(bvi->bvec.page), err);
list_del(p);
kfree(bvi);
}
@@ -1302,7 +1266,7 @@ static int z_erofs_decompress_pcluster(struct z_erofs_decompress_backend *be,
DBG_BUGON(z_erofs_page_is_invalidated(page));
if (!z_erofs_is_shortlived_page(page)) {
- z_erofs_onlinefolio_end(page_folio(page), err);
+ erofs_onlinefolio_end(page_folio(page), err);
continue;
}
if (pcl->algorithmformat != Z_EROFS_COMPRESSION_LZ4) {
--
2.43.5
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 2/4] erofs: support unencoded inodes for fileio
2024-09-05 9:30 ` [PATCH v3 2/4] erofs: support unencoded inodes for fileio Gao Xiang
@ 2024-09-05 9:44 ` Chao Yu
0 siblings, 0 replies; 30+ messages in thread
From: Chao Yu @ 2024-09-05 9:44 UTC (permalink / raw)
To: Gao Xiang, linux-erofs; +Cc: LKML
On 2024/9/5 17:30, Gao Xiang wrote:
> Since EROFS only needs to handle read requests in simple contexts,
> Just directly use vfs_iocb_iter_read() for data I/Os.
>
> Reviewed-by: Sandeep Dhavale <dhavale@google.com>
> Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Reviewed-by: Chao Yu <chao@kernel.org>
Thanks,
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 3/4] erofs: support compressed inodes for fileio
2024-08-30 3:28 ` [PATCH v2 3/4] erofs: support compressed " Gao Xiang
2024-09-01 20:47 ` Sandeep Dhavale
2024-09-05 9:01 ` Chao Yu
@ 2024-09-05 9:46 ` Chao Yu
2 siblings, 0 replies; 30+ messages in thread
From: Chao Yu @ 2024-09-05 9:46 UTC (permalink / raw)
To: Gao Xiang, linux-erofs; +Cc: LKML
On 2024/8/30 11:28, Gao Xiang wrote:
> Use pseudo bios just like the previous fscache approach since
> merged bio_vecs can be filled properly with unique interfaces.
>
> Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Reviewed-by: Chao Yu <chao@kernel.org>
Thanks,
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 1/4] erofs: add file-backed mount support
2024-08-30 3:28 [PATCH v2 1/4] erofs: add file-backed mount support Gao Xiang
` (6 preceding siblings ...)
2024-09-05 9:30 ` [PATCH v3 2/4] erofs: support unencoded inodes for fileio Gao Xiang
@ 2024-09-24 9:21 ` Geert Uytterhoeven
2024-09-24 9:29 ` Gao Xiang
2024-09-30 14:18 ` Jan Kara
7 siblings, 2 replies; 30+ messages in thread
From: Geert Uytterhoeven @ 2024-09-24 9:21 UTC (permalink / raw)
To: Gao Xiang
Cc: linux-erofs, LKML, Al Viro, Christian Brauner, Jan Kara, Linux FS Devel
Hi Gao,
CC vfs
On Fri, Aug 30, 2024 at 5:29 AM Gao Xiang <hsiangkao@linux.alibaba.com> wrote:
> It actually has been around for years: For containers and other sandbox
> use cases, there will be thousands (and even more) of authenticated
> (sub)images running on the same host, unlike OS images.
>
> Of course, all scenarios can use the same EROFS on-disk format, but
> bdev-backed mounts just work well for OS images since golden data is
> dumped into real block devices. However, it's somewhat hard for
> container runtimes to manage and isolate so many unnecessary virtual
> block devices safely and efficiently [1]: they just look like a burden
> to orchestrators and file-backed mounts are preferred indeed. There
> were already enough attempts such as Incremental FS, the original
> ComposeFS and PuzzleFS acting in the same way for immutable fses. As
> for current EROFS users, ComposeFS, containerd and Android APEXs will
> be directly benefited from it.
>
> On the other hand, previous experimental feature "erofs over fscache"
> was once also intended to provide a similar solution (inspired by
> Incremental FS discussion [2]), but the following facts show file-backed
> mounts will be a better approach:
> - Fscache infrastructure has recently been moved into new Netfslib
> which is an unexpected dependency to EROFS really, although it
> originally claims "it could be used for caching other things such as
> ISO9660 filesystems too." [3]
>
> - It takes an unexpectedly long time to upstream Fscache/Cachefiles
> enhancements. For example, the failover feature took more than
> one year, and the deamonless feature is still far behind now;
>
> - Ongoing HSM "fanotify pre-content hooks" [4] together with this will
> perfectly supersede "erofs over fscache" in a simpler way since
> developers (mainly containerd folks) could leverage their existing
> caching mechanism entirely in userspace instead of strictly following
> the predefined in-kernel caching tree hierarchy.
>
> After "fanotify pre-content hooks" lands upstream to provide the same
> functionality, "erofs over fscache" will be removed then (as an EROFS
> internal improvement and EROFS will not have to bother with on-demand
> fetching and/or caching improvements anymore.)
>
> [1] https://github.com/containers/storage/pull/2039
> [2] https://lore.kernel.org/r/CAOQ4uxjbVxnubaPjVaGYiSwoGDTdpWbB=w_AeM6YM=zVixsUfQ@mail.gmail.com
> [3] https://docs.kernel.org/filesystems/caching/fscache.html
> [4] https://lore.kernel.org/r/cover.1723670362.git.josef@toxicpanda.com
>
> Closes: https://github.com/containers/composefs/issues/144
> Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Thanks for your patch, which is now commit fb176750266a3d7f
("erofs: add file-backed mount support").
> ---
> v2:
> - should use kill_anon_super();
> - add O_LARGEFILE to support large files.
>
> fs/erofs/Kconfig | 17 ++++++++++
> fs/erofs/data.c | 35 ++++++++++++---------
> fs/erofs/inode.c | 5 ++-
> fs/erofs/internal.h | 11 +++++--
> fs/erofs/super.c | 76 +++++++++++++++++++++++++++++----------------
> 5 files changed, 100 insertions(+), 44 deletions(-)
>
> diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig
> index 7dcdce660cac..1428d0530e1c 100644
> --- a/fs/erofs/Kconfig
> +++ b/fs/erofs/Kconfig
> @@ -74,6 +74,23 @@ config EROFS_FS_SECURITY
>
> If you are not using a security module, say N.
>
> +config EROFS_FS_BACKED_BY_FILE
> + bool "File-backed EROFS filesystem support"
> + depends on EROFS_FS
> + default y
I am a bit reluctant to have this default to y, without an ack from
the VFS maintainers.
> + help
> + This allows EROFS to use filesystem image files directly, without
> + the intercession of loopback block devices or likewise. It is
> + particularly useful for container images with numerous blobs and
> + other sandboxes, where loop devices behave intricately. It can also
> + be used to simplify error-prone lifetime management of unnecessary
> + virtual block devices.
> +
> + Note that this feature, along with ongoing fanotify pre-content
> + hooks, will eventually replace "EROFS over fscache."
> +
> + If you don't want to enable this feature, say N.
> +
> config EROFS_FS_ZIP
> bool "EROFS Data Compression Support"
> depends on EROFS_FS
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 1/4] erofs: add file-backed mount support
2024-09-24 9:21 ` [PATCH v2 1/4] erofs: add file-backed mount support Geert Uytterhoeven
@ 2024-09-24 9:29 ` Gao Xiang
2024-09-30 14:18 ` Jan Kara
1 sibling, 0 replies; 30+ messages in thread
From: Gao Xiang @ 2024-09-24 9:29 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: linux-erofs, LKML, Al Viro, Christian Brauner, Jan Kara, Linux FS Devel
Hi Geert,
On 2024/9/24 17:21, Geert Uytterhoeven wrote:
> Hi Gao,
>
> CC vfs
>
> On Fri, Aug 30, 2024 at 5:29 AM Gao Xiang <hsiangkao@linux.alibaba.com> wrote:
>> It actually has been around for years: For containers and other sandbox
>> use cases, there will be thousands (and even more) of authenticated
>> (sub)images running on the same host, unlike OS images.
>>
>> Of course, all scenarios can use the same EROFS on-disk format, but
>> bdev-backed mounts just work well for OS images since golden data is
>> dumped into real block devices. However, it's somewhat hard for
>> container runtimes to manage and isolate so many unnecessary virtual
>> block devices safely and efficiently [1]: they just look like a burden
>> to orchestrators and file-backed mounts are preferred indeed. There
>> were already enough attempts such as Incremental FS, the original
>> ComposeFS and PuzzleFS acting in the same way for immutable fses. As
>> for current EROFS users, ComposeFS, containerd and Android APEXs will
>> be directly benefited from it.
>>
>> On the other hand, previous experimental feature "erofs over fscache"
>> was once also intended to provide a similar solution (inspired by
>> Incremental FS discussion [2]), but the following facts show file-backed
>> mounts will be a better approach:
>> - Fscache infrastructure has recently been moved into new Netfslib
>> which is an unexpected dependency to EROFS really, although it
>> originally claims "it could be used for caching other things such as
>> ISO9660 filesystems too." [3]
>>
>> - It takes an unexpectedly long time to upstream Fscache/Cachefiles
>> enhancements. For example, the failover feature took more than
>> one year, and the deamonless feature is still far behind now;
>>
>> - Ongoing HSM "fanotify pre-content hooks" [4] together with this will
>> perfectly supersede "erofs over fscache" in a simpler way since
>> developers (mainly containerd folks) could leverage their existing
>> caching mechanism entirely in userspace instead of strictly following
>> the predefined in-kernel caching tree hierarchy.
>>
>> After "fanotify pre-content hooks" lands upstream to provide the same
>> functionality, "erofs over fscache" will be removed then (as an EROFS
>> internal improvement and EROFS will not have to bother with on-demand
>> fetching and/or caching improvements anymore.)
>>
>> [1] https://github.com/containers/storage/pull/2039
>> [2] https://lore.kernel.org/r/CAOQ4uxjbVxnubaPjVaGYiSwoGDTdpWbB=w_AeM6YM=zVixsUfQ@mail.gmail.com
>> [3] https://docs.kernel.org/filesystems/caching/fscache.html
>> [4] https://lore.kernel.org/r/cover.1723670362.git.josef@toxicpanda.com
>>
>> Closes: https://github.com/containers/composefs/issues/144
>> Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
>
> Thanks for your patch, which is now commit fb176750266a3d7f
> ("erofs: add file-backed mount support").
>
>> ---
>> v2:
>> - should use kill_anon_super();
>> - add O_LARGEFILE to support large files.
>>
>> fs/erofs/Kconfig | 17 ++++++++++
>> fs/erofs/data.c | 35 ++++++++++++---------
>> fs/erofs/inode.c | 5 ++-
>> fs/erofs/internal.h | 11 +++++--
>> fs/erofs/super.c | 76 +++++++++++++++++++++++++++++----------------
>> 5 files changed, 100 insertions(+), 44 deletions(-)
>>
>> diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig
>> index 7dcdce660cac..1428d0530e1c 100644
>> --- a/fs/erofs/Kconfig
>> +++ b/fs/erofs/Kconfig
>> @@ -74,6 +74,23 @@ config EROFS_FS_SECURITY
>>
>> If you are not using a security module, say N.
>>
>> +config EROFS_FS_BACKED_BY_FILE
>> + bool "File-backed EROFS filesystem support"
>> + depends on EROFS_FS
>> + default y
>
> I am a bit reluctant to have this default to y, without an ack from
> the VFS maintainers.
It don't touch any VFS stuffs so I didn't cc -fsdevel.
Okay, if VFS maintainers have any objection of this, I could turn
it into "default n", if not, I tend to leave it as "y" since I
believe it shouldn't be any risk of this feature (since EROFS is
only an immutable filesystem and I don't think out a context which
could be risky) with clear use cases and I've clearly documented
and showed in the commit message and upstream pull request.
Thanks,
Gao Xiang
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 1/4] erofs: add file-backed mount support
2024-09-24 9:21 ` [PATCH v2 1/4] erofs: add file-backed mount support Geert Uytterhoeven
2024-09-24 9:29 ` Gao Xiang
@ 2024-09-30 14:18 ` Jan Kara
2024-09-30 14:22 ` Geert Uytterhoeven
2024-10-02 6:12 ` Christian Brauner
1 sibling, 2 replies; 30+ messages in thread
From: Jan Kara @ 2024-09-30 14:18 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Gao Xiang, linux-erofs, LKML, Al Viro, Christian Brauner,
Jan Kara, Linux FS Devel
Hi!
On Tue 24-09-24 11:21:59, Geert Uytterhoeven wrote:
> On Fri, Aug 30, 2024 at 5:29 AM Gao Xiang <hsiangkao@linux.alibaba.com> wrote:
> > It actually has been around for years: For containers and other sandbox
> > use cases, there will be thousands (and even more) of authenticated
> > (sub)images running on the same host, unlike OS images.
> >
> > Of course, all scenarios can use the same EROFS on-disk format, but
> > bdev-backed mounts just work well for OS images since golden data is
> > dumped into real block devices. However, it's somewhat hard for
> > container runtimes to manage and isolate so many unnecessary virtual
> > block devices safely and efficiently [1]: they just look like a burden
> > to orchestrators and file-backed mounts are preferred indeed. There
> > were already enough attempts such as Incremental FS, the original
> > ComposeFS and PuzzleFS acting in the same way for immutable fses. As
> > for current EROFS users, ComposeFS, containerd and Android APEXs will
> > be directly benefited from it.
> >
> > On the other hand, previous experimental feature "erofs over fscache"
> > was once also intended to provide a similar solution (inspired by
> > Incremental FS discussion [2]), but the following facts show file-backed
> > mounts will be a better approach:
> > - Fscache infrastructure has recently been moved into new Netfslib
> > which is an unexpected dependency to EROFS really, although it
> > originally claims "it could be used for caching other things such as
> > ISO9660 filesystems too." [3]
> >
> > - It takes an unexpectedly long time to upstream Fscache/Cachefiles
> > enhancements. For example, the failover feature took more than
> > one year, and the deamonless feature is still far behind now;
> >
> > - Ongoing HSM "fanotify pre-content hooks" [4] together with this will
> > perfectly supersede "erofs over fscache" in a simpler way since
> > developers (mainly containerd folks) could leverage their existing
> > caching mechanism entirely in userspace instead of strictly following
> > the predefined in-kernel caching tree hierarchy.
> >
> > After "fanotify pre-content hooks" lands upstream to provide the same
> > functionality, "erofs over fscache" will be removed then (as an EROFS
> > internal improvement and EROFS will not have to bother with on-demand
> > fetching and/or caching improvements anymore.)
> >
> > [1] https://github.com/containers/storage/pull/2039
> > [2] https://lore.kernel.org/r/CAOQ4uxjbVxnubaPjVaGYiSwoGDTdpWbB=w_AeM6YM=zVixsUfQ@mail.gmail.com
> > [3] https://docs.kernel.org/filesystems/caching/fscache.html
> > [4] https://lore.kernel.org/r/cover.1723670362.git.josef@toxicpanda.com
> >
> > Closes: https://github.com/containers/composefs/issues/144
> > Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
>
> Thanks for your patch, which is now commit fb176750266a3d7f
> ("erofs: add file-backed mount support").
>
> > ---
> > v2:
> > - should use kill_anon_super();
> > - add O_LARGEFILE to support large files.
> >
> > fs/erofs/Kconfig | 17 ++++++++++
> > fs/erofs/data.c | 35 ++++++++++++---------
> > fs/erofs/inode.c | 5 ++-
> > fs/erofs/internal.h | 11 +++++--
> > fs/erofs/super.c | 76 +++++++++++++++++++++++++++++----------------
> > 5 files changed, 100 insertions(+), 44 deletions(-)
> >
> > diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig
> > index 7dcdce660cac..1428d0530e1c 100644
> > --- a/fs/erofs/Kconfig
> > +++ b/fs/erofs/Kconfig
> > @@ -74,6 +74,23 @@ config EROFS_FS_SECURITY
> >
> > If you are not using a security module, say N.
> >
> > +config EROFS_FS_BACKED_BY_FILE
> > + bool "File-backed EROFS filesystem support"
> > + depends on EROFS_FS
> > + default y
>
> I am a bit reluctant to have this default to y, without an ack from
> the VFS maintainers.
Well, we generally let filesystems do whatever they decide to do unless it
is a affecting stability / security / maintainability of the whole system.
In this case I don't see anything that would be substantially different
than if we go through a loop device. So although the feature looks somewhat
unusual I don't see a reason to nack it or otherwise interfere with
whatever the fs maintainer wants to do. Are you concerned about a
particular problem?
Honza
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 1/4] erofs: add file-backed mount support
2024-09-30 14:18 ` Jan Kara
@ 2024-09-30 14:22 ` Geert Uytterhoeven
2024-10-02 6:12 ` Christian Brauner
1 sibling, 0 replies; 30+ messages in thread
From: Geert Uytterhoeven @ 2024-09-30 14:22 UTC (permalink / raw)
To: Jan Kara
Cc: Gao Xiang, linux-erofs, LKML, Al Viro, Christian Brauner, Linux FS Devel
Hi Jan,
On Mon, Sep 30, 2024 at 4:18 PM Jan Kara <jack@suse.cz> wrote:
> On Tue 24-09-24 11:21:59, Geert Uytterhoeven wrote:
> > On Fri, Aug 30, 2024 at 5:29 AM Gao Xiang <hsiangkao@linux.alibaba.com> wrote:
> > > It actually has been around for years: For containers and other sandbox
> > > use cases, there will be thousands (and even more) of authenticated
> > > (sub)images running on the same host, unlike OS images.
> > >
> > > Of course, all scenarios can use the same EROFS on-disk format, but
> > > bdev-backed mounts just work well for OS images since golden data is
> > > dumped into real block devices. However, it's somewhat hard for
> > > container runtimes to manage and isolate so many unnecessary virtual
> > > block devices safely and efficiently [1]: they just look like a burden
> > > to orchestrators and file-backed mounts are preferred indeed. There
> > > were already enough attempts such as Incremental FS, the original
> > > ComposeFS and PuzzleFS acting in the same way for immutable fses. As
> > > for current EROFS users, ComposeFS, containerd and Android APEXs will
> > > be directly benefited from it.
> > >
> > > On the other hand, previous experimental feature "erofs over fscache"
> > > was once also intended to provide a similar solution (inspired by
> > > Incremental FS discussion [2]), but the following facts show file-backed
> > > mounts will be a better approach:
> > > - Fscache infrastructure has recently been moved into new Netfslib
> > > which is an unexpected dependency to EROFS really, although it
> > > originally claims "it could be used for caching other things such as
> > > ISO9660 filesystems too." [3]
> > >
> > > - It takes an unexpectedly long time to upstream Fscache/Cachefiles
> > > enhancements. For example, the failover feature took more than
> > > one year, and the deamonless feature is still far behind now;
> > >
> > > - Ongoing HSM "fanotify pre-content hooks" [4] together with this will
> > > perfectly supersede "erofs over fscache" in a simpler way since
> > > developers (mainly containerd folks) could leverage their existing
> > > caching mechanism entirely in userspace instead of strictly following
> > > the predefined in-kernel caching tree hierarchy.
> > >
> > > After "fanotify pre-content hooks" lands upstream to provide the same
> > > functionality, "erofs over fscache" will be removed then (as an EROFS
> > > internal improvement and EROFS will not have to bother with on-demand
> > > fetching and/or caching improvements anymore.)
> > >
> > > [1] https://github.com/containers/storage/pull/2039
> > > [2] https://lore.kernel.org/r/CAOQ4uxjbVxnubaPjVaGYiSwoGDTdpWbB=w_AeM6YM=zVixsUfQ@mail.gmail.com
> > > [3] https://docs.kernel.org/filesystems/caching/fscache.html
> > > [4] https://lore.kernel.org/r/cover.1723670362.git.josef@toxicpanda.com
> > >
> > > Closes: https://github.com/containers/composefs/issues/144
> > > Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
> >
> > Thanks for your patch, which is now commit fb176750266a3d7f
> > ("erofs: add file-backed mount support").
> >
> > > ---
> > > v2:
> > > - should use kill_anon_super();
> > > - add O_LARGEFILE to support large files.
> > >
> > > fs/erofs/Kconfig | 17 ++++++++++
> > > fs/erofs/data.c | 35 ++++++++++++---------
> > > fs/erofs/inode.c | 5 ++-
> > > fs/erofs/internal.h | 11 +++++--
> > > fs/erofs/super.c | 76 +++++++++++++++++++++++++++++----------------
> > > 5 files changed, 100 insertions(+), 44 deletions(-)
> > >
> > > diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig
> > > index 7dcdce660cac..1428d0530e1c 100644
> > > --- a/fs/erofs/Kconfig
> > > +++ b/fs/erofs/Kconfig
> > > @@ -74,6 +74,23 @@ config EROFS_FS_SECURITY
> > >
> > > If you are not using a security module, say N.
> > >
> > > +config EROFS_FS_BACKED_BY_FILE
> > > + bool "File-backed EROFS filesystem support"
> > > + depends on EROFS_FS
> > > + default y
> >
> > I am a bit reluctant to have this default to y, without an ack from
> > the VFS maintainers.
>
> Well, we generally let filesystems do whatever they decide to do unless it
> is a affecting stability / security / maintainability of the whole system.
> In this case I don't see anything that would be substantially different
> than if we go through a loop device. So although the feature looks somewhat
> unusual I don't see a reason to nack it or otherwise interfere with
> whatever the fs maintainer wants to do. Are you concerned about a
> particular problem?
I was just wondering if there are any issues with accessing files directly.
If you're fine with it, I am, too.
Thanks!
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 1/4] erofs: add file-backed mount support
2024-09-30 14:18 ` Jan Kara
2024-09-30 14:22 ` Geert Uytterhoeven
@ 2024-10-02 6:12 ` Christian Brauner
2024-10-02 11:25 ` Gao Xiang
1 sibling, 1 reply; 30+ messages in thread
From: Christian Brauner @ 2024-10-02 6:12 UTC (permalink / raw)
To: Jan Kara
Cc: Geert Uytterhoeven, Gao Xiang, linux-erofs, LKML, Al Viro,
Linux FS Devel
On Mon, Sep 30, 2024 at 04:18:19PM GMT, Jan Kara wrote:
> Hi!
>
> On Tue 24-09-24 11:21:59, Geert Uytterhoeven wrote:
> > On Fri, Aug 30, 2024 at 5:29 AM Gao Xiang <hsiangkao@linux.alibaba.com> wrote:
> > > It actually has been around for years: For containers and other sandbox
> > > use cases, there will be thousands (and even more) of authenticated
> > > (sub)images running on the same host, unlike OS images.
> > >
> > > Of course, all scenarios can use the same EROFS on-disk format, but
> > > bdev-backed mounts just work well for OS images since golden data is
> > > dumped into real block devices. However, it's somewhat hard for
> > > container runtimes to manage and isolate so many unnecessary virtual
> > > block devices safely and efficiently [1]: they just look like a burden
> > > to orchestrators and file-backed mounts are preferred indeed. There
> > > were already enough attempts such as Incremental FS, the original
> > > ComposeFS and PuzzleFS acting in the same way for immutable fses. As
> > > for current EROFS users, ComposeFS, containerd and Android APEXs will
> > > be directly benefited from it.
> > >
> > > On the other hand, previous experimental feature "erofs over fscache"
> > > was once also intended to provide a similar solution (inspired by
> > > Incremental FS discussion [2]), but the following facts show file-backed
> > > mounts will be a better approach:
> > > - Fscache infrastructure has recently been moved into new Netfslib
> > > which is an unexpected dependency to EROFS really, although it
> > > originally claims "it could be used for caching other things such as
> > > ISO9660 filesystems too." [3]
> > >
> > > - It takes an unexpectedly long time to upstream Fscache/Cachefiles
> > > enhancements. For example, the failover feature took more than
> > > one year, and the deamonless feature is still far behind now;
> > >
> > > - Ongoing HSM "fanotify pre-content hooks" [4] together with this will
> > > perfectly supersede "erofs over fscache" in a simpler way since
> > > developers (mainly containerd folks) could leverage their existing
> > > caching mechanism entirely in userspace instead of strictly following
> > > the predefined in-kernel caching tree hierarchy.
> > >
> > > After "fanotify pre-content hooks" lands upstream to provide the same
> > > functionality, "erofs over fscache" will be removed then (as an EROFS
> > > internal improvement and EROFS will not have to bother with on-demand
> > > fetching and/or caching improvements anymore.)
> > >
> > > [1] https://github.com/containers/storage/pull/2039
> > > [2] https://lore.kernel.org/r/CAOQ4uxjbVxnubaPjVaGYiSwoGDTdpWbB=w_AeM6YM=zVixsUfQ@mail.gmail.com
> > > [3] https://docs.kernel.org/filesystems/caching/fscache.html
> > > [4] https://lore.kernel.org/r/cover.1723670362.git.josef@toxicpanda.com
> > >
> > > Closes: https://github.com/containers/composefs/issues/144
> > > Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
> >
> > Thanks for your patch, which is now commit fb176750266a3d7f
> > ("erofs: add file-backed mount support").
> >
> > > ---
> > > v2:
> > > - should use kill_anon_super();
> > > - add O_LARGEFILE to support large files.
> > >
> > > fs/erofs/Kconfig | 17 ++++++++++
> > > fs/erofs/data.c | 35 ++++++++++++---------
> > > fs/erofs/inode.c | 5 ++-
> > > fs/erofs/internal.h | 11 +++++--
> > > fs/erofs/super.c | 76 +++++++++++++++++++++++++++++----------------
> > > 5 files changed, 100 insertions(+), 44 deletions(-)
> > >
> > > diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig
> > > index 7dcdce660cac..1428d0530e1c 100644
> > > --- a/fs/erofs/Kconfig
> > > +++ b/fs/erofs/Kconfig
> > > @@ -74,6 +74,23 @@ config EROFS_FS_SECURITY
> > >
> > > If you are not using a security module, say N.
> > >
> > > +config EROFS_FS_BACKED_BY_FILE
> > > + bool "File-backed EROFS filesystem support"
> > > + depends on EROFS_FS
> > > + default y
> >
> > I am a bit reluctant to have this default to y, without an ack from
> > the VFS maintainers.
>
> Well, we generally let filesystems do whatever they decide to do unless it
> is a affecting stability / security / maintainability of the whole system.
> In this case I don't see anything that would be substantially different
> than if we go through a loop device. So although the feature looks somewhat
> unusual I don't see a reason to nack it or otherwise interfere with
> whatever the fs maintainer wants to do. Are you concerned about a
> particular problem?
I see no reason to nak it either.
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 1/4] erofs: add file-backed mount support
2024-10-02 6:12 ` Christian Brauner
@ 2024-10-02 11:25 ` Gao Xiang
0 siblings, 0 replies; 30+ messages in thread
From: Gao Xiang @ 2024-10-02 11:25 UTC (permalink / raw)
To: Christian Brauner, Jan Kara
Cc: Geert Uytterhoeven, linux-erofs, LKML, Al Viro, Linux FS Devel
On 2024/10/2 14:12, Christian Brauner wrote:
> On Mon, Sep 30, 2024 at 04:18:19PM GMT, Jan Kara wrote:
..
>>>>
>>>> diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig
>>>> index 7dcdce660cac..1428d0530e1c 100644
>>>> --- a/fs/erofs/Kconfig
>>>> +++ b/fs/erofs/Kconfig
>>>> @@ -74,6 +74,23 @@ config EROFS_FS_SECURITY
>>>>
>>>> If you are not using a security module, say N.
>>>>
>>>> +config EROFS_FS_BACKED_BY_FILE
>>>> + bool "File-backed EROFS filesystem support"
>>>> + depends on EROFS_FS
>>>> + default y
>>>
>>> I am a bit reluctant to have this default to y, without an ack from
>>> the VFS maintainers.
>>
>> Well, we generally let filesystems do whatever they decide to do unless it
>> is a affecting stability / security / maintainability of the whole system.
>> In this case I don't see anything that would be substantially different
>> than if we go through a loop device. So although the feature looks somewhat
>> unusual I don't see a reason to nack it or otherwise interfere with
>> whatever the fs maintainer wants to do. Are you concerned about a
>> particular problem?
>
> I see no reason to nak it either.
Thanks all for taking time on writing down these!
Unfortunately, fanotify pre-content hooks was't landed in 6.12 cycle
(which I think will be used in a lot of scenarios)..
I do hope it could be landed in the next cycle so I could clean up
the codebase then.
Thanks,
Gao Xiang
^ permalink raw reply [flat|nested] 30+ messages in thread
end of thread, other threads:[~2024-10-02 11:30 UTC | newest]
Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-08-30 3:28 [PATCH v2 1/4] erofs: add file-backed mount support Gao Xiang
2024-08-30 3:28 ` [PATCH v2 2/4] erofs: support unencoded inodes for fileio Gao Xiang
2024-08-31 0:19 ` Sandeep Dhavale
2024-08-31 2:54 ` Gao Xiang
2024-08-31 4:25 ` Sandeep Dhavale
2024-08-31 7:54 ` Gao Xiang
2024-09-01 20:47 ` Sandeep Dhavale
2024-09-04 6:56 ` Gao Xiang
2024-09-05 9:01 ` Chao Yu
2024-09-05 9:13 ` Gao Xiang
2024-08-30 3:28 ` [PATCH v2 3/4] erofs: support compressed " Gao Xiang
2024-09-01 20:47 ` Sandeep Dhavale
2024-09-05 9:01 ` Chao Yu
2024-09-05 9:14 ` Gao Xiang
2024-09-05 9:46 ` Chao Yu
2024-08-30 3:28 ` [PATCH v2 4/4] erofs: mark experimental fscache backend deprecated Gao Xiang
2024-09-01 20:48 ` Sandeep Dhavale
2024-09-05 9:03 ` Chao Yu
2024-08-31 0:30 ` [PATCH v2 1/4] erofs: add file-backed mount support Sandeep Dhavale
2024-08-31 3:00 ` Gao Xiang
2024-09-01 20:46 ` Sandeep Dhavale
2024-09-05 8:22 ` Chao Yu
2024-09-05 9:30 ` [PATCH v3 2/4] erofs: support unencoded inodes for fileio Gao Xiang
2024-09-05 9:44 ` Chao Yu
2024-09-24 9:21 ` [PATCH v2 1/4] erofs: add file-backed mount support Geert Uytterhoeven
2024-09-24 9:29 ` Gao Xiang
2024-09-30 14:18 ` Jan Kara
2024-09-30 14:22 ` Geert Uytterhoeven
2024-10-02 6:12 ` Christian Brauner
2024-10-02 11:25 ` Gao Xiang
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®