mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:04:51 +01:00
Description for this pull request:
- Improve error code handling and four cleanups.
- Reduce unnecessary valid_size extension during mmap write to avoid
over-extending writes.
- Optimize consecutive FAT entry reads by caching buffer heads in
__exfat_ent_get to significantly reduce sb_bread() calls.
- Add multi-cluster (contiguous cluster) support to
exfat_get_cluster() and exfat_map_cluster() for better sequential read
performance, especially on small cluster sizes.
-----BEGIN PGP SIGNATURE-----
iQJKBAABCgA0FiEE6NzKS6Uv/XAAGHgyZwv7A1FEIQgFAmmNx1IWHGxpbmtpbmpl
b25Aa2VybmVsLm9yZwAKCRBnC/sDUUQhCAnwEADPCRqamaN//Kz2UryyBVhudWSS
trDwSjzeRqVjQJh1e8+5T932ZXPiJmSAJcVnsF12iSeXDWLczRPbQfcN9U6BcjKG
R1PMziZgC4WQfRv7bosQykySCLd0ulGLZx81oqejcVJIguqQK3nRwQEdlNQuuEoH
ABwpfmnYk4AnH9aQ6W/Up7+j0KcQJ3t8LophlyEgSx0pB6g4nZYFLTlCEeQwV8D2
RJ2tGJHN2anV1ppCa1Z5ys4hDAABHV9S+ivnXKmnqjk/jde9PXTgJxDersVybBmw
Bn7EjhgCCn76dL4Ijtoevx54Pm1No3N9IK9h+Im5dDLFNM1VbYRM1hWZ9KQbPrCK
tsVAM2Cf7yUYJM6b6t28r2HEIxpyPq0AVdEgPaM1sRkHuwUs2E0vlxZXoHly4P4i
+XdSCnicUWFhvIXeQoEpuEZqFNb1I9w6J0yidAAu9QMh6wBnYHxVzzUGtu8+6ae3
oty2s/kUg16b7bJpgzE9J874IoZq7Xrx+j/awmHMb2dIeS+TkdH4+gzjOXapdfDQ
x7zacVI6NZZq7Ae/lEf5EvGypcW7XPc9y08NrIVRdIuRAYcBrFZgnB8+GKQnIWkk
RYwBs6uuE42qJBJ1EzlbYLDKO0b9gMFxEaDDAbSupvooMf/Ai3ve8EQlEXckSvc3
Y4//+sqff+7EHb0PAA==
=FC+L
-----END PGP SIGNATURE-----
Merge tag 'exfat-for-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat
Pull exfat updates from Namjae Jeon:
- Improve error code handling and four cleanups
- Reduce unnecessary valid_size extension during mmap write to avoid
over-extending writes
- Optimize consecutive FAT entry reads by caching buffer heads in
__exfat_ent_get to significantly reduce sb_bread() calls
- Add multi-cluster (contiguous cluster) support to exfat_get_cluster()
and exfat_map_cluster() for better sequential read performance,
especially on small cluster sizes
* tag 'exfat-for-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat:
exfat: add blank line after declarations
exfat: remove unnecessary else after return statement
exfat: support multi-cluster for exfat_get_cluster
exfat: return the start of next cache in exfat_cache_lookup
exfat: tweak cluster cache to support zero offset
exfat: support multi-cluster for exfat_map_cluster
exfat: remove handling of non-file types in exfat_map_cluster
exfat: reuse cache to improve exfat_get_cluster
exfat: reduce the number of parameters for exfat_get_cluster()
exfat: remove the unreachable warning for cache miss cases
exfat: remove the check for infinite cluster chain loop
exfat: improve exfat_find_last_cluster
exfat: improve exfat_count_num_clusters
exfat: support reuse buffer head for exfat_ent_get
exfat: add cache option for __exfat_ent_get
exfat: reduce unnecessary writes during mmap write
exfat: improve error code handling in exfat_find_empty_entry()
This commit is contained in:
commit
dd530598bc
6 changed files with 170 additions and 125 deletions
149
fs/exfat/cache.c
149
fs/exfat/cache.c
|
|
@ -80,41 +80,66 @@ static inline void exfat_cache_update_lru(struct inode *inode,
|
|||
list_move(&cache->cache_list, &ei->cache_lru);
|
||||
}
|
||||
|
||||
static unsigned int exfat_cache_lookup(struct inode *inode,
|
||||
unsigned int fclus, struct exfat_cache_id *cid,
|
||||
/*
|
||||
* Find the cache that covers or precedes 'fclus' and return the last
|
||||
* cluster before the next cache range.
|
||||
*/
|
||||
static inline unsigned int
|
||||
exfat_cache_lookup(struct inode *inode, struct exfat_cache_id *cid,
|
||||
unsigned int fclus, unsigned int end,
|
||||
unsigned int *cached_fclus, unsigned int *cached_dclus)
|
||||
{
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
static struct exfat_cache nohit = { .fcluster = 0, };
|
||||
struct exfat_cache *hit = &nohit, *p;
|
||||
unsigned int offset = EXFAT_EOF_CLUSTER;
|
||||
unsigned int tail = 0; /* End boundary of hit cache */
|
||||
|
||||
/*
|
||||
* Search range [fclus, end]. Stop early if:
|
||||
* 1. Cache covers entire range, or
|
||||
* 2. Next cache starts at current cache tail
|
||||
*/
|
||||
spin_lock(&ei->cache_lru_lock);
|
||||
list_for_each_entry(p, &ei->cache_lru, cache_list) {
|
||||
/* Find the cache of "fclus" or nearest cache. */
|
||||
if (p->fcluster <= fclus && hit->fcluster < p->fcluster) {
|
||||
if (p->fcluster <= fclus) {
|
||||
if (p->fcluster < hit->fcluster)
|
||||
continue;
|
||||
|
||||
hit = p;
|
||||
if (hit->fcluster + hit->nr_contig < fclus) {
|
||||
offset = hit->nr_contig;
|
||||
} else {
|
||||
offset = fclus - hit->fcluster;
|
||||
tail = hit->fcluster + hit->nr_contig;
|
||||
|
||||
/* Current cache covers [fclus, end] completely */
|
||||
if (tail >= end)
|
||||
break;
|
||||
} else if (p->fcluster <= end) {
|
||||
end = p->fcluster - 1;
|
||||
|
||||
/*
|
||||
* If we have a hit and next cache starts within/at
|
||||
* its tail, caches are contiguous, stop searching.
|
||||
*/
|
||||
if (tail && tail >= end)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hit != &nohit) {
|
||||
exfat_cache_update_lru(inode, hit);
|
||||
unsigned int offset;
|
||||
|
||||
exfat_cache_update_lru(inode, hit);
|
||||
cid->id = ei->cache_valid_id;
|
||||
cid->nr_contig = hit->nr_contig;
|
||||
cid->fcluster = hit->fcluster;
|
||||
cid->dcluster = hit->dcluster;
|
||||
|
||||
offset = min(cid->nr_contig, fclus - cid->fcluster);
|
||||
*cached_fclus = cid->fcluster + offset;
|
||||
*cached_dclus = cid->dcluster + offset;
|
||||
}
|
||||
spin_unlock(&ei->cache_lru_lock);
|
||||
|
||||
return offset;
|
||||
/* Return next cache start or 'end' if no more caches */
|
||||
return end;
|
||||
}
|
||||
|
||||
static struct exfat_cache *exfat_cache_merge(struct inode *inode,
|
||||
|
|
@ -234,15 +259,15 @@ static inline void cache_init(struct exfat_cache_id *cid,
|
|||
}
|
||||
|
||||
int exfat_get_cluster(struct inode *inode, unsigned int cluster,
|
||||
unsigned int *fclus, unsigned int *dclus,
|
||||
unsigned int *last_dclus, int allow_eof)
|
||||
unsigned int *dclus, unsigned int *count,
|
||||
unsigned int *last_dclus)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
unsigned int limit = sbi->num_clusters;
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
struct buffer_head *bh = NULL;
|
||||
struct exfat_cache_id cid;
|
||||
unsigned int content;
|
||||
unsigned int content, fclus;
|
||||
unsigned int end = cluster + *count - 1;
|
||||
|
||||
if (ei->start_clu == EXFAT_FREE_CLUSTER) {
|
||||
exfat_fs_error(sb,
|
||||
|
|
@ -251,64 +276,82 @@ int exfat_get_cluster(struct inode *inode, unsigned int cluster,
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
*fclus = 0;
|
||||
fclus = 0;
|
||||
*dclus = ei->start_clu;
|
||||
*last_dclus = *dclus;
|
||||
|
||||
/*
|
||||
* Don`t use exfat_cache if zero offset or non-cluster allocation
|
||||
* This case should not exist, as exfat_map_cluster function doesn't
|
||||
* call this routine when start_clu == EXFAT_EOF_CLUSTER.
|
||||
* This case is retained here for routine completeness.
|
||||
*/
|
||||
if (cluster == 0 || *dclus == EXFAT_EOF_CLUSTER)
|
||||
if (*dclus == EXFAT_EOF_CLUSTER) {
|
||||
*count = 0;
|
||||
return 0;
|
||||
|
||||
cache_init(&cid, EXFAT_EOF_CLUSTER, EXFAT_EOF_CLUSTER);
|
||||
|
||||
if (exfat_cache_lookup(inode, cluster, &cid, fclus, dclus) ==
|
||||
EXFAT_EOF_CLUSTER) {
|
||||
/*
|
||||
* dummy, always not contiguous
|
||||
* This is reinitialized by cache_init(), later.
|
||||
*/
|
||||
WARN_ON(cid.id != EXFAT_CACHE_VALID ||
|
||||
cid.fcluster != EXFAT_EOF_CLUSTER ||
|
||||
cid.dcluster != EXFAT_EOF_CLUSTER ||
|
||||
cid.nr_contig != 0);
|
||||
}
|
||||
|
||||
if (*fclus == cluster)
|
||||
/* If only the first cluster is needed, return now. */
|
||||
if (fclus == cluster && *count == 1)
|
||||
return 0;
|
||||
|
||||
while (*fclus < cluster) {
|
||||
/* prevent the infinite loop of cluster chain */
|
||||
if (*fclus > limit) {
|
||||
exfat_fs_error(sb,
|
||||
"detected the cluster chain loop (i_pos %u)",
|
||||
(*fclus));
|
||||
return -EIO;
|
||||
}
|
||||
cache_init(&cid, fclus, *dclus);
|
||||
/*
|
||||
* Update the 'end' to exclude the next cache range, as clusters in
|
||||
* different cache are typically not contiguous.
|
||||
*/
|
||||
end = exfat_cache_lookup(inode, &cid, cluster, end, &fclus, dclus);
|
||||
|
||||
if (exfat_ent_get(sb, *dclus, &content))
|
||||
/* Return if the cache covers the entire range. */
|
||||
if (cid.fcluster + cid.nr_contig >= end) {
|
||||
*count = end - cluster + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find the first cluster we need. */
|
||||
while (fclus < cluster) {
|
||||
if (exfat_ent_get(sb, *dclus, &content, &bh))
|
||||
return -EIO;
|
||||
|
||||
*last_dclus = *dclus;
|
||||
*dclus = content;
|
||||
(*fclus)++;
|
||||
|
||||
if (content == EXFAT_EOF_CLUSTER) {
|
||||
if (!allow_eof) {
|
||||
exfat_fs_error(sb,
|
||||
"invalid cluster chain (i_pos %u, last_clus 0x%08x is EOF)",
|
||||
*fclus, (*last_dclus));
|
||||
return -EIO;
|
||||
}
|
||||
fclus++;
|
||||
|
||||
if (content == EXFAT_EOF_CLUSTER)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!cache_contiguous(&cid, *dclus))
|
||||
cache_init(&cid, *fclus, *dclus);
|
||||
cache_init(&cid, fclus, *dclus);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now the cid cache contains the first cluster requested, collect
|
||||
* the remaining clusters of this contiguous extent.
|
||||
*/
|
||||
if (*dclus != EXFAT_EOF_CLUSTER) {
|
||||
unsigned int clu = *dclus;
|
||||
|
||||
while (fclus < end) {
|
||||
if (exfat_ent_get(sb, clu, &content, &bh))
|
||||
return -EIO;
|
||||
if (++clu != content)
|
||||
break;
|
||||
fclus++;
|
||||
}
|
||||
cid.nr_contig = fclus - cid.fcluster;
|
||||
*count = fclus - cluster + 1;
|
||||
|
||||
/*
|
||||
* Cache this discontiguous cluster, we'll definitely need
|
||||
* it later
|
||||
*/
|
||||
if (fclus < end && content != EXFAT_EOF_CLUSTER) {
|
||||
exfat_cache_add(inode, &cid);
|
||||
cache_init(&cid, fclus + 1, content);
|
||||
}
|
||||
} else {
|
||||
*count = 0;
|
||||
}
|
||||
brelse(bh);
|
||||
exfat_cache_add(inode, &cid);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -432,13 +432,13 @@ int exfat_set_volume_dirty(struct super_block *sb);
|
|||
int exfat_clear_volume_dirty(struct super_block *sb);
|
||||
|
||||
/* fatent.c */
|
||||
#define exfat_get_next_cluster(sb, pclu) exfat_ent_get(sb, *(pclu), pclu)
|
||||
#define exfat_get_next_cluster(sb, pclu) exfat_ent_get(sb, *(pclu), pclu, NULL)
|
||||
|
||||
int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
||||
struct exfat_chain *p_chain, bool sync_bmap);
|
||||
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);
|
||||
unsigned int *content, struct buffer_head **last);
|
||||
int exfat_ent_set(struct super_block *sb, unsigned int loc,
|
||||
unsigned int content);
|
||||
int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain,
|
||||
|
|
@ -486,8 +486,7 @@ int exfat_cache_init(void);
|
|||
void exfat_cache_shutdown(void);
|
||||
void exfat_cache_inval_inode(struct inode *inode);
|
||||
int exfat_get_cluster(struct inode *inode, unsigned int cluster,
|
||||
unsigned int *fclus, unsigned int *dclus,
|
||||
unsigned int *last_dclus, int allow_eof);
|
||||
unsigned int *dclus, unsigned int *count, unsigned int *last_dclus);
|
||||
|
||||
/* dir.c */
|
||||
extern const struct inode_operations exfat_dir_inode_operations;
|
||||
|
|
|
|||
|
|
@ -36,18 +36,23 @@ static int exfat_mirror_bh(struct super_block *sb, sector_t sec,
|
|||
}
|
||||
|
||||
static int __exfat_ent_get(struct super_block *sb, unsigned int loc,
|
||||
unsigned int *content)
|
||||
unsigned int *content, struct buffer_head **last)
|
||||
{
|
||||
unsigned int off;
|
||||
sector_t sec;
|
||||
struct buffer_head *bh;
|
||||
struct buffer_head *bh = last ? *last : NULL;
|
||||
|
||||
sec = FAT_ENT_OFFSET_SECTOR(sb, loc);
|
||||
off = FAT_ENT_OFFSET_BYTE_IN_SECTOR(sb, loc);
|
||||
|
||||
bh = sb_bread(sb, sec);
|
||||
if (!bh)
|
||||
return -EIO;
|
||||
if (!bh || bh->b_blocknr != sec || !buffer_uptodate(bh)) {
|
||||
brelse(bh);
|
||||
bh = sb_bread(sb, sec);
|
||||
if (last)
|
||||
*last = bh;
|
||||
if (unlikely(!bh))
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*content = le32_to_cpu(*(__le32 *)(&bh->b_data[off]));
|
||||
|
||||
|
|
@ -55,7 +60,8 @@ static int __exfat_ent_get(struct super_block *sb, unsigned int loc,
|
|||
if (*content > EXFAT_BAD_CLUSTER)
|
||||
*content = EXFAT_EOF_CLUSTER;
|
||||
|
||||
brelse(bh);
|
||||
if (!last)
|
||||
brelse(bh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -82,49 +88,58 @@ int exfat_ent_set(struct super_block *sb, unsigned int loc,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Caller must release the buffer_head if no error return.
|
||||
*/
|
||||
int exfat_ent_get(struct super_block *sb, unsigned int loc,
|
||||
unsigned int *content)
|
||||
unsigned int *content, struct buffer_head **last)
|
||||
{
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
int err;
|
||||
|
||||
if (!is_valid_cluster(sbi, loc)) {
|
||||
exfat_fs_error_ratelimit(sb,
|
||||
"invalid access to FAT (entry 0x%08x)",
|
||||
loc);
|
||||
return -EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = __exfat_ent_get(sb, loc, content);
|
||||
if (err) {
|
||||
if (unlikely(__exfat_ent_get(sb, loc, content, last))) {
|
||||
exfat_fs_error_ratelimit(sb,
|
||||
"failed to access to FAT (entry 0x%08x, err:%d)",
|
||||
loc, err);
|
||||
return err;
|
||||
"failed to access to FAT (entry 0x%08x)",
|
||||
loc);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (*content == EXFAT_FREE_CLUSTER) {
|
||||
if (unlikely(*content == EXFAT_FREE_CLUSTER)) {
|
||||
exfat_fs_error_ratelimit(sb,
|
||||
"invalid access to FAT free cluster (entry 0x%08x)",
|
||||
loc);
|
||||
return -EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (*content == EXFAT_BAD_CLUSTER) {
|
||||
if (unlikely(*content == EXFAT_BAD_CLUSTER)) {
|
||||
exfat_fs_error_ratelimit(sb,
|
||||
"invalid access to FAT bad cluster (entry 0x%08x)",
|
||||
loc);
|
||||
return -EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (*content != EXFAT_EOF_CLUSTER && !is_valid_cluster(sbi, *content)) {
|
||||
exfat_fs_error_ratelimit(sb,
|
||||
"invalid access to FAT (entry 0x%08x) bogus content (0x%08x)",
|
||||
loc, *content);
|
||||
return -EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
if (last) {
|
||||
brelse(*last);
|
||||
|
||||
/* Avoid double release */
|
||||
*last = NULL;
|
||||
}
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain,
|
||||
|
|
@ -192,6 +207,7 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain
|
|||
if (p_chain->flags == ALLOC_NO_FAT_CHAIN) {
|
||||
int err;
|
||||
unsigned int last_cluster = p_chain->dir + p_chain->size - 1;
|
||||
|
||||
do {
|
||||
bool sync = false;
|
||||
|
||||
|
|
@ -281,6 +297,7 @@ int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain)
|
|||
int exfat_find_last_cluster(struct super_block *sb, struct exfat_chain *p_chain,
|
||||
unsigned int *ret_clu)
|
||||
{
|
||||
struct buffer_head *bh = NULL;
|
||||
unsigned int clu, next;
|
||||
unsigned int count = 0;
|
||||
|
||||
|
|
@ -293,10 +310,11 @@ int exfat_find_last_cluster(struct super_block *sb, struct exfat_chain *p_chain,
|
|||
do {
|
||||
count++;
|
||||
clu = next;
|
||||
if (exfat_ent_get(sb, clu, &next))
|
||||
if (exfat_ent_get(sb, clu, &next, &bh))
|
||||
return -EIO;
|
||||
} while (next != EXFAT_EOF_CLUSTER && count <= p_chain->size);
|
||||
|
||||
brelse(bh);
|
||||
if (p_chain->size != count) {
|
||||
exfat_fs_error(sb,
|
||||
"bogus directory size (clus : ondisk(%d) != counted(%d))",
|
||||
|
|
@ -469,6 +487,7 @@ int exfat_count_num_clusters(struct super_block *sb,
|
|||
unsigned int i, count;
|
||||
unsigned int clu;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
struct buffer_head *bh = NULL;
|
||||
|
||||
if (!p_chain->dir || p_chain->dir == EXFAT_EOF_CLUSTER) {
|
||||
*ret_count = 0;
|
||||
|
|
@ -484,12 +503,13 @@ int exfat_count_num_clusters(struct super_block *sb,
|
|||
count = 0;
|
||||
for (i = EXFAT_FIRST_CLUSTER; i < sbi->num_clusters; i++) {
|
||||
count++;
|
||||
if (exfat_ent_get(sb, clu, &clu))
|
||||
if (exfat_ent_get(sb, clu, &clu, &bh))
|
||||
return -EIO;
|
||||
if (clu == EXFAT_EOF_CLUSTER)
|
||||
break;
|
||||
}
|
||||
|
||||
brelse(bh);
|
||||
*ret_count = count;
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -683,6 +683,7 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
|
|||
|
||||
if (iocb->ki_pos > pos) {
|
||||
ssize_t err = generic_write_sync(iocb, iocb->ki_pos - pos);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
|
@ -708,21 +709,18 @@ static ssize_t exfat_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
|
|||
static vm_fault_t exfat_page_mkwrite(struct vm_fault *vmf)
|
||||
{
|
||||
int err;
|
||||
struct vm_area_struct *vma = vmf->vma;
|
||||
struct file *file = vma->vm_file;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct inode *inode = file_inode(vmf->vma->vm_file);
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
loff_t start, end;
|
||||
loff_t new_valid_size;
|
||||
|
||||
if (!inode_trylock(inode))
|
||||
return VM_FAULT_RETRY;
|
||||
|
||||
start = ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
|
||||
end = min_t(loff_t, i_size_read(inode),
|
||||
start + vma->vm_end - vma->vm_start);
|
||||
new_valid_size = ((loff_t)vmf->pgoff + 1) << PAGE_SHIFT;
|
||||
new_valid_size = min(new_valid_size, i_size_read(inode));
|
||||
|
||||
if (ei->valid_size < end) {
|
||||
err = exfat_extend_valid_size(inode, end);
|
||||
if (ei->valid_size < new_valid_size) {
|
||||
err = exfat_extend_valid_size(inode, new_valid_size);
|
||||
if (err < 0) {
|
||||
inode_unlock(inode);
|
||||
return vmf_fs_error(err);
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ void exfat_sync_inode(struct inode *inode)
|
|||
* *clu = (~0), if it's unable to allocate a new cluster
|
||||
*/
|
||||
static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
|
||||
unsigned int *clu, int create)
|
||||
unsigned int *clu, unsigned int *count, int create)
|
||||
{
|
||||
int ret;
|
||||
unsigned int last_clu;
|
||||
|
|
@ -147,39 +147,22 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
|
|||
|
||||
*clu = last_clu = ei->start_clu;
|
||||
|
||||
if (ei->flags == ALLOC_NO_FAT_CHAIN) {
|
||||
if (clu_offset > 0 && *clu != EXFAT_EOF_CLUSTER) {
|
||||
last_clu += clu_offset - 1;
|
||||
|
||||
if (clu_offset == num_clusters)
|
||||
*clu = EXFAT_EOF_CLUSTER;
|
||||
else
|
||||
*clu += clu_offset;
|
||||
if (*clu == EXFAT_EOF_CLUSTER) {
|
||||
*count = 0;
|
||||
} else if (ei->flags == ALLOC_NO_FAT_CHAIN) {
|
||||
last_clu += num_clusters - 1;
|
||||
if (clu_offset < num_clusters) {
|
||||
*clu += clu_offset;
|
||||
*count = min(num_clusters - clu_offset, *count);
|
||||
} else {
|
||||
*clu = EXFAT_EOF_CLUSTER;
|
||||
*count = 0;
|
||||
}
|
||||
} else if (ei->type == TYPE_FILE) {
|
||||
unsigned int fclus = 0;
|
||||
} else {
|
||||
int err = exfat_get_cluster(inode, clu_offset,
|
||||
&fclus, clu, &last_clu, 1);
|
||||
clu, count, &last_clu);
|
||||
if (err)
|
||||
return -EIO;
|
||||
|
||||
clu_offset -= fclus;
|
||||
} else {
|
||||
/* hint information */
|
||||
if (clu_offset > 0 && ei->hint_bmap.off != EXFAT_EOF_CLUSTER &&
|
||||
ei->hint_bmap.off > 0 && clu_offset >= ei->hint_bmap.off) {
|
||||
clu_offset -= ei->hint_bmap.off;
|
||||
/* hint_bmap.clu should be valid */
|
||||
WARN_ON(ei->hint_bmap.clu < 2);
|
||||
*clu = ei->hint_bmap.clu;
|
||||
}
|
||||
|
||||
while (clu_offset > 0 && *clu != EXFAT_EOF_CLUSTER) {
|
||||
last_clu = *clu;
|
||||
if (exfat_get_next_cluster(sb, clu))
|
||||
return -EIO;
|
||||
clu_offset--;
|
||||
}
|
||||
}
|
||||
|
||||
if (*clu == EXFAT_EOF_CLUSTER) {
|
||||
|
|
@ -251,7 +234,7 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
|
|||
num_to_be_allocated--;
|
||||
}
|
||||
}
|
||||
|
||||
*count = 1;
|
||||
}
|
||||
|
||||
/* hint information */
|
||||
|
|
@ -270,7 +253,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
|
|||
unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits;
|
||||
int err = 0;
|
||||
unsigned long mapped_blocks = 0;
|
||||
unsigned int cluster, sec_offset;
|
||||
unsigned int cluster, sec_offset, count;
|
||||
sector_t last_block;
|
||||
sector_t phys = 0;
|
||||
sector_t valid_blks;
|
||||
|
|
@ -283,8 +266,9 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
|
|||
goto done;
|
||||
|
||||
/* Is this block already allocated? */
|
||||
count = EXFAT_B_TO_CLU_ROUND_UP(bh_result->b_size, sbi);
|
||||
err = exfat_map_cluster(inode, iblock >> sbi->sect_per_clus_bits,
|
||||
&cluster, create);
|
||||
&cluster, &count, create);
|
||||
if (err) {
|
||||
if (err != -ENOSPC)
|
||||
exfat_fs_error_ratelimit(sb,
|
||||
|
|
@ -300,7 +284,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
|
|||
sec_offset = iblock & (sbi->sect_per_clus - 1);
|
||||
|
||||
phys = exfat_cluster_to_sector(sbi, cluster) + sec_offset;
|
||||
mapped_blocks = sbi->sect_per_clus - sec_offset;
|
||||
mapped_blocks = ((unsigned long)count << sbi->sect_per_clus_bits) - sec_offset;
|
||||
max_blocks = min(mapped_blocks, max_blocks);
|
||||
|
||||
map_bh(bh_result, sb, phys);
|
||||
|
|
@ -511,8 +495,9 @@ static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
|
|||
exfat_write_failed(mapping, size);
|
||||
|
||||
return ret;
|
||||
} else
|
||||
size = pos + ret;
|
||||
}
|
||||
|
||||
size = pos + ret;
|
||||
|
||||
if (rw == WRITE) {
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -304,8 +304,8 @@ int exfat_find_empty_entry(struct inode *inode,
|
|||
struct exfat_chain *p_dir, int num_entries,
|
||||
struct exfat_entry_set_cache *es)
|
||||
{
|
||||
int dentry;
|
||||
unsigned int ret, last_clu;
|
||||
int dentry, ret;
|
||||
unsigned int last_clu;
|
||||
loff_t size = 0;
|
||||
struct exfat_chain clu;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue