* [PATCH v4 0/3] minix: convert to iomap
@ 2026-08-26 21:41 Jeremy Bingham
2026-08-26 21:41 ` [PATCH v4 1/3] iomap: add iomap_symlink_write Jeremy Bingham
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Jeremy Bingham @ 2026-08-26 21:41 UTC (permalink / raw)
To: linux-fsdevel
Cc: linux-kernel, brauner, jkoolstra, jack, djwong, viro, hch,
Jeremy Bingham
This is version 4 of the minix iomap conversion patch series. Versions 1
and 2 had issues uncovered by syzbot. Version 3 fixed those and
addressed some critiques given by Darrick J. Wong, while in turn having
issues pointed out by Christoph Hellwig and Darrick J. Wong including
having direct I/O in the patch series and proper testing. This version
addresses those criticisms.
The rationale for converting minix to use iomap instead of buffer heads
is pretty simple: it both provides a very basic example of a filesystem
using iomap, and it makes it easier to keep the minix filesystem in the
kernel in the future. It is a rarely used bit of computer history, but
it's one that's significant to Linux's early history. Plus, filesystems
are a hard subject to approach. Having a very simple filesystem in the
mainline kernel tree as an example for aspiring kernel filesystem
hackers would be good to help them learn.
Per Christoph Hellwig's remarks in the previous version of this patch
series, the direct I/O support has been removed. The iomap_symlink_write
function has also been reworked to be more useful for symlinks. The
original implementation of that function was proposed by Darrick J. Wong
as an alternative to the custom buffer head implementation symlink
function I had previously that sidestepped iomap for symlinks entirely.
One thing reviewers may find in here that seems very odd is the way that
iomap.c is included in itree_v1.c and itree_v2.c. This is in common with
how itree_common.c is included in those files and minix has been like
this at least as far back as the git history goes, and probably back
into the 90s. Not including iomap.c in itree_v1.c and itree_v2.c is
technically possible, but ended up being a massive headache to make
iomap.c stand by itself while itree_common.c is included in itree_v1.c
and itree_v2.c. Re-architecting minix to not require these separate
itree files with different versions of basic functions depending on the
version of the filesystem in question is possible, but definitely out of
scope for this patch series. If there is interest I could pick up a
patch series I made a little while back, dust it off, and give it
another go, but it's a pretty big change for a rarely touched
filesystem. I will defer to the collective wiser heads on that.
It is not possible to run xfstests against the minix filesystems with a
vanilla xfstests-dev repository because mkfs.minix and fsck.minix do not
support the proper options. There were some changes required in
'common/rc' to sidestep that issue, which fortunately did not require
any changes to mkfs.minix or fsck.minix. Once the tests were able to
run, there are also many tests that fail miserably because of inherent
limitations in all versions of the minix filesystems. Since these
failures aren't "bugs" as such, I then updated xfstests to skip the
tests that would never pass so I could focus on actual potential
failures and regressions.
Across all versions of the minix filesystems, there are 81 tests
skipped. Sixty-eight of them are unique to the minix V1 filesystem,
while an additional 13 are common to all versions. The exact breakdown
of skipped tests will be given at the end of this cover letter. I have
created a git repository forked from the main xfstests-dev repository to
share these changes for running minix tests. The minix branch can be
found at https://github.com/ctdk/xfstests-dev/tree/minix.
Leaving the skipped tests out, the iomap patch does not introduce any
new failures compared to the baseline in the master linux branch. The
iomap patches do fix a test that fails on v1 and v3 (but not v2):
generic/472, which tests swapfiles. After the patch, v1 and v3 will
properly report that swapfiles are not supported and the test is
skipped. Other than that, everything is the same and there are no
regressions.
This patch series has also been verified to build between each patch
being applied. Additionally, the minix module continues to function
between each patch.
======
The breakdown of the skipped xfstests:
13 tests skipped for all minix versions:
003, 075, 112, 127, 169, 249, 338, 347, 363, 563, 616, 676, 759
These failures relate to atime/ctime semantics, fallocate not being
supported, copy_file_range not being supported, sendfile not being
supported, FS_IOC_GETXATTR not being supported, not supporting dm-thin
cleanup properly, not supporting cgroup2 writeback accounting, not
handling I/O errors while unmounting, and not supporting filenames long
enough to be able to run the test.
These tests test features that no version of minix supports.
68 skipped only for minix v1. Of those, 52 are skipped because the 64MB
minix v1 filesystem fills up while the test is running:
013, 035, 074, 080, 087, 089, 100, 126, 131, 215, 245, 246, 248, 257,
309, 310, 313, 346, 394, 409, 410, 411, 430, 431, 432, 433, 434, 438,
443, 464, 471, 564, 565, 585, 589, 632, 633, 637, 638, 639, 650, 696,
712, 713, 715, 718, 719, 723, 724, 725, 732, 736, 741, 742, 754, 763
2 are skipped because minix v1 does not support fallocate at all.
749, 758
4 fail because fallocate is unsupported and the 64MB filesystem limit.
340, 344, 345, 354
Another 6 tests fail for their own reasons:
124: An aligned vector rw pattern test. Fails with output mismatch.
132: Another aligned vector rw test. Fails because the v1 fs is too
small for large writes.
192: An atime persistence test. Fails because v1 atime/ctime is wonky.
428: DAX mmap test. Minix does not support DAX.
706: A seek sanity check. Fails for v1.
707: Testing directory modification race condition during rename. The v1
directory link limit is too small to run the test.
======
Jeremy Bingham (3):
iomap: add iomap_symlink_write
minix: add iomap functions and definitions
minix: finish wiring in iomap functions
fs/iomap/buffered-io.c | 34 +++++++++++
fs/minix/file.c | 30 +++++++++-
fs/minix/inode.c | 85 ++++++++++++++++++++++++----
fs/minix/iomap.c | 122 ++++++++++++++++++++++++++++++++++++++++
fs/minix/itree_common.c | 10 +++-
fs/minix/itree_v1.c | 25 +++++++-
fs/minix/itree_v2.c | 17 +++++-
fs/minix/minix.h | 23 +++++++-
fs/minix/namei.c | 7 ++-
include/linux/iomap.h | 3 +
10 files changed, 336 insertions(+), 20 deletions(-)
create mode 100644 fs/minix/iomap.c
--
2.47.3
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v4 1/3] iomap: add iomap_symlink_write
2026-08-26 21:41 [PATCH v4 0/3] minix: convert to iomap Jeremy Bingham
@ 2026-08-26 21:41 ` Jeremy Bingham
2026-08-26 21:41 ` [PATCH v4 2/3] minix: add iomap functions and definitions Jeremy Bingham
2026-08-26 21:41 ` [PATCH v4 3/3] minix: finish wiring in iomap functions Jeremy Bingham
2 siblings, 0 replies; 4+ messages in thread
From: Jeremy Bingham @ 2026-08-26 21:41 UTC (permalink / raw)
To: linux-fsdevel
Cc: linux-kernel, brauner, jkoolstra, jack, djwong, viro, hch,
Jeremy Bingham
Add a new iomap_symlink_write function as an iomap based equivalent to
page_symlink found in fs/namei.c. Part of being that equivalency is
behaving similarly to page_symlink. This function now expects the same
len as page_symlink, where len is the length of the null terminated
target string. The target is still written out without the trailing
null.
Suggested-by: Darrick J. Wong <djwong@kernel.org>
Suggested-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Jeremy Bingham <jbingham@gmail.com>
---
fs/iomap/buffered-io.c | 34 ++++++++++++++++++++++++++++++++++
include/linux/iomap.h | 3 +++
2 files changed, 37 insertions(+)
diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index 0a5ebfda90f1..5e8ac3fad671 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -2100,3 +2100,37 @@ iomap_writepages(struct iomap_writepage_ctx *wpc)
return error;
}
EXPORT_SYMBOL_GPL(iomap_writepages);
+
+int iomap_symlink_write(struct inode *inode, const char *target, int len,
+ const struct iomap_ops *ops,
+ const struct iomap_write_ops *write_ops, void *private)
+{
+ struct kvec vec = {
+ .iov_base = (void *)target,
+ .iov_len = len - 1,
+ };
+ struct iomap_iter iter = {
+ .inode = inode,
+ .pos = 0,
+ .len = len - 1,
+ .flags = IOMAP_WRITE,
+ .private = private,
+ };
+ struct iov_iter iov;
+ int ret;
+
+ iov_iter_kvec(&iov, ITER_SOURCE, &vec, 1, len - 1);
+
+ while ((ret = iomap_iter(&iter, ops)) > 0)
+ iter.status = iomap_write_iter(&iter, &iov, write_ops);
+
+ if (ret < 0)
+ return ret;
+
+ if (unlikely(iter.pos == 0))
+ return -EIO;
+
+ mark_inode_dirty(inode);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iomap_symlink_write);
diff --git a/include/linux/iomap.h b/include/linux/iomap.h
index 8c754eb974fb..ab27a3a5b8d2 100644
--- a/include/linux/iomap.h
+++ b/include/linux/iomap.h
@@ -471,6 +471,9 @@ loff_t iomap_seek_data(struct inode *inode, loff_t offset,
const struct iomap_ops *ops);
sector_t iomap_bmap(struct address_space *mapping, sector_t bno,
const struct iomap_ops *ops);
+int iomap_symlink_write(struct inode *inode, const char *target, int len,
+ const struct iomap_ops *ops,
+ const struct iomap_write_ops *write_ops, void *private);
/*
* Flags for iomap_ioend->io_flags.
--
2.47.3
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v4 2/3] minix: add iomap functions and definitions
2026-08-26 21:41 [PATCH v4 0/3] minix: convert to iomap Jeremy Bingham
2026-08-26 21:41 ` [PATCH v4 1/3] iomap: add iomap_symlink_write Jeremy Bingham
@ 2026-08-26 21:41 ` Jeremy Bingham
2026-08-26 21:41 ` [PATCH v4 3/3] minix: finish wiring in iomap functions Jeremy Bingham
2 siblings, 0 replies; 4+ messages in thread
From: Jeremy Bingham @ 2026-08-26 21:41 UTC (permalink / raw)
To: linux-fsdevel
Cc: linux-kernel, brauner, jkoolstra, jack, djwong, viro, hch,
Jeremy Bingham
Adds a new file, iomap.c, to fs/minix. This provides minix_iomap_begin,
which is the iomap-based version of get_block in itree_common.c. It also
provides minix_iomap_end, which is a no-op function required by the
iomap_ops struct. The minix_iomap_begin function also uses helper
functions to undo the nest of gotos inherited from get_block.
This patch also wires iomap.c into itree_v1.c and itree_v2.c, similarly
to how itree_common.c is included in those files, and exports version
specific versions of minix_iomap_begin and iomap_ops.
Also updates 'unsigned' to 'unsigned int' a few places that got picked
up by checkpatch.pl.
Suggested-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Jeremy Bingham <jbingham@gmail.com>
---
fs/minix/iomap.c | 122 ++++++++++++++++++++++++++++++++++++++++++++
fs/minix/itree_v1.c | 25 ++++++++-
fs/minix/itree_v2.c | 17 +++++-
fs/minix/minix.h | 21 +++++++-
4 files changed, 181 insertions(+), 4 deletions(-)
create mode 100644 fs/minix/iomap.c
diff --git a/fs/minix/iomap.c b/fs/minix/iomap.c
new file mode 100644
index 000000000000..90e016aaa01e
--- /dev/null
+++ b/fs/minix/iomap.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * iomap functions for minix.
+ */
+
+static inline void minix_chain_cleanup(Indirect *chain, Indirect *partial)
+{
+ while (partial > chain) {
+ brelse(partial->bh);
+ partial--;
+ }
+}
+
+static inline void minix_iomap_set_mapped(struct iomap *iomap, sector_t phys,
+ unsigned int blkbits, sector_t iblock)
+{
+ iomap->type = IOMAP_MAPPED;
+ iomap->addr = (u64)phys << blkbits;
+ iomap->length = 1 << blkbits;
+ iomap->offset = (u64)iblock << blkbits;
+}
+
+static inline void minix_iomap_set_hole(struct iomap *iomap,
+ unsigned int blkbits, sector_t iblock)
+{
+ iomap->type = IOMAP_HOLE;
+ iomap->addr = IOMAP_NULL_ADDR;
+ iomap->length = 1 << blkbits;
+ iomap->offset = (u64)iblock << blkbits;
+}
+
+/*
+ * minix_iomap_begin - map a file range to disk blocks. It acts as a replacement
+ * for get_block in itree_common.c, at least in the important ways, and is
+ * adapted from it, but it uses iomap instead of buffer_head.
+ */
+static int minix_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
+ unsigned int flags, struct iomap *iomap, struct iomap *srcmap)
+{
+ struct super_block *sb = inode->i_sb;
+ unsigned int blkbits = sb->s_blocksize_bits;
+ sector_t iblock = offset >> blkbits;
+ int create = flags & IOMAP_WRITE;
+
+ int offsets[DEPTH];
+ Indirect chain[DEPTH];
+ Indirect *partial;
+ int depth = block_to_path(inode, iblock, offsets);
+ int left;
+ int err = -EIO;
+
+ sector_t phys;
+
+ /* block is beyond max file size */
+ if (depth == 0)
+ return -EINVAL;
+
+ iomap->bdev = inode->i_sb->s_bdev;
+
+reread:
+ partial = get_branch(inode, depth, offsets, chain, &err);
+
+ /* Simplest case - block found, no allocation needed */
+ if (!partial) {
+ iomap->flags = 0;
+ phys = block_to_cpu(chain[depth - 1].key);
+ partial = chain+depth-1;
+ minix_iomap_set_mapped(iomap, phys, blkbits, iblock);
+ minix_chain_cleanup(chain, partial);
+ return err;
+ }
+
+ /* Next simple case - plain lookup or failed read of indirect block */
+ if (!create || err == -EIO) {
+ minix_iomap_set_hole(iomap, blkbits, iblock);
+ minix_chain_cleanup(chain, partial);
+ return err;
+ }
+
+ /*
+ * This is held over from the original get_block logic, where it
+ * acted as a guard in case truncate() deleted blocks from under that
+ * function. There should not be a race with iomap operations, but
+ * we're retaining the defensive coding here to be extra safe just in
+ * case.
+ */
+ if (err == -EAGAIN) {
+ minix_chain_cleanup(chain, partial);
+ goto reread;
+ }
+
+ left = (chain + depth) - partial;
+ err = alloc_branch(inode, left, offsets + (partial - chain), partial);
+ if (err) {
+ minix_chain_cleanup(chain, partial);
+ return err;
+ }
+
+ if (splice_branch(inode, chain, partial, left) < 0) {
+ minix_chain_cleanup(chain, partial);
+ goto reread;
+ }
+
+ /* Successful allocation, mapping it. */
+ iomap->flags = IOMAP_F_NEW;
+ phys = block_to_cpu(chain[depth - 1].key);
+ minix_iomap_set_mapped(iomap, phys, blkbits, iblock);
+ minix_chain_cleanup(chain, partial);
+
+ return err;
+}
+
+/*
+ * minix_iomap_end ends up being a nop; since minix doesn't have any extents or
+ * transactions to worry about, there isn't anything to update here. The on-disk
+ * indirect blocks get dirtied in minix_iomap_begin.
+ */
+static int minix_iomap_end(struct inode *inode, loff_t offset, loff_t length,
+ ssize_t written, unsigned int flags, struct iomap *iomap)
+{
+ return 0;
+}
diff --git a/fs/minix/itree_v1.c b/fs/minix/itree_v1.c
index 1fed906042aa..58c29f4443d3 100644
--- a/fs/minix/itree_v1.c
+++ b/fs/minix/itree_v1.c
@@ -49,6 +49,18 @@ static int block_to_path(struct inode * inode, long block, int offsets[DEPTH])
}
#include "itree_common.c"
+/* NOTA BENE:
+ *
+ * This is icky to me, but at the same time having it be a standalone C file
+ * that's compiled to object form and linked separately like it is in xiafs is
+ * much nastier in minix because of the different versions of the minix fs that
+ * have some very, very different aspects, like the size of block_t. I don't
+ * like it, but since minix already has this pattern where a common itree file
+ * is included in the itree_v1 and itree_v2(and v3) files, I'm including iomap.c
+ * in these files as well. It does at least avoid exporting some currently
+ * static functions that aren't needed anywhere but itree_common.c and iomap.c.
+ */
+#include "iomap.c"
int V1_minix_get_block(struct inode * inode, long block,
struct buffer_head *bh_result, int create)
@@ -61,7 +73,18 @@ void V1_minix_truncate(struct inode * inode)
truncate(inode);
}
-unsigned V1_minix_blocks(loff_t size, struct super_block *sb)
+unsigned int V1_minix_blocks(loff_t size, struct super_block *sb)
{
return nblocks(size, sb);
}
+
+int V1_minix_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
+ unsigned int flags, struct iomap *iomap, struct iomap *srcmap)
+{
+ return minix_iomap_begin(inode, offset, length, flags, iomap, srcmap);
+}
+
+const struct iomap_ops V1_minix_iomap_ops = {
+ .iomap_begin = V1_minix_iomap_begin,
+ .iomap_end = minix_iomap_end,
+};
diff --git a/fs/minix/itree_v2.c b/fs/minix/itree_v2.c
index 9d00f31a2d9d..fc7a5ae8fa1c 100644
--- a/fs/minix/itree_v2.c
+++ b/fs/minix/itree_v2.c
@@ -57,6 +57,10 @@ static int block_to_path(struct inode * inode, long block, int offsets[DEPTH])
}
#include "itree_common.c"
+/* See the note in itree_v1 in a comment that starts "NOTA BENE" for an
+ * explanation for why iomap.c is included here.
+ */
+#include "iomap.c"
int V2_minix_get_block(struct inode * inode, long block,
struct buffer_head *bh_result, int create)
@@ -69,7 +73,18 @@ void V2_minix_truncate(struct inode * inode)
truncate(inode);
}
-unsigned V2_minix_blocks(loff_t size, struct super_block *sb)
+unsigned int V2_minix_blocks(loff_t size, struct super_block *sb)
{
return nblocks(size, sb);
}
+
+int V2_minix_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
+ unsigned int flags, struct iomap *iomap, struct iomap *srcmap)
+{
+ return minix_iomap_begin(inode, offset, length, flags, iomap, srcmap);
+}
+
+const struct iomap_ops V2_minix_iomap_ops = {
+ .iomap_begin = V2_minix_iomap_begin,
+ .iomap_end = minix_iomap_end,
+};
diff --git a/fs/minix/minix.h b/fs/minix/minix.h
index 78722ce22e1e..f1141d36e3d5 100644
--- a/fs/minix/minix.h
+++ b/fs/minix/minix.h
@@ -5,6 +5,7 @@
#include <linux/fs.h>
#include <linux/pagemap.h>
#include <linux/minix_fs.h>
+#include <linux/iomap.h>
#define INODE_VERSION(inode) minix_sb(inode->i_sb)->s_version
#define MINIX_V1 0x0001 /* original minix fs */
@@ -76,13 +77,23 @@ int minix_make_empty(struct inode*, struct inode*);
int minix_empty_dir(struct inode*);
int minix_set_link(struct minix_dir_entry *de, struct folio *folio,
struct inode *inode);
+
struct minix_dir_entry *minix_dotdot(struct inode*, struct folio **);
ino_t minix_inode_by_name(struct dentry*);
+int V1_minix_iomap_begin(struct inode *inode, loff_t offset,
+ loff_t length, unsigned int flags, struct iomap *iomap,
+ struct iomap *srcmap);
+int V2_minix_iomap_begin(struct inode *inode, loff_t offset,
+ loff_t length, unsigned int flags, struct iomap *iomap,
+ struct iomap *srcmap);
+
extern const struct inode_operations minix_file_inode_operations;
extern const struct inode_operations minix_dir_inode_operations;
extern const struct file_operations minix_file_operations;
extern const struct file_operations minix_dir_operations;
+extern const struct iomap_ops V1_minix_iomap_ops;
+extern const struct iomap_ops V2_minix_iomap_ops;
static inline struct minix_sb_info *minix_sb(struct super_block *sb)
{
@@ -94,11 +105,17 @@ static inline struct minix_inode_info *minix_i(struct inode *inode)
return container_of(inode, struct minix_inode_info, vfs_inode);
}
-static inline unsigned minix_blocks_needed(unsigned bits, unsigned blocksize)
+static inline unsigned int minix_blocks_needed(unsigned int bits, unsigned int blocksize)
{
return DIV_ROUND_UP_POW2(bits, blocksize * 8);
}
+static inline const struct iomap_ops *minix_iomap_ops_ver(struct inode *inode)
+{
+ return (INODE_VERSION(inode) == MINIX_V1) ?
+ &V1_minix_iomap_ops : &V2_minix_iomap_ops;
+}
+
#if defined(CONFIG_MINIX_FS_NATIVE_ENDIAN) && \
defined(CONFIG_MINIX_FS_BIG_ENDIAN_16BIT_INDEXED)
@@ -128,7 +145,7 @@ static inline unsigned minix_blocks_needed(unsigned bits, unsigned blocksize)
* big-endian 16bit indexed bitmaps
*/
-static inline int minix_find_first_zero_bit(const void *vaddr, unsigned size)
+static inline int minix_find_first_zero_bit(const void *vaddr, unsigned int size)
{
const unsigned short *p = vaddr, *addr = vaddr;
unsigned short num;
--
2.47.3
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v4 3/3] minix: finish wiring in iomap functions
2026-08-26 21:41 [PATCH v4 0/3] minix: convert to iomap Jeremy Bingham
2026-08-26 21:41 ` [PATCH v4 1/3] iomap: add iomap_symlink_write Jeremy Bingham
2026-08-26 21:41 ` [PATCH v4 2/3] minix: add iomap functions and definitions Jeremy Bingham
@ 2026-08-26 21:41 ` Jeremy Bingham
2 siblings, 0 replies; 4+ messages in thread
From: Jeremy Bingham @ 2026-08-26 21:41 UTC (permalink / raw)
To: linux-fsdevel
Cc: linux-kernel, brauner, jkoolstra, jack, djwong, viro, hch,
Jeremy Bingham
Wire in the new iomap functionality in one pass. Per Christoph Hellwig's
feedback, there are no longer direct I/O operations. Without that, only
write_iter in minix_file_operations needs a custom function. That and
exporting minix_setattr for minix_symlink_inode_operations are the only
changes in file.c.
There are two main additions in inode.c. First, minix_writeback_range
and minix_writeback_ops are newly added. Secondly, the old
minix_writepages and minix_read_folio functions were renamed to
minix_block_writepages and minix_block_read_folio respectively while new
functions with those names were created that use iomap. Because
directory operations need to stay using buffer heads, a new set of
address space operations just for directory operations was created while
other file types use the new iomap based address space operations. The
minix_symlink_inode_operations also have setattr set to minix_setattr
now. Support for bmap has been dropped entirely, per Darrick J. Wong's
suggestion.
In itree_common.c, truncate() is updated to use different functions
depending on whether the inode being truncated is a directory or not.
This is because of the changes above where directory operations still
use buffer heads and have their own address operations.
This patch also updates minix_symlink to use the new iomap_symlink_write
function, which brings symlinks under iomap and removes the need to
bypass it with a private custom function. This was suggested by Darrick
J. Wong in an earlier version of this patch series.
Suggested-by: Darrick J. Wong <djwong@kernel.org>
Suggested-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Jeremy Bingham <jbingham@gmail.com>
---
fs/minix/file.c | 30 ++++++++++++++-
fs/minix/inode.c | 85 +++++++++++++++++++++++++++++++++++------
fs/minix/itree_common.c | 10 ++++-
fs/minix/minix.h | 2 +
fs/minix/namei.c | 7 +++-
5 files changed, 118 insertions(+), 16 deletions(-)
diff --git a/fs/minix/file.c b/fs/minix/file.c
index 02aabbdb5dea..6765f571bd3f 100644
--- a/fs/minix/file.c
+++ b/fs/minix/file.c
@@ -10,6 +10,32 @@
#include <linux/buffer_head.h>
#include "minix.h"
+static ssize_t minix_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+ struct inode *inode = iocb->ki_filp->f_mapping->host;
+ ssize_t ret;
+ const struct iomap_ops *ops = minix_iomap_ops_ver(inode);
+
+ inode_lock(inode);
+ ret = generic_write_checks(iocb, from);
+ if (ret <= 0)
+ goto unlock;
+
+ ret = file_modified(iocb->ki_filp);
+ if (ret)
+ goto unlock;
+
+ ret = iomap_file_buffered_write(iocb, from, ops,
+ NULL, NULL);
+
+ if (ret > 0)
+ ret = generic_write_sync(iocb, ret);
+
+unlock:
+ inode_unlock(inode);
+ return ret;
+}
+
/*
* We have mostly NULLs here: the current defaults are OK for
* the minix filesystem.
@@ -17,13 +43,13 @@
const struct file_operations minix_file_operations = {
.llseek = generic_file_llseek,
.read_iter = generic_file_read_iter,
- .write_iter = generic_file_write_iter,
+ .write_iter = minix_file_write_iter,
.mmap_prepare = generic_file_mmap_prepare,
.fsync = simple_fsync,
.splice_read = filemap_splice_read,
};
-static int minix_setattr(struct mnt_idmap *idmap,
+int minix_setattr(struct mnt_idmap *idmap,
struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = d_inode(dentry);
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index daf83e4ff25c..2bf5ea92360a 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -439,6 +439,31 @@ static int minix_statfs(struct dentry *dentry, struct kstatfs *buf)
return 0;
}
+static ssize_t minix_writeback_range(struct iomap_writepage_ctx *wpc,
+ struct folio *folio, u64 pos, unsigned int len, u64 end_pos)
+{
+ int error;
+
+ if (pos < wpc->iomap.offset ||
+ pos >= wpc->iomap.offset + wpc->iomap.length) {
+ if (INODE_VERSION(wpc->inode) == MINIX_V1)
+ error = V1_minix_iomap_begin(wpc->inode, pos, len, IOMAP_WRITE,
+ &wpc->iomap, NULL);
+ else
+ error = V2_minix_iomap_begin(wpc->inode, pos, len, IOMAP_WRITE,
+ &wpc->iomap, NULL);
+ if (error)
+ return error;
+ }
+
+ return iomap_add_to_ioend(wpc, folio, pos, end_pos, len);
+}
+
+static const struct iomap_writeback_ops minix_writeback_ops = {
+ .writeback_range = minix_writeback_range,
+ .writeback_submit = iomap_ioend_writeback_submit,
+};
+
static int minix_get_block(struct inode *inode, sector_t block,
struct buffer_head *bh_result, int create)
{
@@ -448,17 +473,45 @@ static int minix_get_block(struct inode *inode, sector_t block,
return V2_minix_get_block(inode, block, bh_result, create);
}
-static int minix_writepages(struct address_space *mapping,
+/* The old minix_writepages, preserved for directory operations. */
+static int minix_block_writepages(struct address_space *mapping,
struct writeback_control *wbc)
{
return mpage_writepages(mapping, wbc, minix_get_block);
}
+static int minix_writepages(struct address_space *mapping,
+ struct writeback_control *wbc)
+{
+ struct iomap_writepage_ctx wpc = {
+ .inode = mapping->host,
+ .wbc = wbc,
+ .ops = &minix_writeback_ops,
+ };
+ return iomap_writepages(&wpc);
+}
+
static int minix_read_folio(struct file *file, struct folio *folio)
+{
+ const struct iomap_ops *ops = minix_iomap_ops_ver(folio->mapping->host);
+
+ iomap_bio_read_folio(folio, ops);
+ return 0;
+}
+
+/* The old minix_read_folio, preserved for directory operations. */
+static int minix_block_read_folio(struct file *file, struct folio *folio)
{
return block_read_full_folio(folio, minix_get_block);
}
+static void minix_readahead(struct readahead_control *rac)
+{
+ const struct iomap_ops *ops = minix_iomap_ops_ver(rac->mapping->host);
+
+ iomap_bio_readahead(rac, ops);
+}
+
int minix_prepare_chunk(struct folio *folio, loff_t pos, unsigned len)
{
return __block_write_begin(folio, pos, len, minix_get_block);
@@ -488,26 +541,35 @@ static int minix_write_begin(const struct kiocb *iocb,
return ret;
}
-static sector_t minix_bmap(struct address_space *mapping, sector_t block)
-{
- return generic_block_bmap(mapping,block,minix_get_block);
-}
-
static const struct address_space_operations minix_aops = {
- .dirty_folio = block_dirty_folio,
- .invalidate_folio = block_invalidate_folio,
+ .dirty_folio = iomap_dirty_folio,
+ .invalidate_folio = iomap_invalidate_folio,
.read_folio = minix_read_folio,
+ .readahead = minix_readahead,
.writepages = minix_writepages,
+ .migrate_folio = filemap_migrate_folio,
+ .is_partially_uptodate = iomap_is_partially_uptodate,
+ .release_folio = iomap_release_folio,
+ .error_remove_folio = generic_error_remove_folio,
+};
+
+/* A special aops for directories that keeps using the buffer head chunks, at
+ * least for the time being.
+ */
+static const struct address_space_operations minix_dir_aops = {
+ .dirty_folio = block_dirty_folio,
+ .invalidate_folio = block_invalidate_folio,
+ .read_folio = minix_block_read_folio,
.write_begin = minix_write_begin,
.write_end = generic_write_end,
.migrate_folio = buffer_migrate_folio,
- .bmap = minix_bmap,
- .direct_IO = noop_direct_IO
+ .writepages = minix_block_writepages,
};
static const struct inode_operations minix_symlink_inode_operations = {
.get_link = page_get_link,
.getattr = minix_getattr,
+ .setattr = minix_setattr,
};
void minix_set_inode(struct inode *inode, dev_t rdev)
@@ -519,7 +581,7 @@ void minix_set_inode(struct inode *inode, dev_t rdev)
} else if (S_ISDIR(inode->i_mode)) {
inode->i_op = &minix_dir_inode_operations;
inode->i_fop = &minix_dir_operations;
- inode->i_mapping->a_ops = &minix_aops;
+ inode->i_mapping->a_ops = &minix_dir_aops;
} else if (S_ISLNK(inode->i_mode)) {
inode->i_op = &minix_symlink_inode_operations;
inode_nohighmem(inode);
@@ -786,4 +848,3 @@ module_init(init_minix_fs)
module_exit(exit_minix_fs)
MODULE_DESCRIPTION("Minix file system");
MODULE_LICENSE("GPL");
-
diff --git a/fs/minix/itree_common.c b/fs/minix/itree_common.c
index c3cd2c75af9c..d73ab1caacfe 100644
--- a/fs/minix/itree_common.c
+++ b/fs/minix/itree_common.c
@@ -311,7 +311,15 @@ static inline void truncate (struct inode * inode)
long iblock;
iblock = (inode->i_size + sb->s_blocksize -1) >> sb->s_blocksize_bits;
- block_truncate_page(inode->i_mapping, inode->i_size, get_block);
+
+ /* Depending on whether the inode being truncated is a directory or not,
+ * we need to either call iomap_truncate_page or block_truncate_page.
+ */
+ if (S_ISDIR(inode->i_mode))
+ block_truncate_page(inode->i_mapping, inode->i_size, get_block);
+ else
+ iomap_truncate_page(inode, inode->i_size, NULL,
+ minix_iomap_ops_ver(inode), NULL, NULL);
n = block_to_path(inode, iblock, offsets);
if (!n)
diff --git a/fs/minix/minix.h b/fs/minix/minix.h
index f1141d36e3d5..9035604c68ca 100644
--- a/fs/minix/minix.h
+++ b/fs/minix/minix.h
@@ -58,6 +58,8 @@ void minix_free_block(struct inode *inode, unsigned long block);
unsigned long minix_count_free_blocks(struct super_block *sb);
int minix_getattr(struct mnt_idmap *, const struct path *,
struct kstat *, u32, unsigned int);
+int minix_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ struct iattr *attr);
int minix_prepare_chunk(struct folio *folio, loff_t pos, unsigned len);
struct mapping_metadata_bhs *minix_get_metadata_bhs(struct inode *inode);
diff --git a/fs/minix/namei.c b/fs/minix/namei.c
index 5525ba367ed7..52e115013abf 100644
--- a/fs/minix/namei.c
+++ b/fs/minix/namei.c
@@ -6,6 +6,7 @@
*/
#include "minix.h"
+#include <linux/iomap.h>
static int add_nondir(struct dentry *dentry, struct inode *inode)
{
@@ -84,12 +85,16 @@ static int minix_symlink(struct mnt_idmap *idmap, struct inode *dir,
return PTR_ERR(inode);
minix_set_inode(inode, 0);
- err = page_symlink(inode, symname, i);
+ err = iomap_symlink_write(inode, symname, i, minix_iomap_ops_ver(inode), NULL, NULL);
+
if (unlikely(err)) {
inode_dec_link_count(inode);
iput(inode);
return err;
}
+
+ i_size_write(inode, i - 1);
+
return add_nondir(dentry, inode);
}
--
2.47.3
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-08-26 21:42 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-26 21:41 [PATCH v4 0/3] minix: convert to iomap Jeremy Bingham
2026-08-26 21:41 ` [PATCH v4 1/3] iomap: add iomap_symlink_write Jeremy Bingham
2026-08-26 21:41 ` [PATCH v4 2/3] minix: add iomap functions and definitions Jeremy Bingham
2026-08-26 21:41 ` [PATCH v4 3/3] minix: finish wiring in iomap functions Jeremy Bingham
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox