Commit af02c72d authored by Yuezhang Mo's avatar Yuezhang Mo Committed by Namjae Jeon

exfat: convert exfat_find_empty_entry() to use dentry cache

Before this conversion, each dentry traversed needs to be read
from the storage device or page cache. There are at least 16
dentries in a sector. This will result in frequent page cache
searches.

After this conversion, if all directory entries in a sector are
used, the sector only needs to be read once.
Signed-off-by: default avatarYuezhang Mo <Yuezhang.Mo@sony.com>
Reviewed-by: default avatarAndy Wu <Andy.Wu@sony.com>
Reviewed-by: default avatarAoyama Wataru <wataru.aoyama@sony.com>
Reviewed-by: default avatarSungjong Seo <sj1557.seo@samsung.com>
Signed-off-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
parent d97e0606
...@@ -204,21 +204,16 @@ const struct dentry_operations exfat_utf8_dentry_ops = { ...@@ -204,21 +204,16 @@ const struct dentry_operations exfat_utf8_dentry_ops = {
.d_compare = exfat_utf8_d_cmp, .d_compare = exfat_utf8_d_cmp,
}; };
/* used only in search empty_slot() */
#define CNT_UNUSED_NOHIT (-1)
#define CNT_UNUSED_HIT (-2)
/* search EMPTY CONTINUOUS "num_entries" entries */ /* search EMPTY CONTINUOUS "num_entries" entries */
static int exfat_search_empty_slot(struct super_block *sb, static int exfat_search_empty_slot(struct super_block *sb,
struct exfat_hint_femp *hint_femp, struct exfat_chain *p_dir, struct exfat_hint_femp *hint_femp, struct exfat_chain *p_dir,
int num_entries) int num_entries, struct exfat_entry_set_cache *es)
{ {
int i, dentry, num_empty = 0; int i, dentry, ret;
int dentries_per_clu; int dentries_per_clu;
unsigned int type;
struct exfat_chain clu; struct exfat_chain clu;
struct exfat_dentry *ep;
struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct buffer_head *bh; int total_entries = EXFAT_CLU_TO_DEN(p_dir->size, sbi);
dentries_per_clu = sbi->dentries_per_clu; dentries_per_clu = sbi->dentries_per_clu;
...@@ -231,7 +226,7 @@ static int exfat_search_empty_slot(struct super_block *sb, ...@@ -231,7 +226,7 @@ static int exfat_search_empty_slot(struct super_block *sb,
* Otherwise, and if "dentry + hint_famp->count" is also equal * Otherwise, and if "dentry + hint_famp->count" is also equal
* to "p_dir->size * dentries_per_clu", it means ENOSPC. * to "p_dir->size * dentries_per_clu", it means ENOSPC.
*/ */
if (dentry + hint_femp->count == p_dir->size * dentries_per_clu && if (dentry + hint_femp->count == total_entries &&
num_entries > hint_femp->count) num_entries > hint_femp->count)
return -ENOSPC; return -ENOSPC;
...@@ -242,69 +237,41 @@ static int exfat_search_empty_slot(struct super_block *sb, ...@@ -242,69 +237,41 @@ static int exfat_search_empty_slot(struct super_block *sb,
dentry = 0; dentry = 0;
} }
while (clu.dir != EXFAT_EOF_CLUSTER) { while (dentry + num_entries < total_entries &&
clu.dir != EXFAT_EOF_CLUSTER) {
i = dentry & (dentries_per_clu - 1); i = dentry & (dentries_per_clu - 1);
for (; i < dentries_per_clu; i++, dentry++) { ret = exfat_get_empty_dentry_set(es, sb, &clu, i, num_entries);
ep = exfat_get_dentry(sb, &clu, i, &bh); if (ret < 0)
if (!ep) return ret;
return -EIO; else if (ret == 0)
type = exfat_get_entry_type(ep); return dentry;
brelse(bh);
dentry += ret;
if (type == TYPE_UNUSED || type == TYPE_DELETED) { i += ret;
num_empty++;
if (hint_femp->eidx == EXFAT_HINT_NONE) { while (i >= dentries_per_clu) {
hint_femp->eidx = dentry; if (clu.flags == ALLOC_NO_FAT_CHAIN) {
hint_femp->count = CNT_UNUSED_NOHIT; if (--clu.size > 0)
exfat_chain_set(&hint_femp->cur, clu.dir++;
clu.dir, clu.size, clu.flags); else
} clu.dir = EXFAT_EOF_CLUSTER;
if (type == TYPE_UNUSED &&
hint_femp->count != CNT_UNUSED_HIT)
hint_femp->count = CNT_UNUSED_HIT;
} else { } else {
if (hint_femp->eidx != EXFAT_HINT_NONE && if (exfat_get_next_cluster(sb, &clu.dir))
hint_femp->count == CNT_UNUSED_HIT) {
/* unused empty group means
* an empty group which includes
* unused dentry
*/
exfat_fs_error(sb,
"found bogus dentry(%d) beyond unused empty group(%d) (start_clu : %u, cur_clu : %u)",
dentry, hint_femp->eidx,
p_dir->dir, clu.dir);
return -EIO; return -EIO;
}
num_empty = 0;
hint_femp->eidx = EXFAT_HINT_NONE;
} }
if (num_empty >= num_entries) { i -= dentries_per_clu;
/* found and invalidate hint_femp */
hint_femp->eidx = EXFAT_HINT_NONE;
return (dentry - (num_entries - 1));
}
}
if (clu.flags == ALLOC_NO_FAT_CHAIN) {
if (--clu.size > 0)
clu.dir++;
else
clu.dir = EXFAT_EOF_CLUSTER;
} else {
if (exfat_get_next_cluster(sb, &clu.dir))
return -EIO;
} }
} }
hint_femp->eidx = p_dir->size * dentries_per_clu - num_empty; hint_femp->eidx = dentry;
hint_femp->count = num_empty; hint_femp->count = 0;
if (num_empty == 0) if (dentry == total_entries || clu.dir == EXFAT_EOF_CLUSTER)
exfat_chain_set(&hint_femp->cur, EXFAT_EOF_CLUSTER, 0, exfat_chain_set(&hint_femp->cur, EXFAT_EOF_CLUSTER, 0,
clu.flags); clu.flags);
else
hint_femp->cur = clu;
return -ENOSPC; return -ENOSPC;
} }
...@@ -325,7 +292,8 @@ static int exfat_check_max_dentries(struct inode *inode) ...@@ -325,7 +292,8 @@ static int exfat_check_max_dentries(struct inode *inode)
* if there isn't any empty slot, expand cluster chain. * if there isn't any empty slot, expand cluster chain.
*/ */
static int exfat_find_empty_entry(struct inode *inode, static int exfat_find_empty_entry(struct inode *inode,
struct exfat_chain *p_dir, int num_entries) struct exfat_chain *p_dir, int num_entries,
struct exfat_entry_set_cache *es)
{ {
int dentry; int dentry;
unsigned int ret, last_clu; unsigned int ret, last_clu;
...@@ -344,7 +312,7 @@ static int exfat_find_empty_entry(struct inode *inode, ...@@ -344,7 +312,7 @@ static int exfat_find_empty_entry(struct inode *inode,
} }
while ((dentry = exfat_search_empty_slot(sb, &hint_femp, p_dir, while ((dentry = exfat_search_empty_slot(sb, &hint_femp, p_dir,
num_entries)) < 0) { num_entries, es)) < 0) {
if (dentry == -EIO) if (dentry == -EIO)
break; break;
...@@ -515,7 +483,7 @@ static int exfat_add_entry(struct inode *inode, const char *path, ...@@ -515,7 +483,7 @@ static int exfat_add_entry(struct inode *inode, const char *path,
} }
/* exfat_find_empty_entry must be called before alloc_cluster() */ /* exfat_find_empty_entry must be called before alloc_cluster() */
dentry = exfat_find_empty_entry(inode, p_dir, num_entries); dentry = exfat_find_empty_entry(inode, p_dir, num_entries, &es);
if (dentry < 0) { if (dentry < 0) {
ret = dentry; /* -EIO or -ENOSPC */ ret = dentry; /* -EIO or -ENOSPC */
goto out; goto out;
...@@ -523,8 +491,10 @@ static int exfat_add_entry(struct inode *inode, const char *path, ...@@ -523,8 +491,10 @@ static int exfat_add_entry(struct inode *inode, const char *path,
if (type == TYPE_DIR && !sbi->options.zero_size_dir) { if (type == TYPE_DIR && !sbi->options.zero_size_dir) {
ret = exfat_alloc_new_dir(inode, &clu); ret = exfat_alloc_new_dir(inode, &clu);
if (ret) if (ret) {
exfat_put_dentry_set(&es, false);
goto out; goto out;
}
start_clu = clu.dir; start_clu = clu.dir;
clu_size = sbi->cluster_size; clu_size = sbi->cluster_size;
} }
...@@ -533,11 +503,6 @@ static int exfat_add_entry(struct inode *inode, const char *path, ...@@ -533,11 +503,6 @@ static int exfat_add_entry(struct inode *inode, const char *path,
/* fill the dos name directory entry information of the created file. /* fill the dos name directory entry information of the created file.
* the first cluster is not determined yet. (0) * the first cluster is not determined yet. (0)
*/ */
ret = exfat_get_empty_dentry_set(&es, sb, p_dir, dentry, num_entries);
if (ret)
goto out;
exfat_init_dir_entry(&es, type, start_clu, clu_size, &ts); exfat_init_dir_entry(&es, type, start_clu, clu_size, &ts);
exfat_init_ext_entry(&es, num_entries, &uniname); exfat_init_ext_entry(&es, num_entries, &uniname);
...@@ -1033,18 +998,13 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir, ...@@ -1033,18 +998,13 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
if (old_es.num_entries < num_new_entries) { if (old_es.num_entries < num_new_entries) {
int newentry; int newentry;
newentry = newentry = exfat_find_empty_entry(inode, p_dir, num_new_entries,
exfat_find_empty_entry(inode, p_dir, num_new_entries); &new_es);
if (newentry < 0) { if (newentry < 0) {
ret = newentry; /* -EIO or -ENOSPC */ ret = newentry; /* -EIO or -ENOSPC */
goto put_old_es; goto put_old_es;
} }
ret = exfat_get_empty_dentry_set(&new_es, sb, p_dir, newentry,
num_new_entries);
if (ret)
goto put_old_es;
epnew = exfat_get_dentry_cached(&new_es, ES_IDX_FILE); epnew = exfat_get_dentry_cached(&new_es, ES_IDX_FILE);
*epnew = *epold; *epnew = *epold;
if (exfat_get_entry_type(epnew) == TYPE_FILE) { if (exfat_get_entry_type(epnew) == TYPE_FILE) {
...@@ -1094,19 +1054,17 @@ static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir, ...@@ -1094,19 +1054,17 @@ static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir,
if (num_new_entries < 0) if (num_new_entries < 0)
return num_new_entries; return num_new_entries;
newentry = exfat_find_empty_entry(inode, p_newdir, num_new_entries);
if (newentry < 0)
return newentry; /* -EIO or -ENOSPC */
ret = exfat_get_dentry_set(&mov_es, sb, p_olddir, oldentry, ret = exfat_get_dentry_set(&mov_es, sb, p_olddir, oldentry,
ES_ALL_ENTRIES); ES_ALL_ENTRIES);
if (ret) if (ret)
return -EIO; return -EIO;
ret = exfat_get_empty_dentry_set(&new_es, sb, p_newdir, newentry, newentry = exfat_find_empty_entry(inode, p_newdir, num_new_entries,
num_new_entries); &new_es);
if (ret) if (newentry < 0) {
ret = newentry; /* -EIO or -ENOSPC */
goto put_mov_es; goto put_mov_es;
}
epmov = exfat_get_dentry_cached(&mov_es, ES_IDX_FILE); epmov = exfat_get_dentry_cached(&mov_es, ES_IDX_FILE);
epnew = exfat_get_dentry_cached(&new_es, ES_IDX_FILE); epnew = exfat_get_dentry_cached(&new_es, ES_IDX_FILE);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment