[RFC PATCH 2/8] fat: use iomap for ->bmap()
From: Matheus Alves de Almeida
Date: Sat Oct 31 2026 - 12:44:29 EST
Make FAT ->bmap() use iomap_bmap(). Add an iomap-backed
fat_swap_activate() to prevent a swapon() deadlock. Remove
truncate_lock as ->bmap() now uses i_rwsem.
Signed-off-by: Matheus Alves de Almeida <matheus.aalmeida@xxxxxxxxxxxx>
---
fs/fat/fat.h | 2 +-
fs/fat/file.c | 2 --
fs/fat/inode.c | 47 ++++++++++++++---------------------------------
fs/fat/iomap.c | 24 ++++++++++++++++++++++++
4 files changed, 39 insertions(+), 36 deletions(-)
diff --git a/fs/fat/fat.h b/fs/fat/fat.h
index 3d0f91fa11df..81c454f8a78a 100644
--- a/fs/fat/fat.h
+++ b/fs/fat/fat.h
@@ -131,7 +131,6 @@ struct msdos_inode_info {
loff_t i_pos; /* on-disk position of directory entry or 0 */
struct hlist_node i_fat_hash; /* hash by i_location */
struct hlist_node i_dir_hash; /* hash by i_logstart */
- struct rw_semaphore truncate_lock; /* protect bmap against truncate */
struct timespec64 i_crtime; /* File creation (birth) time */
struct mapping_metadata_bhs i_metadata_bhs;
struct inode vfs_inode;
@@ -492,6 +491,7 @@ extern const struct export_operations fat_export_ops_nostale;
/* fat/iomap.c */
extern const struct iomap_ops fat_iomap_ops;
+extern const struct iomap_ops fat_iomap_bmap_ops;
/* helper for printk */
typedef unsigned long long llu;
diff --git a/fs/fat/file.c b/fs/fat/file.c
index 531abf9e533c..c27d4e7f698c 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -613,10 +613,8 @@ int fat_setattr(const struct mnt_idmap *idmap, struct dentry *dentry,
error = fat_block_truncate_page(inode, attr->ia_size);
if (error)
goto out;
- down_write(&MSDOS_I(inode)->truncate_lock);
truncate_setsize(inode, attr->ia_size);
fat_truncate_blocks(inode, attr->ia_size);
- up_write(&MSDOS_I(inode)->truncate_lock);
}
/*
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 2a16ed415631..3885ddb916bd 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -216,9 +216,7 @@ static void fat_write_failed(struct address_space *mapping, loff_t to)
if (to > inode->i_size) {
truncate_pagecache(inode, inode->i_size);
- down_write(&MSDOS_I(inode)->truncate_lock);
fat_truncate_blocks(inode, inode->i_size);
- up_write(&MSDOS_I(inode)->truncate_lock);
}
}
@@ -290,43 +288,26 @@ static ssize_t fat_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
return ret;
}
-static int fat_get_block_bmap(struct inode *inode, sector_t iblock,
- struct buffer_head *bh_result, int create)
-{
- struct super_block *sb = inode->i_sb;
- unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits;
- int err;
- sector_t bmap;
- unsigned long mapped_blocks;
-
- BUG_ON(create != 0);
-
- err = fat_bmap(inode, iblock, &bmap, &mapped_blocks, create, true);
- if (err)
- return err;
-
- if (bmap) {
- map_bh(bh_result, sb, bmap);
- max_blocks = min(mapped_blocks, max_blocks);
- }
-
- bh_result->b_size = max_blocks << sb->s_blocksize_bits;
-
- return 0;
-}
-
static sector_t _fat_bmap(struct address_space *mapping, sector_t block)
{
sector_t blocknr;
- /* fat_get_cluster() assumes the requested blocknr isn't truncated. */
- down_read(&MSDOS_I(mapping->host)->truncate_lock);
- blocknr = generic_block_bmap(mapping, block, fat_get_block_bmap);
- up_read(&MSDOS_I(mapping->host)->truncate_lock);
-
+ inode_lock_shared(mapping->host);
+ blocknr = iomap_bmap(mapping, block, &fat_iomap_bmap_ops);
+ inode_unlock_shared(mapping->host);
return blocknr;
}
+static int fat_swap_activate(struct swap_info_struct *sis, struct file *file,
+ sector_t *span)
+{
+#ifdef CONFIG_SWAP
+ return iomap_swapfile_activate(sis, file, span, &fat_iomap_ops);
+#else
+ return -EIO;
+#endif
+}
+
/*
* fat_block_truncate_page() zeroes out a mapping from file offset `from'
* up to the end of the block which corresponds to `from'.
@@ -350,6 +331,7 @@ static const struct address_space_operations fat_aops = {
.direct_IO = fat_direct_IO,
.bmap = _fat_bmap,
.migrate_folio = buffer_migrate_folio,
+ .swap_activate = fat_swap_activate,
};
/*
@@ -790,7 +772,6 @@ static struct inode *fat_alloc_inode(struct super_block *sb)
if (!ei)
return NULL;
- init_rwsem(&ei->truncate_lock);
/* Zeroing to allow iput() even if partial initialized inode. */
ei->mmu_private = 0;
ei->i_start = 0;
diff --git a/fs/fat/iomap.c b/fs/fat/iomap.c
index 7fb24d0296f7..7977147889bb 100644
--- a/fs/fat/iomap.c
+++ b/fs/fat/iomap.c
@@ -69,3 +69,27 @@ static DEFINE_IOMAP_ITER_NEXT(fat_iomap_next, fat_iomap_begin);
const struct iomap_ops fat_iomap_ops = {
.iomap_next = fat_iomap_next
};
+
+static int fat_iomap_begin_bmap(struct inode *inode, loff_t offset,
+ loff_t length, unsigned int flags,
+ struct iomap *iomap, struct iomap *srcmap)
+{
+ int err;
+
+ err = __fat_iomap_begin(inode, offset, length, flags, iomap);
+ if (err)
+ return err;
+ /*
+ * iomap_bmap() only returns blocks in an IOMAP_MAPPED range.
+ * As the old ->bmap() returned KEEP_SIZE blocks, keep that behavior.
+ */
+ if (iomap->type == IOMAP_UNWRITTEN)
+ iomap->type = IOMAP_MAPPED;
+ return 0;
+}
+
+static DEFINE_IOMAP_ITER_NEXT(fat_iomap_next_bmap, fat_iomap_begin_bmap);
+
+const struct iomap_ops fat_iomap_bmap_ops = {
+ .iomap_next = fat_iomap_next_bmap
+};
--
2.43.0