hfsplus: optimize fsync

Avoid doing unessecary work in fsync.  Do nothing unless the inode
was marked dirty, and only write the various metadata inodes out if
they contain any dirty state from this inode.  This is archived by
adding three new dirty bits to the hfsplus-specific inode which are
set in the correct places.

Signed-off-by: Christoph Hellwig <hch@tuxera.com>
This commit is contained in:
Christoph Hellwig 2010-11-23 14:38:15 +01:00 committed by Christoph Hellwig
parent b33b7921db
commit e349470560
6 changed files with 78 additions and 32 deletions

View File

@ -227,7 +227,8 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir, struct qstr *str, struct ino
dir->i_size++; dir->i_size++;
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(dir); hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
hfs_find_exit(&fd); hfs_find_exit(&fd);
return 0; return 0;
@ -308,7 +309,7 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
dir->i_size--; dir->i_size--;
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(dir); hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
out: out:
hfs_find_exit(&fd); hfs_find_exit(&fd);
@ -353,7 +354,6 @@ int hfsplus_rename_cat(u32 cnid,
goto out; goto out;
dst_dir->i_size++; dst_dir->i_size++;
dst_dir->i_mtime = dst_dir->i_ctime = CURRENT_TIME_SEC; dst_dir->i_mtime = dst_dir->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(dst_dir);
/* finally remove the old entry */ /* finally remove the old entry */
hfsplus_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name); hfsplus_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name);
@ -365,7 +365,6 @@ int hfsplus_rename_cat(u32 cnid,
goto out; goto out;
src_dir->i_size--; src_dir->i_size--;
src_dir->i_mtime = src_dir->i_ctime = CURRENT_TIME_SEC; src_dir->i_mtime = src_dir->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(src_dir);
/* remove old thread entry */ /* remove old thread entry */
hfsplus_cat_build_key(sb, src_fd.search_key, cnid, NULL); hfsplus_cat_build_key(sb, src_fd.search_key, cnid, NULL);
@ -387,6 +386,9 @@ int hfsplus_rename_cat(u32 cnid,
goto out; goto out;
} }
err = hfs_brec_insert(&dst_fd, &entry, entry_size); err = hfs_brec_insert(&dst_fd, &entry, entry_size);
hfsplus_mark_inode_dirty(dst_dir, HFSPLUS_I_CAT_DIRTY);
hfsplus_mark_inode_dirty(src_dir, HFSPLUS_I_CAT_DIRTY);
out: out:
hfs_bnode_put(dst_fd.bnode); hfs_bnode_put(dst_fd.bnode);
hfs_find_exit(&src_fd); hfs_find_exit(&src_fd);

View File

@ -108,6 +108,14 @@ static void __hfsplus_ext_write_extent(struct inode *inode, struct hfs_find_data
fd->entryoffset, fd->entrylength); fd->entryoffset, fd->entrylength);
hip->extent_state &= ~HFSPLUS_EXT_DIRTY; hip->extent_state &= ~HFSPLUS_EXT_DIRTY;
} }
/*
* We can't just use hfsplus_mark_inode_dirty here, because we
* also get called from hfsplus_write_inode, which should not
* redirty the inode. Instead the callers have to be careful
* to explicily mark the inode dirty, too.
*/
set_bit(HFSPLUS_I_EXT_DIRTY, &hip->flags);
} }
static void hfsplus_ext_write_extent_locked(struct inode *inode) static void hfsplus_ext_write_extent_locked(struct inode *inode)
@ -197,6 +205,7 @@ int hfsplus_get_block(struct inode *inode, sector_t iblock,
struct hfsplus_inode_info *hip = HFSPLUS_I(inode); struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
int res = -EIO; int res = -EIO;
u32 ablock, dblock, mask; u32 ablock, dblock, mask;
int was_dirty = 0;
int shift; int shift;
/* Convert inode block to disk allocation block */ /* Convert inode block to disk allocation block */
@ -223,14 +232,20 @@ int hfsplus_get_block(struct inode *inode, sector_t iblock,
return -EIO; return -EIO;
mutex_lock(&hip->extents_lock); mutex_lock(&hip->extents_lock);
/*
* hfsplus_ext_read_extent will write out a cached extent into
* the extents btree. In that case we may have to mark the inode
* dirty even for a pure read of an extent here.
*/
was_dirty = (hip->extent_state & HFSPLUS_EXT_DIRTY);
res = hfsplus_ext_read_extent(inode, ablock); res = hfsplus_ext_read_extent(inode, ablock);
if (!res) { if (res) {
dblock = hfsplus_ext_find_block(hip->cached_extents,
ablock - hip->cached_start);
} else {
mutex_unlock(&hip->extents_lock); mutex_unlock(&hip->extents_lock);
return -EIO; return -EIO;
} }
dblock = hfsplus_ext_find_block(hip->cached_extents,
ablock - hip->cached_start);
mutex_unlock(&hip->extents_lock); mutex_unlock(&hip->extents_lock);
done: done:
@ -242,8 +257,9 @@ done:
hip->phys_size += sb->s_blocksize; hip->phys_size += sb->s_blocksize;
hip->fs_blocks++; hip->fs_blocks++;
inode_add_bytes(inode, sb->s_blocksize); inode_add_bytes(inode, sb->s_blocksize);
mark_inode_dirty(inode);
} }
if (create || was_dirty)
mark_inode_dirty(inode);
return 0; return 0;
} }
@ -438,7 +454,7 @@ out:
mutex_unlock(&hip->extents_lock); mutex_unlock(&hip->extents_lock);
if (!res) { if (!res) {
hip->alloc_blocks += len; hip->alloc_blocks += len;
mark_inode_dirty(inode); hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY);
} }
return res; return res;
@ -529,5 +545,5 @@ out:
hip->phys_size = inode->i_size; hip->phys_size = inode->i_size;
hip->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; hip->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
inode_set_bytes(inode, hip->fs_blocks << sb->s_blocksize_bits); inode_set_bytes(inode, hip->fs_blocks << sb->s_blocksize_bits);
mark_inode_dirty(inode); hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY);
} }

View File

@ -157,6 +157,11 @@ struct hfsplus_sb_info {
#define HFSPLUS_SB_HFSX 3 #define HFSPLUS_SB_HFSX 3
#define HFSPLUS_SB_CASEFOLD 4 #define HFSPLUS_SB_CASEFOLD 4
static inline struct hfsplus_sb_info *HFSPLUS_SB(struct super_block *sb)
{
return sb->s_fs_info;
}
struct hfsplus_inode_info { struct hfsplus_inode_info {
atomic_t opencnt; atomic_t opencnt;
@ -205,10 +210,31 @@ struct hfsplus_inode_info {
#define HFSPLUS_EXT_NEW 0x0002 #define HFSPLUS_EXT_NEW 0x0002
#define HFSPLUS_I_RSRC 0 /* represents a resource fork */ #define HFSPLUS_I_RSRC 0 /* represents a resource fork */
#define HFSPLUS_I_CAT_DIRTY 1 /* has changes in the catalog tree */
#define HFSPLUS_I_EXT_DIRTY 2 /* has changes in the extent tree */
#define HFSPLUS_I_ALLOC_DIRTY 3 /* has changes in the allocation file */
#define HFSPLUS_IS_RSRC(inode) \ #define HFSPLUS_IS_RSRC(inode) \
test_bit(HFSPLUS_I_RSRC, &HFSPLUS_I(inode)->flags) test_bit(HFSPLUS_I_RSRC, &HFSPLUS_I(inode)->flags)
static inline struct hfsplus_inode_info *HFSPLUS_I(struct inode *inode)
{
return list_entry(inode, struct hfsplus_inode_info, vfs_inode);
}
/*
* Mark an inode dirty, and also mark the btree in which the
* specific type of metadata is stored.
* For data or metadata that gets written back by into the catalog btree
* by hfsplus_write_inode a plain mark_inode_dirty call is enough.
*/
static inline void hfsplus_mark_inode_dirty(struct inode *inode,
unsigned int flag)
{
set_bit(flag, &HFSPLUS_I(inode)->flags);
mark_inode_dirty(inode);
}
struct hfs_find_data { struct hfs_find_data {
/* filled by caller */ /* filled by caller */
hfsplus_btree_key *search_key; hfsplus_btree_key *search_key;
@ -397,17 +423,6 @@ int hfs_part_find(struct super_block *, sector_t *, sector_t *);
int hfsplus_submit_bio(struct block_device *bdev, sector_t sector, int hfsplus_submit_bio(struct block_device *bdev, sector_t sector,
void *data, int rw); void *data, int rw);
/* access macros */
static inline struct hfsplus_sb_info *HFSPLUS_SB(struct super_block *sb)
{
return sb->s_fs_info;
}
static inline struct hfsplus_inode_info *HFSPLUS_I(struct inode *inode)
{
return list_entry(inode, struct hfsplus_inode_info, vfs_inode);
}
/* time macros */ /* time macros */
#define __hfsp_mt2ut(t) (be32_to_cpu(t) - 2082844800U) #define __hfsp_mt2ut(t) (be32_to_cpu(t) - 2082844800U)
#define __hfsp_ut2mt(t) (cpu_to_be32(t + 2082844800U)) #define __hfsp_ut2mt(t) (cpu_to_be32(t + 2082844800U))

View File

@ -307,8 +307,9 @@ static int hfsplus_setattr(struct dentry *dentry, struct iattr *attr)
int hfsplus_file_fsync(struct file *file, int datasync) int hfsplus_file_fsync(struct file *file, int datasync)
{ {
struct inode *inode = file->f_mapping->host; struct inode *inode = file->f_mapping->host;
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb); struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
int error, error2; int error = 0, error2;
/* /*
* Sync inode metadata into the catalog and extent trees. * Sync inode metadata into the catalog and extent trees.
@ -318,13 +319,21 @@ int hfsplus_file_fsync(struct file *file, int datasync)
/* /*
* And explicitly write out the btrees. * And explicitly write out the btrees.
*/ */
if (test_and_clear_bit(HFSPLUS_I_CAT_DIRTY, &hip->flags))
error = filemap_write_and_wait(sbi->cat_tree->inode->i_mapping); error = filemap_write_and_wait(sbi->cat_tree->inode->i_mapping);
if (test_and_clear_bit(HFSPLUS_I_EXT_DIRTY, &hip->flags)) {
error2 = filemap_write_and_wait(sbi->ext_tree->inode->i_mapping); error2 = filemap_write_and_wait(sbi->ext_tree->inode->i_mapping);
if (!error) if (!error)
error = error2; error = error2;
}
if (test_and_clear_bit(HFSPLUS_I_ALLOC_DIRTY, &hip->flags)) {
error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping); error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping);
if (!error) if (!error)
error = error2; error = error2;
}
return error; return error;
} }
@ -590,6 +599,8 @@ int hfsplus_cat_write_inode(struct inode *inode)
hfs_bnode_write(fd.bnode, &entry, fd.entryoffset, hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
sizeof(struct hfsplus_cat_file)); sizeof(struct hfsplus_cat_file));
} }
set_bit(HFSPLUS_I_CAT_DIRTY, &HFSPLUS_I(inode)->flags);
out: out:
hfs_find_exit(&fd); hfs_find_exit(&fd);
return 0; return 0;

View File

@ -147,9 +147,11 @@ int hfsplus_setxattr(struct dentry *dentry, const char *name,
res = -ERANGE; res = -ERANGE;
} else } else
res = -EOPNOTSUPP; res = -EOPNOTSUPP;
if (!res) if (!res) {
hfs_bnode_write(fd.bnode, &entry, fd.entryoffset, hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
sizeof(struct hfsplus_cat_file)); sizeof(struct hfsplus_cat_file));
hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
}
out: out:
hfs_find_exit(&fd); hfs_find_exit(&fd);
return res; return res;

View File

@ -472,7 +472,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
&str, sbi->hidden_dir); &str, sbi->hidden_dir);
mutex_unlock(&sbi->vh_mutex); mutex_unlock(&sbi->vh_mutex);
mark_inode_dirty(sbi->hidden_dir); hfsplus_mark_inode_dirty(sbi->hidden_dir, HFSPLUS_I_CAT_DIRTY);
} }
out: out:
unload_nls(sbi->nls); unload_nls(sbi->nls);