mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
exfat: add support for multi-cluster allocation
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>
This commit is contained in:
parent
a095c12ef1
commit
286ae36887
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user