mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 19:00:25 +00:00
Btrfs: optimize fsyncs on old files
The fsync log has code to make sure all of the parents of a file are in the log along with the file. It uses a minimal log of the parent directory inodes, just enough to get the parent directory on disk. If the transaction that originally created a file is fully on disk, and the file hasn't been renamed or linked into other directories, we can safely skip the parent directory walk. We know the file is on disk somewhere and we can go ahead and just log that single file. This is more important now because unrelated unlinks in the parent directory might make us force a commit if we try to log the parent. Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
parent
12fcfd22fe
commit
af4176b49c
1 changed files with 44 additions and 1 deletions
|
@ -2835,6 +2835,17 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans,
|
|||
int ret = 0;
|
||||
struct btrfs_root *root;
|
||||
|
||||
/*
|
||||
* for regular files, if its inode is already on disk, we don't
|
||||
* have to worry about the parents at all. This is because
|
||||
* we can use the last_unlink_trans field to record renames
|
||||
* and other fun in this file.
|
||||
*/
|
||||
if (S_ISREG(inode->i_mode) &&
|
||||
BTRFS_I(inode)->generation <= last_committed &&
|
||||
BTRFS_I(inode)->last_unlink_trans <= last_committed)
|
||||
goto out;
|
||||
|
||||
if (!S_ISDIR(inode->i_mode)) {
|
||||
if (!parent || !parent->d_inode || sb != parent->d_inode->i_sb)
|
||||
goto out;
|
||||
|
@ -2904,8 +2915,19 @@ int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
|
|||
|
||||
ret = btrfs_log_inode(trans, root, inode, inode_only);
|
||||
BUG_ON(ret);
|
||||
inode_only = LOG_INODE_EXISTS;
|
||||
|
||||
/*
|
||||
* for regular files, if its inode is already on disk, we don't
|
||||
* have to worry about the parents at all. This is because
|
||||
* we can use the last_unlink_trans field to record renames
|
||||
* and other fun in this file.
|
||||
*/
|
||||
if (S_ISREG(inode->i_mode) &&
|
||||
BTRFS_I(inode)->generation <= last_committed &&
|
||||
BTRFS_I(inode)->last_unlink_trans <= last_committed)
|
||||
goto no_parent;
|
||||
|
||||
inode_only = LOG_INODE_EXISTS;
|
||||
while (1) {
|
||||
if (!parent || !parent->d_inode || sb != parent->d_inode->i_sb)
|
||||
break;
|
||||
|
@ -2921,6 +2943,7 @@ int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
|
|||
|
||||
parent = parent->d_parent;
|
||||
}
|
||||
no_parent:
|
||||
ret = 0;
|
||||
btrfs_end_log_trans(root);
|
||||
end_no_trans:
|
||||
|
@ -3069,6 +3092,19 @@ void btrfs_record_unlink_dir(struct btrfs_trans_handle *trans,
|
|||
struct inode *dir, struct inode *inode,
|
||||
int for_rename)
|
||||
{
|
||||
/*
|
||||
* when we're logging a file, if it hasn't been renamed
|
||||
* or unlinked, and its inode is fully committed on disk,
|
||||
* we don't have to worry about walking up the directory chain
|
||||
* to log its parents.
|
||||
*
|
||||
* So, we use the last_unlink_trans field to put this transid
|
||||
* into the file. When the file is logged we check it and
|
||||
* don't log the parents if the file is fully on disk.
|
||||
*/
|
||||
if (S_ISREG(inode->i_mode))
|
||||
BTRFS_I(inode)->last_unlink_trans = trans->transid;
|
||||
|
||||
/*
|
||||
* if this directory was already logged any new
|
||||
* names for this file/dir will get recorded
|
||||
|
@ -3114,6 +3150,13 @@ int btrfs_log_new_name(struct btrfs_trans_handle *trans,
|
|||
{
|
||||
struct btrfs_root * root = BTRFS_I(inode)->root;
|
||||
|
||||
/*
|
||||
* this will force the logging code to walk the dentry chain
|
||||
* up for the file
|
||||
*/
|
||||
if (S_ISREG(inode->i_mode))
|
||||
BTRFS_I(inode)->last_unlink_trans = trans->transid;
|
||||
|
||||
/*
|
||||
* if this inode hasn't been logged and directory we're renaming it
|
||||
* from hasn't been logged, we don't need to log it
|
||||
|
|
Loading…
Reference in a new issue