]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
exfat: add support for multi-cluster allocation
authorNamjae Jeon <linkinjeon@kernel.org>
Thu, 7 May 2026 00:05:08 +0000 (09:05 +0900)
committerNamjae Jeon <linkinjeon@kernel.org>
Mon, 15 Jun 2026 10:55:42 +0000 (19:55 +0900)
Currently exfat_map_cluster() allocates and returns only one cluster
at a time even when more clusters are needed. This causes multiple
FAT walks and repeated allocation calls during large sequential writes
or when using iomap for writes. This change exfat_map_cluster() and
exfat_alloc_cluster() to be able to allocate multiple contiguous
clusters.

Acked-by: Christoph Hellwig <hch@lst.de>
Acked-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
fs/exfat/dir.c
fs/exfat/exfat_fs.h
fs/exfat/fatent.c
fs/exfat/file.c
fs/exfat/inode.c
fs/exfat/namei.c

index f7f0b6a5bbcfa3cc26bce41492637ffae43e1a59..8b8f6bc0c233c8bfb1fa4a3a24610a8d719725c4 100644 (file)
@@ -295,7 +295,7 @@ int exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu)
 
        exfat_chain_set(clu, EXFAT_EOF_CLUSTER, 0, ALLOC_NO_FAT_CHAIN);
 
-       ret = exfat_alloc_cluster(inode, 1, clu, IS_DIRSYNC(inode));
+       ret = exfat_alloc_cluster(inode, 1, clu, IS_DIRSYNC(inode), false);
        if (ret)
                return ret;
 
index ad8d6a48fe1f4d6aa384b27433fb89a86b600fdb..e47fbfa1d8547952a84d0307781c1c6e807390ea 100644 (file)
@@ -497,7 +497,7 @@ int exfat_clear_volume_dirty(struct super_block *sb);
        exfat_cluster_walk(sb, (pclu), 1, ALLOC_FAT_CHAIN)
 
 int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
-               struct exfat_chain *p_chain, bool sync_bmap);
+               struct exfat_chain *p_chain, bool sync_bmap, bool contig);
 int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain);
 int exfat_ent_get(struct super_block *sb, unsigned int loc,
                unsigned int *content, struct buffer_head **last);
index 45b0b754a2e4b5fe8367f531500391f379775831..a8b11e2ce43f1cbd9084fad4438711e15dc027bf 100644 (file)
@@ -419,7 +419,7 @@ int exfat_zeroed_cluster(struct inode *dir, unsigned int clu)
 }
 
 int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
-               struct exfat_chain *p_chain, bool sync_bmap)
+               struct exfat_chain *p_chain, bool sync_bmap, bool contig)
 {
        int ret = -ENOSPC;
        unsigned int total_cnt;
@@ -470,14 +470,20 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
 
        while ((new_clu = exfat_find_free_bitmap(sb, hint_clu)) !=
               EXFAT_EOF_CLUSTER) {
-               if (new_clu != hint_clu &&
-                   p_chain->flags == ALLOC_NO_FAT_CHAIN) {
-                       if (exfat_chain_cont_cluster(sb, p_chain->dir,
-                                       p_chain->size)) {
-                               ret = -EIO;
-                               goto free_cluster;
+               if (new_clu != hint_clu) {
+                       if (p_chain->flags == ALLOC_NO_FAT_CHAIN) {
+                               if (exfat_chain_cont_cluster(sb, p_chain->dir,
+                                                            p_chain->size)) {
+                                       ret = -EIO;
+                                       goto free_cluster;
+                               }
+                               p_chain->flags = ALLOC_FAT_CHAIN;
+                       }
+
+                       if (contig && p_chain->size > 0) {
+                               hint_clu = last_clu;
+                               goto done;
                        }
-                       p_chain->flags = ALLOC_FAT_CHAIN;
                }
 
                /* update allocation bitmap */
@@ -507,9 +513,9 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
                last_clu = new_clu;
 
                if (p_chain->size == num_alloc) {
+done:
                        sbi->clu_srch_ptr = hint_clu;
-                       sbi->used_clusters += num_alloc;
-
+                       sbi->used_clusters += p_chain->size;
                        mutex_unlock(&sbi->bitmap_lock);
                        return 0;
                }
index 857cd3030cae77f97ed00cfec9faba7b9f09ad66..89cc3d046f0f4eb5adb2740f3f39e1413e05482c 100644 (file)
@@ -57,7 +57,7 @@ static int exfat_cont_expand(struct inode *inode, loff_t size)
        clu.flags = ei->flags;
 
        ret = exfat_alloc_cluster(inode, new_num_clusters - num_clusters,
-                       &clu, inode_needs_sync(inode));
+                       &clu, inode_needs_sync(inode), false);
        if (ret)
                return ret;
 
index a10d4f3c66a178e7271da88a7f6b5110c6220c94..7b09d94ac464a337cc99ce27bfbcce1d8273b74f 100644 (file)
@@ -137,9 +137,9 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
        unsigned int num_to_be_allocated = 0, num_clusters;
 
        num_clusters = exfat_bytes_to_cluster(sbi, exfat_ondisk_size(inode));
-
-       if (clu_offset >= num_clusters)
-               num_to_be_allocated = clu_offset - num_clusters + 1;
+       if (clu_offset > num_clusters ||
+           *count > num_clusters - clu_offset)
+               num_to_be_allocated = clu_offset + *count - num_clusters;
 
        if (!create && (num_to_be_allocated > 0)) {
                *clu = EXFAT_EOF_CLUSTER;
@@ -182,7 +182,7 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
                }
 
                ret = exfat_alloc_cluster(inode, num_to_be_allocated, &new_clu,
-                               inode_needs_sync(inode));
+                               inode_needs_sync(inode), true);
                if (ret)
                        return ret;
 
@@ -216,20 +216,9 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
                }
 
                *clu = new_clu.dir;
+               *count = new_clu.size;
 
-               inode->i_blocks +=
-                       exfat_cluster_to_sectors(sbi, num_to_be_allocated);
-
-               /*
-                * Move *clu pointer along FAT chains (hole care) because the
-                * caller of this function expect *clu to be the last cluster.
-                * This only works when num_to_be_allocated >= 2,
-                * *clu = (the first cluster of the allocated chain) =>
-                * (the last cluster of ...)
-                */
-               if (exfat_cluster_walk(sb, clu, num_to_be_allocated - 1, ei->flags))
-                       return -EIO;
-               *count = 1;
+               inode->i_blocks += exfat_cluster_to_sectors(sbi, new_clu.size);
                if (balloc)
                        *balloc = true;
        }
index 269993881dba113dbe495d9e89f2da2119daeced..78f861f23246ff19749bc2bae455f03a004a5241 100644 (file)
@@ -341,7 +341,7 @@ int exfat_find_empty_entry(struct inode *inode,
                }
 
                /* allocate a cluster */
-               ret = exfat_alloc_cluster(inode, 1, &clu, IS_DIRSYNC(inode));
+               ret = exfat_alloc_cluster(inode, 1, &clu, IS_DIRSYNC(inode), false);
                if (ret)
                        return ret;