Commit 84e57d29 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'exfat-for-6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat

Pull exfat update from Namjae Jeon:

 - simplify and remove some redundant directory entry code

 - optimize the size of exfat_entry_set_cache and its allocation policy

 - improve the performance for creating files and directories

* tag 'exfat-for-6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat:
  exfat: reuse exfat_find_location() to simplify exfat_get_dentry_set()
  exfat: fix overflow in sector and cluster conversion
  exfat: remove i_size_write() from __exfat_truncate()
  exfat: remove argument 'size' from exfat_truncate()
  exfat: remove unnecessary arguments from exfat_find_dir_entry()
  exfat: remove unneeded codes from __exfat_rename()
  exfat: remove call ilog2() from exfat_readdir()
  exfat: replace magic numbers with Macros
  exfat: rename exfat_free_dentry_set() to exfat_put_dentry_set()
  exfat: move exfat_entry_set_cache from heap to stack
  exfat: support dynamic allocate bh for exfat_entry_set_cache
  exfat: reduce the size of exfat_entry_set_cache
  exfat: hint the empty entry which at the end of cluster chain
  exfat: simplify empty entry hint
parents 23dc9c75 36955d36
...@@ -33,10 +33,9 @@ static void exfat_get_uniname_from_ext_entry(struct super_block *sb, ...@@ -33,10 +33,9 @@ static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
struct exfat_chain *p_dir, int entry, unsigned short *uniname) struct exfat_chain *p_dir, int entry, unsigned short *uniname)
{ {
int i; int i;
struct exfat_entry_set_cache *es; struct exfat_entry_set_cache es;
es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES); if (exfat_get_dentry_set(&es, sb, p_dir, entry, ES_ALL_ENTRIES))
if (!es)
return; return;
/* /*
...@@ -45,8 +44,8 @@ static void exfat_get_uniname_from_ext_entry(struct super_block *sb, ...@@ -45,8 +44,8 @@ static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
* Third entry : first file-name entry * Third entry : first file-name entry
* So, the index of first file-name dentry should start from 2. * So, the index of first file-name dentry should start from 2.
*/ */
for (i = 2; i < es->num_entries; i++) { for (i = ES_IDX_FIRST_FILENAME; i < es.num_entries; i++) {
struct exfat_dentry *ep = exfat_get_dentry_cached(es, i); struct exfat_dentry *ep = exfat_get_dentry_cached(&es, i);
/* end of name entry */ /* end of name entry */
if (exfat_get_entry_type(ep) != TYPE_EXTEND) if (exfat_get_entry_type(ep) != TYPE_EXTEND)
...@@ -56,13 +55,13 @@ static void exfat_get_uniname_from_ext_entry(struct super_block *sb, ...@@ -56,13 +55,13 @@ static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
uniname += EXFAT_FILE_NAME_LEN; uniname += EXFAT_FILE_NAME_LEN;
} }
exfat_free_dentry_set(es, false); exfat_put_dentry_set(&es, false);
} }
/* read a directory entry from the opened directory */ /* read a directory entry from the opened directory */
static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_entry *dir_entry) static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_entry *dir_entry)
{ {
int i, dentries_per_clu, dentries_per_clu_bits = 0, num_ext; int i, dentries_per_clu, num_ext;
unsigned int type, clu_offset, max_dentries; unsigned int type, clu_offset, max_dentries;
struct exfat_chain dir, clu; struct exfat_chain dir, clu;
struct exfat_uni_name uni_name; struct exfat_uni_name uni_name;
...@@ -84,11 +83,10 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent ...@@ -84,11 +83,10 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags); EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags);
dentries_per_clu = sbi->dentries_per_clu; dentries_per_clu = sbi->dentries_per_clu;
dentries_per_clu_bits = ilog2(dentries_per_clu);
max_dentries = (unsigned int)min_t(u64, MAX_EXFAT_DENTRIES, max_dentries = (unsigned int)min_t(u64, MAX_EXFAT_DENTRIES,
(u64)sbi->num_clusters << dentries_per_clu_bits); (u64)EXFAT_CLU_TO_DEN(sbi->num_clusters, sbi));
clu_offset = dentry >> dentries_per_clu_bits; clu_offset = EXFAT_DEN_TO_CLU(dentry, sbi);
exfat_chain_dup(&clu, &dir); exfat_chain_dup(&clu, &dir);
if (clu.flags == ALLOC_NO_FAT_CHAIN) { if (clu.flags == ALLOC_NO_FAT_CHAIN) {
...@@ -163,7 +161,7 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent ...@@ -163,7 +161,7 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
dir_entry->entry = dentry; dir_entry->entry = dentry;
brelse(bh); brelse(bh);
ei->hint_bmap.off = dentry >> dentries_per_clu_bits; ei->hint_bmap.off = EXFAT_DEN_TO_CLU(dentry, sbi);
ei->hint_bmap.clu = clu.dir; ei->hint_bmap.clu = clu.dir;
*cpos = EXFAT_DEN_TO_B(dentry + 1 + num_ext); *cpos = EXFAT_DEN_TO_B(dentry + 1 + num_ext);
...@@ -337,7 +335,7 @@ int exfat_calc_num_entries(struct exfat_uni_name *p_uniname) ...@@ -337,7 +335,7 @@ int exfat_calc_num_entries(struct exfat_uni_name *p_uniname)
return -EINVAL; return -EINVAL;
/* 1 file entry + 1 stream entry + name entries */ /* 1 file entry + 1 stream entry + name entries */
return ((len - 1) / EXFAT_FILE_NAME_LEN + 3); return ES_ENTRY_NUM(len);
} }
unsigned int exfat_get_entry_type(struct exfat_dentry *ep) unsigned int exfat_get_entry_type(struct exfat_dentry *ep)
...@@ -592,18 +590,18 @@ void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es) ...@@ -592,18 +590,18 @@ void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)
unsigned short chksum = 0; unsigned short chksum = 0;
struct exfat_dentry *ep; struct exfat_dentry *ep;
for (i = 0; i < es->num_entries; i++) { for (i = ES_IDX_FILE; i < es->num_entries; i++) {
ep = exfat_get_dentry_cached(es, i); ep = exfat_get_dentry_cached(es, i);
chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum, chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum,
chksum_type); chksum_type);
chksum_type = CS_DEFAULT; chksum_type = CS_DEFAULT;
} }
ep = exfat_get_dentry_cached(es, 0); ep = exfat_get_dentry_cached(es, ES_IDX_FILE);
ep->dentry.file.checksum = cpu_to_le16(chksum); ep->dentry.file.checksum = cpu_to_le16(chksum);
es->modified = true; es->modified = true;
} }
int exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync) int exfat_put_dentry_set(struct exfat_entry_set_cache *es, int sync)
{ {
int i, err = 0; int i, err = 0;
...@@ -615,7 +613,10 @@ int exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync) ...@@ -615,7 +613,10 @@ int exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
bforget(es->bh[i]); bforget(es->bh[i]);
else else
brelse(es->bh[i]); brelse(es->bh[i]);
kfree(es);
if (IS_DYNAMIC_ES(es))
kfree(es->bh);
return err; return err;
} }
...@@ -812,14 +813,14 @@ struct exfat_dentry *exfat_get_dentry_cached( ...@@ -812,14 +813,14 @@ struct exfat_dentry *exfat_get_dentry_cached(
* pointer of entry set on success, * pointer of entry set on success,
* NULL on failure. * NULL on failure.
*/ */
struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb, int exfat_get_dentry_set(struct exfat_entry_set_cache *es,
struct exfat_chain *p_dir, int entry, unsigned int type) struct super_block *sb, struct exfat_chain *p_dir, int entry,
unsigned int type)
{ {
int ret, i, num_bh; int ret, i, num_bh;
unsigned int off, byte_offset, clu = 0; unsigned int off;
sector_t sec; sector_t sec;
struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_entry_set_cache *es;
struct exfat_dentry *ep; struct exfat_dentry *ep;
int num_entries; int num_entries;
enum exfat_validate_dentry_mode mode = ES_MODE_STARTED; enum exfat_validate_dentry_mode mode = ES_MODE_STARTED;
...@@ -827,52 +828,51 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb, ...@@ -827,52 +828,51 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
if (p_dir->dir == DIR_DELETED) { if (p_dir->dir == DIR_DELETED) {
exfat_err(sb, "access to deleted dentry"); exfat_err(sb, "access to deleted dentry");
return NULL; return -EIO;
} }
byte_offset = EXFAT_DEN_TO_B(entry); ret = exfat_find_location(sb, p_dir, entry, &sec, &off);
ret = exfat_walk_fat_chain(sb, p_dir, byte_offset, &clu);
if (ret) if (ret)
return NULL; return ret;
es = kzalloc(sizeof(*es), GFP_KERNEL); memset(es, 0, sizeof(*es));
if (!es)
return NULL;
es->sb = sb; es->sb = sb;
es->modified = false; es->modified = false;
/* byte offset in cluster */
byte_offset = EXFAT_CLU_OFFSET(byte_offset, sbi);
/* byte offset in sector */
off = EXFAT_BLK_OFFSET(byte_offset, sb);
es->start_off = off; es->start_off = off;
es->bh = es->__bh;
/* sector offset in cluster */
sec = EXFAT_B_TO_BLK(byte_offset, sb);
sec += exfat_cluster_to_sector(sbi, clu);
bh = sb_bread(sb, sec); bh = sb_bread(sb, sec);
if (!bh) if (!bh)
goto free_es; return -EIO;
es->bh[es->num_bh++] = bh; es->bh[es->num_bh++] = bh;
ep = exfat_get_dentry_cached(es, 0); ep = exfat_get_dentry_cached(es, ES_IDX_FILE);
if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode)) if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
goto free_es; goto put_es;
num_entries = type == ES_ALL_ENTRIES ? num_entries = type == ES_ALL_ENTRIES ?
ep->dentry.file.num_ext + 1 : type; ep->dentry.file.num_ext + 1 : type;
es->num_entries = num_entries; es->num_entries = num_entries;
num_bh = EXFAT_B_TO_BLK_ROUND_UP(off + num_entries * DENTRY_SIZE, sb); num_bh = EXFAT_B_TO_BLK_ROUND_UP(off + num_entries * DENTRY_SIZE, sb);
if (num_bh > ARRAY_SIZE(es->__bh)) {
es->bh = kmalloc_array(num_bh, sizeof(*es->bh), GFP_KERNEL);
if (!es->bh) {
brelse(bh);
return -ENOMEM;
}
es->bh[0] = bh;
}
for (i = 1; i < num_bh; i++) { for (i = 1; i < num_bh; i++) {
/* get the next sector */ /* get the next sector */
if (exfat_is_last_sector_in_cluster(sbi, sec)) { if (exfat_is_last_sector_in_cluster(sbi, sec)) {
unsigned int clu = exfat_sector_to_cluster(sbi, sec);
if (p_dir->flags == ALLOC_NO_FAT_CHAIN) if (p_dir->flags == ALLOC_NO_FAT_CHAIN)
clu++; clu++;
else if (exfat_get_next_cluster(sb, &clu)) else if (exfat_get_next_cluster(sb, &clu))
goto free_es; goto put_es;
sec = exfat_cluster_to_sector(sbi, clu); sec = exfat_cluster_to_sector(sbi, clu);
} else { } else {
sec++; sec++;
...@@ -880,21 +880,51 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb, ...@@ -880,21 +880,51 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
bh = sb_bread(sb, sec); bh = sb_bread(sb, sec);
if (!bh) if (!bh)
goto free_es; goto put_es;
es->bh[es->num_bh++] = bh; es->bh[es->num_bh++] = bh;
} }
/* validate cached dentries */ /* validate cached dentries */
for (i = 1; i < num_entries; i++) { for (i = ES_IDX_STREAM; i < num_entries; i++) {
ep = exfat_get_dentry_cached(es, i); ep = exfat_get_dentry_cached(es, i);
if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode)) if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
goto free_es; goto put_es;
} }
return es; return 0;
free_es: put_es:
exfat_free_dentry_set(es, false); exfat_put_dentry_set(es, false);
return NULL; return -EIO;
}
static inline void exfat_reset_empty_hint(struct exfat_hint_femp *hint_femp)
{
hint_femp->eidx = EXFAT_HINT_NONE;
hint_femp->count = 0;
}
static inline void exfat_set_empty_hint(struct exfat_inode_info *ei,
struct exfat_hint_femp *candi_empty, struct exfat_chain *clu,
int dentry, int num_entries, int entry_type)
{
if (ei->hint_femp.eidx == EXFAT_HINT_NONE ||
ei->hint_femp.eidx > dentry) {
int total_entries = EXFAT_B_TO_DEN(i_size_read(&ei->vfs_inode));
if (candi_empty->count == 0) {
candi_empty->cur = *clu;
candi_empty->eidx = dentry;
}
if (entry_type == TYPE_UNUSED)
candi_empty->count += total_entries - dentry;
else
candi_empty->count++;
if (candi_empty->count == num_entries ||
candi_empty->count + candi_empty->eidx == total_entries)
ei->hint_femp = *candi_empty;
}
} }
enum { enum {
...@@ -917,17 +947,21 @@ enum { ...@@ -917,17 +947,21 @@ enum {
*/ */
int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname, struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname,
int num_entries, unsigned int type, struct exfat_hint *hint_opt) struct exfat_hint *hint_opt)
{ {
int i, rewind = 0, dentry = 0, end_eidx = 0, num_ext = 0, len; int i, rewind = 0, dentry = 0, end_eidx = 0, num_ext = 0, len;
int order, step, name_len = 0; int order, step, name_len = 0;
int dentries_per_clu, num_empty = 0; int dentries_per_clu;
unsigned int entry_type; unsigned int entry_type;
unsigned short *uniname = NULL; unsigned short *uniname = NULL;
struct exfat_chain clu; struct exfat_chain clu;
struct exfat_hint *hint_stat = &ei->hint_stat; struct exfat_hint *hint_stat = &ei->hint_stat;
struct exfat_hint_femp candi_empty; struct exfat_hint_femp candi_empty;
struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_sb_info *sbi = EXFAT_SB(sb);
int num_entries = exfat_calc_num_entries(p_uniname);
if (num_entries < 0)
return num_entries;
dentries_per_clu = sbi->dentries_per_clu; dentries_per_clu = sbi->dentries_per_clu;
...@@ -939,10 +973,13 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, ...@@ -939,10 +973,13 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
end_eidx = dentry; end_eidx = dentry;
} }
candi_empty.eidx = EXFAT_HINT_NONE; exfat_reset_empty_hint(&ei->hint_femp);
rewind: rewind:
order = 0; order = 0;
step = DIRENT_STEP_FILE; step = DIRENT_STEP_FILE;
exfat_reset_empty_hint(&candi_empty);
while (clu.dir != EXFAT_EOF_CLUSTER) { while (clu.dir != EXFAT_EOF_CLUSTER) {
i = dentry & (dentries_per_clu - 1); i = dentry & (dentries_per_clu - 1);
for (; i < dentries_per_clu; i++, dentry++) { for (; i < dentries_per_clu; i++, dentry++) {
...@@ -962,26 +999,9 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, ...@@ -962,26 +999,9 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
entry_type == TYPE_DELETED) { entry_type == TYPE_DELETED) {
step = DIRENT_STEP_FILE; step = DIRENT_STEP_FILE;
num_empty++; exfat_set_empty_hint(ei, &candi_empty, &clu,
if (candi_empty.eidx == EXFAT_HINT_NONE && dentry, num_entries,
num_empty == 1) { entry_type);
exfat_chain_set(&candi_empty.cur,
clu.dir, clu.size, clu.flags);
}
if (candi_empty.eidx == EXFAT_HINT_NONE &&
num_empty >= num_entries) {
candi_empty.eidx =
dentry - (num_empty - 1);
WARN_ON(candi_empty.eidx < 0);
candi_empty.count = num_empty;
if (ei->hint_femp.eidx ==
EXFAT_HINT_NONE ||
candi_empty.eidx <=
ei->hint_femp.eidx)
ei->hint_femp = candi_empty;
}
brelse(bh); brelse(bh);
if (entry_type == TYPE_UNUSED) if (entry_type == TYPE_UNUSED)
...@@ -989,17 +1009,14 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, ...@@ -989,17 +1009,14 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
continue; continue;
} }
num_empty = 0; exfat_reset_empty_hint(&candi_empty);
candi_empty.eidx = EXFAT_HINT_NONE;
if (entry_type == TYPE_FILE || entry_type == TYPE_DIR) { if (entry_type == TYPE_FILE || entry_type == TYPE_DIR) {
step = DIRENT_STEP_FILE; step = DIRENT_STEP_FILE;
hint_opt->clu = clu.dir; hint_opt->clu = clu.dir;
hint_opt->eidx = i; hint_opt->eidx = i;
if (type == TYPE_ALL || type == entry_type) {
num_ext = ep->dentry.file.num_ext; num_ext = ep->dentry.file.num_ext;
step = DIRENT_STEP_STRM; step = DIRENT_STEP_STRM;
}
brelse(bh); brelse(bh);
continue; continue;
} }
...@@ -1090,12 +1107,19 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, ...@@ -1090,12 +1107,19 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
rewind = 1; rewind = 1;
dentry = 0; dentry = 0;
clu.dir = p_dir->dir; clu.dir = p_dir->dir;
/* reset empty hint */
num_empty = 0;
candi_empty.eidx = EXFAT_HINT_NONE;
goto rewind; goto rewind;
} }
/*
* set the EXFAT_EOF_CLUSTER flag to avoid search
* from the beginning again when allocated a new cluster
*/
if (ei->hint_femp.eidx == EXFAT_HINT_NONE) {
ei->hint_femp.cur.dir = EXFAT_EOF_CLUSTER;
ei->hint_femp.eidx = p_dir->size * dentries_per_clu;
ei->hint_femp.count = 0;
}
/* initialized hint_stat */ /* initialized hint_stat */
hint_stat->clu = p_dir->dir; hint_stat->clu = p_dir->dir;
hint_stat->eidx = 0; hint_stat->eidx = 0;
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/ratelimit.h> #include <linux/ratelimit.h>
#include <linux/nls.h> #include <linux/nls.h>
#include <linux/blkdev.h>
#define EXFAT_ROOT_INO 1 #define EXFAT_ROOT_INO 1
...@@ -41,6 +42,14 @@ enum { ...@@ -41,6 +42,14 @@ enum {
#define ES_2_ENTRIES 2 #define ES_2_ENTRIES 2
#define ES_ALL_ENTRIES 0 #define ES_ALL_ENTRIES 0
#define ES_IDX_FILE 0
#define ES_IDX_STREAM 1
#define ES_IDX_FIRST_FILENAME 2
#define EXFAT_FILENAME_ENTRY_NUM(name_len) \
DIV_ROUND_UP(name_len, EXFAT_FILE_NAME_LEN)
#define ES_IDX_LAST_FILENAME(name_len) \
(ES_IDX_FIRST_FILENAME + EXFAT_FILENAME_ENTRY_NUM(name_len) - 1)
#define DIR_DELETED 0xFFFF0321 #define DIR_DELETED 0xFFFF0321
/* type values */ /* type values */
...@@ -62,15 +71,11 @@ enum { ...@@ -62,15 +71,11 @@ enum {
#define TYPE_PADDING 0x0402 #define TYPE_PADDING 0x0402
#define TYPE_ACLTAB 0x0403 #define TYPE_ACLTAB 0x0403
#define TYPE_BENIGN_SEC 0x0800 #define TYPE_BENIGN_SEC 0x0800
#define TYPE_ALL 0x0FFF
#define MAX_CHARSET_SIZE 6 /* max size of multi-byte character */ #define MAX_CHARSET_SIZE 6 /* max size of multi-byte character */
#define MAX_NAME_LENGTH 255 /* max len of file name excluding NULL */ #define MAX_NAME_LENGTH 255 /* max len of file name excluding NULL */
#define MAX_VFSNAME_BUF_SIZE ((MAX_NAME_LENGTH + 1) * MAX_CHARSET_SIZE) #define MAX_VFSNAME_BUF_SIZE ((MAX_NAME_LENGTH + 1) * MAX_CHARSET_SIZE)
/* Enough size to hold 256 dentry (even 512 Byte sector) */
#define DIR_CACHE_SIZE (256*sizeof(struct exfat_dentry)/512+1)
#define EXFAT_HINT_NONE -1 #define EXFAT_HINT_NONE -1
#define EXFAT_MIN_SUBDIR 2 #define EXFAT_MIN_SUBDIR 2
...@@ -95,11 +100,17 @@ enum { ...@@ -95,11 +100,17 @@ enum {
/* /*
* helpers for block size to dentry size conversion. * helpers for block size to dentry size conversion.
*/ */
#define EXFAT_B_TO_DEN_IDX(b, sbi) \
((b) << ((sbi)->cluster_size_bits - DENTRY_SIZE_BITS))
#define EXFAT_B_TO_DEN(b) ((b) >> DENTRY_SIZE_BITS) #define EXFAT_B_TO_DEN(b) ((b) >> DENTRY_SIZE_BITS)
#define EXFAT_DEN_TO_B(b) ((b) << DENTRY_SIZE_BITS) #define EXFAT_DEN_TO_B(b) ((b) << DENTRY_SIZE_BITS)
/*
* helpers for cluster size to dentry size conversion.
*/
#define EXFAT_CLU_TO_DEN(clu, sbi) \
((clu) << ((sbi)->cluster_size_bits - DENTRY_SIZE_BITS))
#define EXFAT_DEN_TO_CLU(dentry, sbi) \
((dentry) >> ((sbi)->cluster_size_bits - DENTRY_SIZE_BITS))
/* /*
* helpers for fat entry. * helpers for fat entry.
*/ */
...@@ -125,6 +136,17 @@ enum { ...@@ -125,6 +136,17 @@ enum {
#define BITS_PER_BYTE_MASK 0x7 #define BITS_PER_BYTE_MASK 0x7
#define IGNORED_BITS_REMAINED(clu, clu_base) ((1 << ((clu) - (clu_base))) - 1) #define IGNORED_BITS_REMAINED(clu, clu_base) ((1 << ((clu) - (clu_base))) - 1)
#define ES_ENTRY_NUM(name_len) (ES_IDX_LAST_FILENAME(name_len) + 1)
/* 19 entries = 1 file entry + 1 stream entry + 17 filename entries */
#define ES_MAX_ENTRY_NUM ES_ENTRY_NUM(MAX_NAME_LENGTH)
/*
* 19 entries x 32 bytes/entry = 608 bytes.
* The 608 bytes are in 3 sectors at most (even 512 Byte sector).
*/
#define DIR_CACHE_SIZE \
(DIV_ROUND_UP(EXFAT_DEN_TO_B(ES_MAX_ENTRY_NUM), SECTOR_SIZE) + 1)
struct exfat_dentry_namebuf { struct exfat_dentry_namebuf {
char *lfn; char *lfn;
int lfnbuf_len; /* usually MAX_UNINAME_BUF_SIZE */ int lfnbuf_len; /* usually MAX_UNINAME_BUF_SIZE */
...@@ -166,13 +188,16 @@ struct exfat_hint { ...@@ -166,13 +188,16 @@ struct exfat_hint {
struct exfat_entry_set_cache { struct exfat_entry_set_cache {
struct super_block *sb; struct super_block *sb;
bool modified;
unsigned int start_off; unsigned int start_off;
int num_bh; int num_bh;
struct buffer_head *bh[DIR_CACHE_SIZE]; struct buffer_head *__bh[DIR_CACHE_SIZE];
struct buffer_head **bh;
unsigned int num_entries; unsigned int num_entries;
bool modified;
}; };
#define IS_DYNAMIC_ES(es) ((es)->__bh != (es)->bh)
struct exfat_dir_entry { struct exfat_dir_entry {
struct exfat_chain dir; struct exfat_chain dir;
int entry; int entry;
...@@ -375,7 +400,7 @@ static inline sector_t exfat_cluster_to_sector(struct exfat_sb_info *sbi, ...@@ -375,7 +400,7 @@ static inline sector_t exfat_cluster_to_sector(struct exfat_sb_info *sbi,
sbi->data_start_sector; sbi->data_start_sector;
} }
static inline int exfat_sector_to_cluster(struct exfat_sb_info *sbi, static inline unsigned int exfat_sector_to_cluster(struct exfat_sb_info *sbi,
sector_t sec) sector_t sec)
{ {
return ((sec - sbi->data_start_sector) >> sbi->sect_per_clus_bits) + return ((sec - sbi->data_start_sector) >> sbi->sect_per_clus_bits) +
...@@ -423,8 +448,8 @@ int exfat_trim_fs(struct inode *inode, struct fstrim_range *range); ...@@ -423,8 +448,8 @@ int exfat_trim_fs(struct inode *inode, struct fstrim_range *range);
/* file.c */ /* file.c */
extern const struct file_operations exfat_file_operations; extern const struct file_operations exfat_file_operations;
int __exfat_truncate(struct inode *inode, loff_t new_size); int __exfat_truncate(struct inode *inode);
void exfat_truncate(struct inode *inode, loff_t size); void exfat_truncate(struct inode *inode);
int exfat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, int exfat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
struct iattr *attr); struct iattr *attr);
int exfat_getattr(struct user_namespace *mnt_userns, const struct path *path, int exfat_getattr(struct user_namespace *mnt_userns, const struct path *path,
...@@ -464,15 +489,16 @@ void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es); ...@@ -464,15 +489,16 @@ void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es);
int exfat_calc_num_entries(struct exfat_uni_name *p_uniname); int exfat_calc_num_entries(struct exfat_uni_name *p_uniname);
int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname, struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname,
int num_entries, unsigned int type, struct exfat_hint *hint_opt); struct exfat_hint *hint_opt);
int exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu); int exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu);
struct exfat_dentry *exfat_get_dentry(struct super_block *sb, struct exfat_dentry *exfat_get_dentry(struct super_block *sb,
struct exfat_chain *p_dir, int entry, struct buffer_head **bh); struct exfat_chain *p_dir, int entry, struct buffer_head **bh);
struct exfat_dentry *exfat_get_dentry_cached(struct exfat_entry_set_cache *es, struct exfat_dentry *exfat_get_dentry_cached(struct exfat_entry_set_cache *es,
int num); int num);
struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb, int exfat_get_dentry_set(struct exfat_entry_set_cache *es,
struct exfat_chain *p_dir, int entry, unsigned int type); struct super_block *sb, struct exfat_chain *p_dir, int entry,
int exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync); unsigned int type);
int exfat_put_dentry_set(struct exfat_entry_set_cache *es, int sync);
int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir); int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir);
/* inode.c */ /* inode.c */
......
...@@ -93,7 +93,7 @@ static int exfat_sanitize_mode(const struct exfat_sb_info *sbi, ...@@ -93,7 +93,7 @@ static int exfat_sanitize_mode(const struct exfat_sb_info *sbi,
} }
/* resize the file length */ /* resize the file length */
int __exfat_truncate(struct inode *inode, loff_t new_size) int __exfat_truncate(struct inode *inode)
{ {
unsigned int num_clusters_new, num_clusters_phys; unsigned int num_clusters_new, num_clusters_phys;
unsigned int last_clu = EXFAT_FREE_CLUSTER; unsigned int last_clu = EXFAT_FREE_CLUSTER;
...@@ -113,7 +113,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size) ...@@ -113,7 +113,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
exfat_chain_set(&clu, ei->start_clu, num_clusters_phys, ei->flags); exfat_chain_set(&clu, ei->start_clu, num_clusters_phys, ei->flags);
if (new_size > 0) { if (i_size_read(inode) > 0) {
/* /*
* Truncate FAT chain num_clusters after the first cluster * Truncate FAT chain num_clusters after the first cluster
* num_clusters = min(new, phys); * num_clusters = min(new, phys);
...@@ -143,8 +143,6 @@ int __exfat_truncate(struct inode *inode, loff_t new_size) ...@@ -143,8 +143,6 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
ei->start_clu = EXFAT_EOF_CLUSTER; ei->start_clu = EXFAT_EOF_CLUSTER;
} }
i_size_write(inode, new_size);
if (ei->type == TYPE_FILE) if (ei->type == TYPE_FILE)
ei->attr |= ATTR_ARCHIVE; ei->attr |= ATTR_ARCHIVE;
...@@ -189,7 +187,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size) ...@@ -189,7 +187,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
return 0; return 0;
} }
void exfat_truncate(struct inode *inode, loff_t size) void exfat_truncate(struct inode *inode)
{ {
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_sb_info *sbi = EXFAT_SB(sb);
...@@ -207,7 +205,7 @@ void exfat_truncate(struct inode *inode, loff_t size) ...@@ -207,7 +205,7 @@ void exfat_truncate(struct inode *inode, loff_t size)
goto write_size; goto write_size;
} }
err = __exfat_truncate(inode, i_size_read(inode)); err = __exfat_truncate(inode);
if (err) if (err)
goto write_size; goto write_size;
...@@ -310,7 +308,7 @@ int exfat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -310,7 +308,7 @@ int exfat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
* __exfat_write_inode() is called from exfat_truncate(), inode * __exfat_write_inode() is called from exfat_truncate(), inode
* is already written by it, so mark_inode_dirty() is unneeded. * is already written by it, so mark_inode_dirty() is unneeded.
*/ */
exfat_truncate(inode, attr->ia_size); exfat_truncate(inode);
up_write(&EXFAT_I(inode)->truncate_lock); up_write(&EXFAT_I(inode)->truncate_lock);
} else } else
mark_inode_dirty(inode); mark_inode_dirty(inode);
......
...@@ -21,7 +21,7 @@ int __exfat_write_inode(struct inode *inode, int sync) ...@@ -21,7 +21,7 @@ int __exfat_write_inode(struct inode *inode, int sync)
{ {
unsigned long long on_disk_size; unsigned long long on_disk_size;
struct exfat_dentry *ep, *ep2; struct exfat_dentry *ep, *ep2;
struct exfat_entry_set_cache *es = NULL; struct exfat_entry_set_cache es;
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(inode); struct exfat_inode_info *ei = EXFAT_I(inode);
...@@ -42,11 +42,10 @@ int __exfat_write_inode(struct inode *inode, int sync) ...@@ -42,11 +42,10 @@ int __exfat_write_inode(struct inode *inode, int sync)
exfat_set_volume_dirty(sb); exfat_set_volume_dirty(sb);
/* get the directory entry of given file or directory */ /* get the directory entry of given file or directory */
es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry, ES_ALL_ENTRIES); if (exfat_get_dentry_set(&es, sb, &(ei->dir), ei->entry, ES_ALL_ENTRIES))
if (!es)
return -EIO; return -EIO;
ep = exfat_get_dentry_cached(es, 0); ep = exfat_get_dentry_cached(&es, ES_IDX_FILE);
ep2 = exfat_get_dentry_cached(es, 1); ep2 = exfat_get_dentry_cached(&es, ES_IDX_STREAM);
ep->dentry.file.attr = cpu_to_le16(exfat_make_attr(inode)); ep->dentry.file.attr = cpu_to_le16(exfat_make_attr(inode));
...@@ -83,8 +82,8 @@ int __exfat_write_inode(struct inode *inode, int sync) ...@@ -83,8 +82,8 @@ int __exfat_write_inode(struct inode *inode, int sync)
ep2->dentry.stream.start_clu = EXFAT_FREE_CLUSTER; ep2->dentry.stream.start_clu = EXFAT_FREE_CLUSTER;
} }
exfat_update_dir_chksum_with_entry_set(es); exfat_update_dir_chksum_with_entry_set(&es);
return exfat_free_dentry_set(es, sync); return exfat_put_dentry_set(&es, sync);
} }
int exfat_write_inode(struct inode *inode, struct writeback_control *wbc) int exfat_write_inode(struct inode *inode, struct writeback_control *wbc)
...@@ -358,7 +357,7 @@ static void exfat_write_failed(struct address_space *mapping, loff_t to) ...@@ -358,7 +357,7 @@ static void exfat_write_failed(struct address_space *mapping, loff_t to)
if (to > i_size_read(inode)) { if (to > i_size_read(inode)) {
truncate_pagecache(inode, i_size_read(inode)); truncate_pagecache(inode, i_size_read(inode));
inode->i_mtime = inode->i_ctime = current_time(inode); inode->i_mtime = inode->i_ctime = current_time(inode);
exfat_truncate(inode, EXFAT_I(inode)->i_size_aligned); exfat_truncate(inode);
} }
} }
...@@ -622,7 +621,7 @@ void exfat_evict_inode(struct inode *inode) ...@@ -622,7 +621,7 @@ void exfat_evict_inode(struct inode *inode)
if (!inode->i_nlink) { if (!inode->i_nlink) {
i_size_write(inode, 0); i_size_write(inode, 0);
mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock); mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock);
__exfat_truncate(inode, 0); __exfat_truncate(inode);
mutex_unlock(&EXFAT_SB(inode->i_sb)->s_lock); mutex_unlock(&EXFAT_SB(inode->i_sb)->s_lock);
} }
......
...@@ -224,11 +224,18 @@ static int exfat_search_empty_slot(struct super_block *sb, ...@@ -224,11 +224,18 @@ static int exfat_search_empty_slot(struct super_block *sb,
if (hint_femp->eidx != EXFAT_HINT_NONE) { if (hint_femp->eidx != EXFAT_HINT_NONE) {
dentry = hint_femp->eidx; dentry = hint_femp->eidx;
if (num_entries <= hint_femp->count) {
hint_femp->eidx = EXFAT_HINT_NONE;
return dentry;
}
/*
* If hint_femp->count is enough, it is needed to check if
* there are actual empty entries.
* Otherwise, and if "dentry + hint_famp->count" is also equal
* to "p_dir->size * dentries_per_clu", it means ENOSPC.
*/
if (dentry + hint_femp->count == p_dir->size * dentries_per_clu &&
num_entries > hint_femp->count)
return -ENOSPC;
hint_femp->eidx = EXFAT_HINT_NONE;
exfat_chain_dup(&clu, &hint_femp->cur); exfat_chain_dup(&clu, &hint_femp->cur);
} else { } else {
exfat_chain_dup(&clu, p_dir); exfat_chain_dup(&clu, p_dir);
...@@ -293,6 +300,12 @@ static int exfat_search_empty_slot(struct super_block *sb, ...@@ -293,6 +300,12 @@ static int exfat_search_empty_slot(struct super_block *sb,
} }
} }
hint_femp->eidx = p_dir->size * dentries_per_clu - num_empty;
hint_femp->count = num_empty;
if (num_empty == 0)
exfat_chain_set(&hint_femp->cur, EXFAT_EOF_CLUSTER, 0,
clu.flags);
return -ENOSPC; return -ENOSPC;
} }
...@@ -369,15 +382,11 @@ static int exfat_find_empty_entry(struct inode *inode, ...@@ -369,15 +382,11 @@ static int exfat_find_empty_entry(struct inode *inode,
if (exfat_ent_set(sb, last_clu, clu.dir)) if (exfat_ent_set(sb, last_clu, clu.dir))
return -EIO; return -EIO;
if (hint_femp.eidx == EXFAT_HINT_NONE) { if (hint_femp.cur.dir == EXFAT_EOF_CLUSTER)
/* the special case that new dentry
* should be allocated from the start of new cluster
*/
hint_femp.eidx = EXFAT_B_TO_DEN_IDX(p_dir->size, sbi);
hint_femp.count = sbi->dentries_per_clu;
exfat_chain_set(&hint_femp.cur, clu.dir, 0, clu.flags); exfat_chain_set(&hint_femp.cur, clu.dir, 0, clu.flags);
}
hint_femp.count += sbi->dentries_per_clu;
hint_femp.cur.size++; hint_femp.cur.size++;
p_dir->size++; p_dir->size++;
size = EXFAT_CLU_TO_B(p_dir->size, sbi); size = EXFAT_CLU_TO_B(p_dir->size, sbi);
...@@ -588,14 +597,14 @@ static int exfat_create(struct user_namespace *mnt_userns, struct inode *dir, ...@@ -588,14 +597,14 @@ static int exfat_create(struct user_namespace *mnt_userns, struct inode *dir,
static int exfat_find(struct inode *dir, struct qstr *qname, static int exfat_find(struct inode *dir, struct qstr *qname,
struct exfat_dir_entry *info) struct exfat_dir_entry *info)
{ {
int ret, dentry, num_entries, count; int ret, dentry, count;
struct exfat_chain cdir; struct exfat_chain cdir;
struct exfat_uni_name uni_name; struct exfat_uni_name uni_name;
struct super_block *sb = dir->i_sb; struct super_block *sb = dir->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(dir); struct exfat_inode_info *ei = EXFAT_I(dir);
struct exfat_dentry *ep, *ep2; struct exfat_dentry *ep, *ep2;
struct exfat_entry_set_cache *es; struct exfat_entry_set_cache es;
/* for optimized dir & entry to prevent long traverse of cluster chain */ /* for optimized dir & entry to prevent long traverse of cluster chain */
struct exfat_hint hint_opt; struct exfat_hint hint_opt;
...@@ -607,10 +616,6 @@ static int exfat_find(struct inode *dir, struct qstr *qname, ...@@ -607,10 +616,6 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
if (ret) if (ret)
return ret; return ret;
num_entries = exfat_calc_num_entries(&uni_name);
if (num_entries < 0)
return num_entries;
/* check the validation of hint_stat and initialize it if required */ /* check the validation of hint_stat and initialize it if required */
if (ei->version != (inode_peek_iversion_raw(dir) & 0xffffffff)) { if (ei->version != (inode_peek_iversion_raw(dir) & 0xffffffff)) {
ei->hint_stat.clu = cdir.dir; ei->hint_stat.clu = cdir.dir;
...@@ -620,9 +625,7 @@ static int exfat_find(struct inode *dir, struct qstr *qname, ...@@ -620,9 +625,7 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
} }
/* search the file name for directories */ /* search the file name for directories */
dentry = exfat_find_dir_entry(sb, ei, &cdir, &uni_name, dentry = exfat_find_dir_entry(sb, ei, &cdir, &uni_name, &hint_opt);
num_entries, TYPE_ALL, &hint_opt);
if (dentry < 0) if (dentry < 0)
return dentry; /* -error value */ return dentry; /* -error value */
...@@ -635,11 +638,10 @@ static int exfat_find(struct inode *dir, struct qstr *qname, ...@@ -635,11 +638,10 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
if (cdir.flags & ALLOC_NO_FAT_CHAIN) if (cdir.flags & ALLOC_NO_FAT_CHAIN)
cdir.size -= dentry / sbi->dentries_per_clu; cdir.size -= dentry / sbi->dentries_per_clu;
dentry = hint_opt.eidx; dentry = hint_opt.eidx;
es = exfat_get_dentry_set(sb, &cdir, dentry, ES_2_ENTRIES); if (exfat_get_dentry_set(&es, sb, &cdir, dentry, ES_2_ENTRIES))
if (!es)
return -EIO; return -EIO;
ep = exfat_get_dentry_cached(es, 0); ep = exfat_get_dentry_cached(&es, ES_IDX_FILE);
ep2 = exfat_get_dentry_cached(es, 1); ep2 = exfat_get_dentry_cached(&es, ES_IDX_STREAM);
info->type = exfat_get_entry_type(ep); info->type = exfat_get_entry_type(ep);
info->attr = le16_to_cpu(ep->dentry.file.attr); info->attr = le16_to_cpu(ep->dentry.file.attr);
...@@ -668,7 +670,7 @@ static int exfat_find(struct inode *dir, struct qstr *qname, ...@@ -668,7 +670,7 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
ep->dentry.file.access_time, ep->dentry.file.access_time,
ep->dentry.file.access_date, ep->dentry.file.access_date,
0); 0);
exfat_free_dentry_set(es, false); exfat_put_dentry_set(&es, false);
if (ei->start_clu == EXFAT_FREE_CLUSTER) { if (ei->start_clu == EXFAT_FREE_CLUSTER) {
exfat_fs_error(sb, exfat_fs_error(sb,
...@@ -1167,7 +1169,7 @@ static int __exfat_rename(struct inode *old_parent_inode, ...@@ -1167,7 +1169,7 @@ static int __exfat_rename(struct inode *old_parent_inode,
struct exfat_inode_info *new_ei = NULL; struct exfat_inode_info *new_ei = NULL;
unsigned int new_entry_type = TYPE_UNUSED; unsigned int new_entry_type = TYPE_UNUSED;
int new_entry = 0; int new_entry = 0;
struct buffer_head *old_bh, *new_bh = NULL; struct buffer_head *new_bh = NULL;
/* check the validity of pointer parameters */ /* check the validity of pointer parameters */
if (new_path == NULL || strlen(new_path) == 0) if (new_path == NULL || strlen(new_path) == 0)
...@@ -1183,13 +1185,6 @@ static int __exfat_rename(struct inode *old_parent_inode, ...@@ -1183,13 +1185,6 @@ static int __exfat_rename(struct inode *old_parent_inode,
EXFAT_I(old_parent_inode)->flags); EXFAT_I(old_parent_inode)->flags);
dentry = ei->entry; dentry = ei->entry;
ep = exfat_get_dentry(sb, &olddir, dentry, &old_bh);
if (!ep) {
ret = -EIO;
goto out;
}
brelse(old_bh);
/* check whether new dir is existing directory and empty */ /* check whether new dir is existing directory and empty */
if (new_inode) { if (new_inode) {
ret = -EIO; ret = -EIO;
......
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