[RFC PATCH 8/8] fat: add multi-cluster allocation for write
From: Matheus Alves de Almeida
Date: Sat Oct 31 2026 - 12:45:20 EST
The iomap code expands the file cluster-by-cluster, requiring many calls
into the allocator.
Implement multi-cluster allocation on write. To preserve short write
semantics, allow allocations of chains smaller than the requested size
in fat_alloc_chain().
Signed-off-by: Matheus Alves de Almeida <matheus.aalmeida@xxxxxxxxxxxx>
---
fs/fat/fat.h | 6 ++++--
fs/fat/fatent.c | 33 ++++++++++++++++++++++-----------
fs/fat/file.c | 2 +-
fs/fat/inode.c | 4 ++--
fs/fat/iomap.c | 12 ++++++------
5 files changed, 35 insertions(+), 22 deletions(-)
diff --git a/fs/fat/fat.h b/fs/fat/fat.h
index 962f6f6b7717..c953b2fd9c69 100644
--- a/fs/fat/fat.h
+++ b/fs/fat/fat.h
@@ -393,7 +393,8 @@ extern int fat_ent_write(struct inode *inode, struct fat_entry *fatent,
int new, int wait);
extern int fat_alloc_clusters(struct inode *inode, int *cluster,
int nr_cluster);
-int fat_alloc_chain(struct inode *inode, int *cluster, int nr_cluster);
+int fat_alloc_chain(struct inode *inode, int *cluster, int *nr_cluster,
+ bool allow_partial);
extern int fat_free_clusters(struct inode *inode, int cluster);
extern int fat_count_free_clusters(struct super_block *sb);
extern int fat_trim_fs(struct inode *inode, struct fstrim_range *range);
@@ -439,7 +440,8 @@ static inline unsigned long fat_dir_hash(int logstart)
{
return hash_32(logstart, FAT_HASH_BITS);
}
-extern int fat_add_clusters(struct inode *inode, int nr_cluster);
+
+int fat_add_clusters(struct inode *inode, int nr_cluster, bool allow_partial);
/* fat/misc.c */
extern __printf(3, 4) __cold
diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c
index 46899efa3cf8..6fb99a8759b8 100644
--- a/fs/fat/fatent.c
+++ b/fs/fat/fatent.c
@@ -538,8 +538,8 @@ static void fat_clean_bdev_aliases(struct super_block *sb,
(sector_t)count * sbi->sec_per_clus);
}
-static int __fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster,
- bool return_all)
+static int __fat_alloc_clusters(struct inode *inode, int *cluster, int *nr_cluster,
+ bool return_all, bool allow_partial)
{
struct super_block *sb = inode->i_sb;
struct msdos_sb_info *sbi = MSDOS_SB(sb);
@@ -547,12 +547,18 @@ static int __fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluste
struct fat_entry fatent, prev_ent;
struct buffer_head *bhs[MAX_BUF_PER_PAGE];
int i, count, err, nr_bhs, idx_clus, cont_start, cont_len;
+ int nr_alloc = *nr_cluster;
lock_fat(sbi);
- if (sbi->free_clusters != -1 && sbi->free_clus_valid &&
- sbi->free_clusters < nr_cluster) {
- unlock_fat(sbi);
- return -ENOSPC;
+ if (sbi->free_clusters != -1 && sbi->free_clus_valid) {
+ if (sbi->free_clusters == 0 ||
+ (!allow_partial && sbi->free_clusters < nr_alloc)) {
+ unlock_fat(sbi);
+ return -ENOSPC;
+ }
+ if (allow_partial)
+ nr_alloc = min_t(unsigned int, nr_alloc,
+ sbi->free_clusters);
}
cont_len = 0;
@@ -606,7 +612,7 @@ static int __fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluste
}
fat_collect_bhs(bhs, &nr_bhs, &fatent);
- if (idx_clus == nr_cluster)
+ if (idx_clus == nr_alloc)
goto out;
/*
@@ -627,7 +633,8 @@ static int __fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluste
/* Couldn't allocate the free entries */
sbi->free_clusters = 0;
sbi->free_clus_valid = 1;
- err = -ENOSPC;
+ if (!allow_partial || !idx_clus)
+ err = -ENOSPC;
out:
if (cont_len)
@@ -647,17 +654,21 @@ static int __fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluste
if (err && idx_clus)
fat_free_clusters(inode, cluster[0]);
+ if (!err)
+ *nr_cluster = idx_clus;
return err;
}
int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster)
{
- return __fat_alloc_clusters(inode, cluster, nr_cluster, true);
+ return __fat_alloc_clusters(inode, cluster, &nr_cluster, true, false);
}
-int fat_alloc_chain(struct inode *inode, int *cluster, int nr_cluster)
+int fat_alloc_chain(struct inode *inode, int *cluster, int *nr_cluster,
+ bool allow_partial)
{
- return __fat_alloc_clusters(inode, cluster, nr_cluster, false);
+ return __fat_alloc_clusters(inode, cluster, nr_cluster, false,
+ allow_partial);
}
int fat_free_clusters(struct inode *inode, int cluster)
diff --git a/fs/fat/file.c b/fs/fat/file.c
index ac6329d36062..a7349b2b94ee 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -395,7 +395,7 @@ int fat_preallocate(struct inode *inode, loff_t new_size)
sbi->cluster_bits;
/* Allocate the clusters. We are not zeroing them out */
- return fat_add_clusters(inode, nr_cluster);
+ return fat_add_clusters(inode, nr_cluster, false);
}
static int fat_zero_and_extend(struct inode *inode, loff_t new_size)
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index cb2316062bb3..6896c0c0dec9 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -101,11 +101,11 @@ static struct fat_floppy_defaults {
},
};
-int fat_add_clusters(struct inode *inode, int nr_cluster)
+int fat_add_clusters(struct inode *inode, int nr_cluster, bool allow_partial)
{
int err, cluster;
- err = fat_alloc_chain(inode, &cluster, nr_cluster);
+ err = fat_alloc_chain(inode, &cluster, &nr_cluster, allow_partial);
if (err)
return err;
/* FIXME: this cluster should be added after data of this
diff --git a/fs/fat/iomap.c b/fs/fat/iomap.c
index 893d94f6b70c..581fa609ae18 100644
--- a/fs/fat/iomap.c
+++ b/fs/fat/iomap.c
@@ -98,6 +98,7 @@ static int fat_iomap_begin_write(struct inode *inode, loff_t offset,
loff_t length, unsigned int flags,
struct iomap *iomap, struct iomap *srcmap)
{
+ struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
loff_t curr_size = i_size_read(inode);
loff_t old_alloc = inode_get_bytes(inode);
int err;
@@ -106,12 +107,11 @@ static int fat_iomap_begin_write(struct inode *inode, loff_t offset,
if (WARN_ONCE(offset > curr_size, "Write starting past EOF"))
return -EOPNOTSUPP;
- if (offset == curr_size) {
- /*
- * TODO: allocate multiple blocks while allowing
- * short writes
- */
- err = fat_preallocate(inode, offset + 1);
+ if (offset == curr_size && offset >= old_alloc) {
+ int nr_cluster = (offset + length - old_alloc +
+ sbi->cluster_size - 1) >> sbi->cluster_bits;
+
+ err = fat_add_clusters(inode, nr_cluster, true);
if (err)
goto err_truncate;
}
--
2.43.0