minix: convert to iomap
Subsystems (2)
v4 cover minix: convert to iomap
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
Signature · 1 line
2.47.3
PATCH v4 1/3 iomap: add iomap_symlink_write
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.
--- fs/iomap/buffered-io.c | 34 ++++++++++++++++++++++++++++++++++ include/linux/iomap.h | 3 +++ 2 files changed, 37 insertions(+)
fs/iomap/buffered-io.c +34 -0
@@ -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);
include/linux/iomap.h +3 -0
@@ -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.
Signature · 1 line
2.47.3
Re: [PATCH v4 1/3] iomap: add iomap_symlink_write
Quoted 11 lines · Jeremy Bingham
Add a new
iomap_symlink_writefunction as an iomap based equivalent topage_symlinkfound infs/namei.c. Part of being that equivalency is behaving similarly topage_symlink. This function now expects the same len aspage_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 <[email protected]>Suggested-by Christoph Hellwig <[email protected]>Signed-off-by Jeremy Bingham <[email protected]>
No empty line before the signoff, please.
Otherwise looks good:
PATCH v4 2/3 minix: add iomap functions and definitions
In reply to Jeremy BinghamAdds 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.
--- 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
fs/minix/iomap.c new file +122 -0
@@ -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 ; +}
fs/minix/itree_v1.c +24 -1
@@ -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, +};
fs/minix/itree_v2.c +16 -1
@@ -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, +};
fs/minix/minix.h +19 -2
@@ -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;
Signature · 1 line
2.47.3
Re: [PATCH v4 2/3] minix: add iomap functions and definitions
Quoted 3 lines · Jeremy Bingham
This patch also wires
iomap.cintoitree_v1.canditree_v2.c, similarly to howitree_common.cis included in those files, and exports version specific versions ofminix_iomap_beginandiomap_ops.
Can we keep the iomap code out of this double build mess? Just define some low-level helper in it, but keep everything else in an only once build iomap.c
Quoted 2 lines
Also updates 'unsigned' to 'unsigned int' a few places that got picked up by checkpatch.pl.
If you want to do these cleanups please split them into a separate patch.
fs/minix/iomap.c line 4
Quoted 6 lines
+++
b/fs/minix/iomap.cfs/minix/iomap.c new file +4
-0@@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* +* iomap functions for minix. +*/
Not really a very useful comment :) OTOH this lacks a copyright statement for your work.
fs/minix/iomap.c line 38
Quoted 2 lines
fs/minix/iomap.c new file +2
-0+static int minix_iomap_begin (struct inode *inode,loff_t offset,loff_t length, +unsigned int flags,struct iomap *iomap,struct iomap *srcmap)
This mostly does not need the iomap. It basically returns a physical block and a trystate of HOLE/FOUND/ALLOCATED. If you change the interface to that you can keep it in itree_common by directly morphing the existing block lookup into that, or even do it as a prep patch, which first refactors get_block to this interface, and then do the iomap mapping on top of that, and in a place that only gets built once.
fs/minix/iomap.c line 122
Quoted 10 lines
fs/minix/iomap.c new file +10
-0+/* +* 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 ; +}
In which case we can just drop it. Note that for the next version against 7.3-rc should also move from the begin/end calls to the single next one using the DEFINE_IOMAP_ITER_NEXT helper macro.
fs/minix/minix.h static inline struct minix_inode_info *minix_i(struct inode *inode) · line 108
Quoted 2 lines
fs/minix/minix.h +1
-1-static inline unsigned minix_blocks_needed (unsigned bits,unsigned blocksize) +static inline unsigned int minix_blocks_needed (unsigned int bits,unsigned int blocksize)
Overly long line. I'd much rather generate the baseless checkpath.pl complaint and leave the plain unsgineds in place. There is absolutely no technical downside in using that notation.
fs/minix/minix.h static inline struct minix_inode_info *minix_i(struct inode *inode) · line 115
Quoted 3 lines
fs/minix/minix.h +3
-0+static inline const struct iomap_ops *minix_iomap_ops_ver (struct inode *inode) +{ + return (INODE_VERSION(inode) == MINIX_V1) ?
No need for the braces.
Re: [PATCH v4 2/3] minix: add iomap functions and definitions
Quoted 9 lines · Christoph Hellwig
Quoted 3 lines · Jeremy Bingham
This patch also wires
iomap.cintoitree_v1.canditree_v2.c, similarly to howitree_common.cis included in those files, and exports version specific versions ofminix_iomap_beginandiomap_ops.Can we keep the iomap code out of this double build mess? Just define some low-level helper in it, but keep everything else in an only once build
iomap.c
OK. I went back and forth a bunch on how to best do this, because it's *such* a mess. I had a lot of reservations about the way I had done it, but it's so all around
messy I ended up there. When I refactor it I'll either move that function to itree_common.c or craft a way to get that depth information out.
Quoted 5 lines
Quoted 2 lines
Also updates 'unsigned' to 'unsigned int' a few places that got picked up by checkpatch.pl.
If you want to do these cleanups please split them into a separate patch.
Noted. I did submit a patch like that back in I think late June or early July, but got
told (politely) to knock it off. I had kept these cleanups because of checkpatch.pl
griping, but as you said below there's no technical downside to leaving it.
Quoted 9 lines
fs/minix/iomap.c line 4
Quoted 6 lines
+++
b/fs/minix/iomap.cfs/minix/iomap.c new file +4
-0@@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* +* iomap functions for minix. +*/ Not really a very useful comment :) OTOH this lacks a copyright statement for your work.
I didn't want to be presumptuous. I'll add that in.
Quoted 10 lines
fs/minix/iomap.c line 38
Quoted 2 lines
fs/minix/iomap.c new file +2
-0+static int minix_iomap_begin (struct inode *inode,loff_t offset,loff_t length, +unsigned int flags,struct iomap *iomap,struct iomap *srcmap)This mostly does not need the iomap. It basically returns a physical block and a trystate of HOLE/FOUND/ALLOCATED. If you change the interface to that you can keep it in
itree_commonby directly morphing the existing block lookup into that, or even do it as a prep patch, which first refactorsget_blockto this interface, and then do the iomap mapping on top of that, and in a place that only gets built once.
I'll address this in the refactor mentioned above. I don't think I can entirely get rid of
or refactor get_block() unfortunately because of the directory operations, but I'll see
what I can do.
Quoted 6 lines
Quoted 6 lines
fs/minix/iomap.c new file +6
-0+/* +* 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,
Quoted 8 lines
fs/minix/iomap.c line 122
Quoted 4 lines
fs/minix/iomap.c new file +4
-0+ ssize_t written, unsigned int flags, struct iomap *iomap) +{ + return 0; +}In which case we can just drop it. Note that for the next version against 7.3-rc should also move from the begin/end calls to the single next one using the
DEFINE_IOMAP_ITER_NEXThelper macro.
Conveniently I had just updated my test VM kernel to 7.3-rc and verified that the
module still builds unchanged, so everything's in place for that. Thank you for the
head's up.
Quoted 7 lines
fs/minix/minix.h static inline struct minix_inode_info *minix_i(struct inode *inode) · line 108
Quoted 2 lines
fs/minix/minix.h +1
-1-static inline unsigned minix_blocks_needed (unsigned bits,unsigned blocksize) +static inline unsigned int minix_blocks_needed (unsigned int bits,unsigned int blocksize)Overly long line. I'd much rather generate the baseless checkpath.pl complaint and leave the plain unsgineds in place. There is absolutely no technical downside in using that notation.
Quoted 5 lines
fs/minix/minix.h static inline struct minix_inode_info *minix_i(struct inode *inode) · line 115
Quoted 3 lines
fs/minix/minix.h +3
-0+static inline const struct iomap_ops *minix_iomap_ops_ver (struct inode *inode) +{ + return (INODE_VERSION(inode) == MINIX_V1) ?No need for the braces.
All simple, all noted.
Thanks,
-j
Re: [PATCH v4 2/3] minix: add iomap functions and definitions
Quoted 7 lines · Jeremy Bingham
Quoted 6 lines · Christoph Hellwig
Quoted 3 lines · Jeremy Bingham
This patch also wires
iomap.cintoitree_v1.canditree_v2.c, similarly to howitree_common.cis included in those files, and exports version specific versions ofminix_iomap_beginandiomap_ops.
Quoted 5 lines
Quoted 3 lines
Can we keep the iomap code out of this double build mess? Just define some low-level helper in it, but keep everything else in an only once build
iomap.cOK. I went back and forth a bunch on how to best do this, because it's *such* a mess.
Agreed.
Quoted 4 lines
I had a lot of reservations about the way I had done it, but it's so all around
messy I ended up there. When I refactor it I'll either move that function toitree_common.cor craft a way to get that depth information out.
It might be worthwhile to kill this mess off first. AFAICS it was written at a time when branch predictors where much worse than today. So having a branch for te two cases much lower down in the code should be just fine with modern CPUs.
Quoted 4 lines
Quoted 4 lines
Quoted 3 lines
Also updates 'unsigned' to 'unsigned int' a few places that got picked up by checkpatch.pl.
Quoted 8 lines
Quoted 2 lines
If you want to do these cleanups please split them into a separate patch.
Noted. I did submit a patch like that back in I think late June or early July, but got
told (politely) to knock it off. I had kept these cleanups because of checkpatch.pl
griping, but as you said below there's no technical downside to leaving it.
checkpatch.pl is a little weird sometimes unfortunately.
Re: [PATCH v4 2/3] minix: add iomap functions and definitions
Quoted 22 lines · Christoph Hellwig
Quoted 7 lines · Jeremy Bingham
Quoted 6 lines · Christoph Hellwig
Quoted 3 lines · Jeremy Bingham
This patch also wires
iomap.cintoitree_v1.canditree_v2.c, similarly to howitree_common.cis included in those files, and exports version specific versions ofminix_iomap_beginandiomap_ops.Quoted 5 lines
Quoted 3 lines
Can we keep the iomap code out of this double build mess? Just define some low-level helper in it, but keep everything else in an only once build
iomap.cOK. I went back and forth a bunch on how to best do this, because it's *such* a mess.
Agreed.
Quoted 4 lines
I had a lot of reservations about the way I had done it, but it's so all around
messy I ended up there. When I refactor it I'll either move that function toitree_common.cor craft a way to get that depth information out.
Quoted 4 lines
It might be worthwhile to kill this mess off first. AFAICS it was written at a time when branch predictors where much worse than today. So having a branch for te two cases much lower down in the code should be just fine with modern CPUs.
As it happens, back on July 6th I submitted an RFC patch to do this very thing. It dropped off the radar, though, and I didn't push it. It may need updating to match the very very latest kernel sources and strip out the extra RFC-related comments, but the lore link is at
https://lore.kernel.org/linux-fsdevel/[email protected]/T/#u.
If the general concept is good, I'll clean it up and send it through again.
-j
PATCH v4 3/3 minix: finish wiring in iomap functions
In reply to Jeremy BinghamWire 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.
--- 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(-)
fs/minix/file.c +28 -2
@@ -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);
fs/minix/inode.c +73 -12
133 lines not on this page · raw message
fs/minix/itree_common.c +9 -1
@@ -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)
fs/minix/minix.h +2 -0
@@ -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);
fs/minix/namei.c +6 -1
@@ -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); }
Signature · 1 line
2.47.3
Re: [PATCH v4 3/3] minix: finish wiring in iomap functions
Quoted 5 lines · 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_iterinminix_file_operationsneeds a custom function. That and exportingminix_setattrforminix_symlink_inode_operationsare the only changes infile.c.
Commit history just goes into the cover letter. In genral you don't need to enumerate all low-level changes either. Explain the high-level change, what motivated it, and anything that looks a bit unusual and unexpected for that high-level change.
Note that you probably want to merge this into the previous patch adding the actual iomap ops instead of leaving them dangling between thet two patches.
fs/minix/file.c line 29
Quoted 2 lines
fs/minix/file.c +2
-0+ ret =iomap_file_buffered_write (iocb, from, ops, +NULL ,NULL );
The two NULL still fit onto the previous line:
ret = iomap_file_buffered_write(iocb, from, ops, NULL, NULL);
fs/minix/file.c line 35
Quoted 6 lines
fs/minix/file.c +6
-0+ +if (ret >0 ) + ret =generic_write_sync (iocb, ret); + +unlock: +inode_unlock (inode);
For most file systems we try to have the generic_write_sync outside the inode lock to not do the expensive sync with the inode locked. generic_file_write_iter also doesn't have the inode locked, so you should probably sync after dropping the lock here as swell.
fs/minix/file.c line 53
Quoted 2 lines
fs/minix/file.c +1
-0+int minix_setattr (struct mnt_idmap *idmap,struct dentry *dentry,struct iattr *attr)
Odd formatting again. The typical style would be:
int minix_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
struct iattr *attr)
fs/minix/inode.c static int minix_statfs(struct dentry *dentry, struct kstatfs *buf) · line 443
Quoted 2 lines
fs/minix/inode.c +2
-0+static ssize_t minix_writeback_range (struct iomap_writepage_ctx *wpc, +struct folio *folio,u64 pos,unsigned int len,u64 end_pos)
Two-tab indents please:
static ssize_t minix_writeback_range(struct iomap_writepage_ctx *wpc,
struct folio *folio, u64 pos, unsigned int len, u64 end_pos)
fs/minix/inode.c static int minix_statfs(struct dentry *dentry, struct kstatfs *buf) · line 448
Quoted 5 lines
fs/minix/inode.c +5
-0+{ +int error; + +if (pos < wpc->iomap.offset || + pos >= wpc->iomap.offset + wpc->iomap.length) {
if (pos < wpc->iomap.offset ||
pos >= wpc->iomap.offset + wpc->iomap.length) {fs/minix/inode.c static int minix_statfs(struct dentry *dentry, struct kstatfs *buf) · line 454
Quoted 6 lines
fs/minix/inode.c +6
-0+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 );
Overly long lines. These should hopefully go away for free with the common iomap_ops.
fs/minix/inode.c static int minix_get_block(struct inode *inode, sector_t block, · line 477
Quoted 3 lines
fs/minix/inode.c +2
-1-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,
minix_dir_writepages?
If you're looking for another project, we could probably also have a iomap version of the directories in pagecache used by minix, ext2 and co eventually.
fs/minix/inode.c static int minix_write_begin(const struct kiocb *iocb, · line 554
Quoted 13 lines
static const struct
address_space_operationsminix_aops= {fs/minix/inode.c +8
-2- .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, +};
Maybe use tabs to align the initializers if you touch most of them anyway?
fs/minix/itree_common.c static inline void truncate (struct inode * inode) · line 317
Quoted 3 lines
fs/minix/itree_common.c +3
-0+/* Depending on whether the inode being truncated is a directory or not, +* we need to either call iomap_truncate_page or block_truncate_page. +*/
The kernel coding style would be:
/* * Depending on whether the inode being truncated is a directory or not, * we need to either call iomap_truncate_page or block_truncate_page. */
fs/minix/namei.c static int minix_symlink(struct mnt_idmap *idmap, struct inode *dir, · line 88
Quoted 1 line
fs/minix/namei.c +1
-0+ err =iomap_symlink_write (inode, symname, i,minix_iomap_ops_ver (inode),NULL ,NULL );
Overly long line.
Re: [PATCH v4 3/3] minix: finish wiring in iomap functions
I've seen all the comments on formatting and will be fixing them, but I'll just note that once and not keep repeating myself.
Quoted 8 lines · Christoph Hellwig
Quoted 5 lines · 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_iterinminix_file_operationsneeds a custom function. That and exportingminix_setattrforminix_symlink_inode_operationsare the only changes infile.c.
Quoted 8 lines
Commit history just goes into the cover letter. In genral you don't need to enumerate all low-level changes either. Explain the high-level change, what motivated it, and anything that looks a bit unusual and unexpected for that high-level change.
Note that you probably want to merge this into the previous patch adding the actual iomap ops instead of leaving them dangling between thet two patches.
Indeed, and thank you for letting me know that this ought to be merged into the previous patch. I didn't want to run afoul of making the patch too big.
Quoted 11 lines
fs/minix/file.c line 35
Quoted 6 lines
fs/minix/file.c +6
-0+ +if (ret >0 ) + ret =generic_write_sync (iocb, ret); + +unlock: +inode_unlock (inode);For most file systems we try to have the
generic_write_syncoutside the inode lock to not do the expensive sync with the inode locked.generic_file_write_iteralso doesn't have the inode locked, so you should probably sync after dropping the lock here as swell.
That makes sense. I'll rearrange that.
Quoted 3 lines
If you're looking for another project, we could probably also have a iomap version of the directories in pagecache used by minix, ext2 and co eventually.
That does sound worth working on. I don't love having the directory operations using a completely different framework than everything else, but since minix was not the only filesystem afffected by that I left it be.
Thanks again, and I'll start getting this turned around post-haste.
-j