[PATCH 05/10] libext2fs: add tagged block IO for better caching

Darrick J. Wong
2025-05-22T00:09:33Z

Pass inode numbers from the fileio.c code through the io manager to the
unix io manager so that we can manage the disk cache more effectively.
In the next few patches we'll need the ability to flush and invalidate
the caches for specific files.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
lib/ext2fs/ext2_io.h | 25 +++++++++++++++++++++-
debian/libext2fs2t64.symbols | 4 ++++
lib/ext2fs/fileio.c | 14 +++++++-----
lib/ext2fs/io_manager.c | 48 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 84 insertions(+), 7 deletions(-)

diff --git a/lib/ext2fs/ext2_io.h b/lib/ext2fs/ext2_io.h
index bab7f2a6a44b81..64b35b31d669e7 100644
--- a/lib/ext2fs/ext2_io.h
+++ b/lib/ext2fs/ext2_io.h
@@ -39,6 +39,11 @@ typedef struct struct_io_stats *io_stats;

#define io_channel_discard_zeroes_data(i) (i->flags & CHANNEL_FLAGS_DISCARD_ZEROES)

+typedef unsigned int io_channel_tag_t;
+
+/* I/O operation has no associated tag */
+#define IO_CHANNEL_TAG_NULL (0)
+
struct struct_io_channel {
errcode_t magic;
io_manager manager;
@@ -105,7 +110,15 @@ struct struct_io_manager {
errcode_t (*get_fd)(io_channel channel, int *fd);
errcode_t (*invalidate_blk)(io_channel channel,
unsigned long long block);
- long reserved[12];
+ errcode_t (*read_tagblk)(io_channel channel, io_channel_tag_t tag,
+ unsigned long long block, int count,
+ void *data);
+ errcode_t (*write_tagblk)(io_channel channel, io_channel_tag_t tag,
+ unsigned long long block, int count,
+ const void *data);
+ errcode_t (*flush_tag)(io_channel channel, io_channel_tag_t tag);
+ errcode_t (*invalidate_tag)(io_channel channel, io_channel_tag_t tag);
+ long reserved[8];
};

#define IO_FLAG_RW 0x0001
@@ -134,9 +147,17 @@ extern errcode_t io_channel_write_byte(io_channel channel,
extern errcode_t io_channel_read_blk64(io_channel channel,
unsigned long long block,
int count, void *data);
+extern errcode_t io_channel_read_tagblk(io_channel channel,
+ io_channel_tag_t tag,
+ unsigned long long block, int count,
+ void *data);
extern errcode_t io_channel_write_blk64(io_channel channel,
unsigned long long block,
int count, const void *data);
+extern errcode_t io_channel_write_tagblk(io_channel channel,
+ io_channel_tag_t tag,
+ unsigned long long block, int count,
+ const void *data);
extern errcode_t io_channel_discard(io_channel channel,
unsigned long long block,
unsigned long long count);
@@ -151,6 +172,8 @@ extern errcode_t io_channel_cache_readahead(io_channel io,
extern errcode_t io_channel_fd(io_channel io, int *fd);
extern errcode_t io_channel_invalidate_blk(io_channel io,
unsigned long long block);
+extern errcode_t io_channel_flush_tag(io_channel io, io_channel_tag_t tag);
+extern errcode_t io_channel_invalidate_tag(io_channel io, io_channel_tag_t tag);

#ifdef _WIN32
/* windows_io.c */
diff --git a/debian/libext2fs2t64.symbols b/debian/libext2fs2t64.symbols
index 13870c4b545b2f..87ed63155702e0 100644
--- a/debian/libext2fs2t64.symbols
+++ b/debian/libext2fs2t64.symbols
@@ -689,11 +689,15 @@ libext2fs.so.2 libext2fs2t64 #MINVER#
io_channel_cache_readahead@Base 1.43
io_channel_discard@Base 1.42
io_channel_fd@Base 1.47.3
+ io_channel_flush_tag@Base 1.47.3
io_channel_invalidate_blk@Base 1.47.3
+ io_channel_invalidate_tag@Base 1.47.3
io_channel_read_blk64@Base 1.41.1
+ io_channel_read_tagblk@Base 1.47.3
io_channel_set_options@Base 1.37
io_channel_write_blk64@Base 1.41.1
io_channel_write_byte@Base 1.37
+ io_channel_write_tagblk@Base 1.47.3
io_channel_zeroout@Base 1.43
qcow2_read_header@Base 1.42
qcow2_write_raw_image@Base 1.42
diff --git a/lib/ext2fs/fileio.c b/lib/ext2fs/fileio.c
index 818f7f05420029..1b7e88d990036b 100644
--- a/lib/ext2fs/fileio.c
+++ b/lib/ext2fs/fileio.c
@@ -167,7 +167,8 @@ errcode_t ext2fs_file_flush(ext2_file_t file)
return retval;
}

- retval = io_channel_write_blk64(fs->io, file->physblock, 1, file->buf);
+ retval = io_channel_write_tagblk(fs->io, file->ino, file->physblock,
+ 1, file->buf);
if (retval)
return retval;

@@ -220,9 +221,10 @@ static errcode_t load_buffer(ext2_file_t file, int dontfill)
if (!dontfill) {
if (file->physblock &&
!(ret_flags & BMAP_RET_UNINIT)) {
- retval = io_channel_read_blk64(fs->io,
- file->physblock,
- 1, file->buf);
+ retval = io_channel_read_tagblk(fs->io,
+ file->ino,
+ file->physblock,
+ 1, file->buf);
if (retval)
return retval;
} else
@@ -603,13 +605,13 @@ static errcode_t ext2fs_file_zero_past_offset(ext2_file_t file,
return retval;

/* Read/zero/write block */
- retval = io_channel_read_blk64(fs->io, blk, 1, b);
+ retval = io_channel_read_tagblk(fs->io, file->ino, blk, 1, b);
if (retval)
goto out;

memset(b + off, 0, fs->blocksize - off);

- retval = io_channel_write_blk64(fs->io, blk, 1, b);
+ retval = io_channel_write_tagblk(fs->io, file->ino, blk, 1, b);
if (retval)
goto out;

diff --git a/lib/ext2fs/io_manager.c b/lib/ext2fs/io_manager.c
index aa7fc58b846be8..357a3bc7698129 100644
--- a/lib/ext2fs/io_manager.c
+++ b/lib/ext2fs/io_manager.c
@@ -85,6 +85,22 @@ errcode_t io_channel_read_blk64(io_channel channel, unsigned long long block,
count, data);
}

+errcode_t io_channel_read_tagblk(io_channel channel, io_channel_tag_t tag,
+ unsigned long long block, int count,
+ void *data)
+{
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+
+ if (channel->manager->read_tagblk)
+ return (channel->manager->read_tagblk)(channel, tag, block,
+ count, data);
+
+ if (tag != IO_CHANNEL_TAG_NULL)
+ return EXT2_ET_OP_NOT_SUPPORTED;
+
+ return io_channel_read_blk64(channel, block, count, data);
+}
+
errcode_t io_channel_write_blk64(io_channel channel, unsigned long long block,
int count, const void *data)
{
@@ -101,6 +117,22 @@ errcode_t io_channel_write_blk64(io_channel channel, unsigned long long block,
count, data);
}

+errcode_t io_channel_write_tagblk(io_channel channel, io_channel_tag_t tag,
+ unsigned long long block, int count,
+ const void *data)
+{
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+
+ if (channel->manager->write_tagblk)
+ return (channel->manager->write_tagblk)(channel, tag, block,
+ count, data);
+
+ if (tag != IO_CHANNEL_TAG_NULL)
+ return EXT2_ET_OP_NOT_SUPPORTED;
+
+ return io_channel_write_blk64(channel, block, count, data);
+}
+
errcode_t io_channel_discard(io_channel channel, unsigned long long block,
unsigned long long count)
{
@@ -166,3 +198,19 @@ errcode_t io_channel_invalidate_blk(io_channel io, unsigned long long block)

return io->manager->invalidate_blk(io, block);
}
+
+errcode_t io_channel_flush_tag(io_channel io, io_channel_tag_t tag)
+{
+ if (!io->manager->flush_tag && tag != IO_CHANNEL_TAG_NULL)
+ return EXT2_ET_OP_NOT_SUPPORTED;
+
+ return io->manager->flush_tag(io, tag);
+}
+
+errcode_t io_channel_invalidate_tag(io_channel io, io_channel_tag_t tag)
+{
+ if (!io->manager->invalidate_tag && tag != IO_CHANNEL_TAG_NULL)
+ return EXT2_ET_OP_NOT_SUPPORTED;
+
+ return io->manager->invalidate_tag(io, tag);
+}