diff options
author | Chris Mason <chris.mason@oracle.com> | 2009-10-13 13:21:08 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2009-10-13 13:35:12 -0400 |
commit | 257c62e1bce03e5b9f3f069fd52ad73a56de71fd (patch) | |
tree | ad047fe5796156aa88e3f3600111bf2b8d12759f | |
parent | 4722607db6a78bd7748c51fa4c8d7371da797254 (diff) |
Btrfs: avoid tree log commit when there are no changes
rpm has a habit of running fdatasync when the file hasn't
changed. We already detect if a file hasn't been changed
in the current transaction but it might have been sent to
the tree-log in this transaction and not changed since
the last call to fsync.
In this case, we want to avoid a tree log sync, which includes
a number of synchronous writes and barriers. This commit
extends the existing tracking of the last transaction to change
a file to also track the last sub-transaction.
The end result is that rpm -ivh and -Uvh are roughly twice as fast,
and on par with ext3.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
-rw-r--r-- | fs/btrfs/btrfs_inode.h | 6 | ||||
-rw-r--r-- | fs/btrfs/ctree.h | 1 | ||||
-rw-r--r-- | fs/btrfs/disk-io.c | 2 | ||||
-rw-r--r-- | fs/btrfs/file.c | 41 | ||||
-rw-r--r-- | fs/btrfs/inode.c | 6 | ||||
-rw-r--r-- | fs/btrfs/transaction.h | 1 | ||||
-rw-r--r-- | fs/btrfs/tree-log.c | 27 | ||||
-rw-r--r-- | fs/btrfs/tree-log.h | 3 |
8 files changed, 71 insertions, 16 deletions
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index c71abec0ab90..f6783a42f010 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h | |||
@@ -86,6 +86,12 @@ struct btrfs_inode { | |||
86 | * transid of the trans_handle that last modified this inode | 86 | * transid of the trans_handle that last modified this inode |
87 | */ | 87 | */ |
88 | u64 last_trans; | 88 | u64 last_trans; |
89 | |||
90 | /* | ||
91 | * log transid when this inode was last modified | ||
92 | */ | ||
93 | u64 last_sub_trans; | ||
94 | |||
89 | /* | 95 | /* |
90 | * transid that last logged this inode | 96 | * transid that last logged this inode |
91 | */ | 97 | */ |
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 36a19cd43e03..d0cede5ff25e 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
@@ -1009,6 +1009,7 @@ struct btrfs_root { | |||
1009 | atomic_t log_writers; | 1009 | atomic_t log_writers; |
1010 | atomic_t log_commit[2]; | 1010 | atomic_t log_commit[2]; |
1011 | unsigned long log_transid; | 1011 | unsigned long log_transid; |
1012 | unsigned long last_log_commit; | ||
1012 | unsigned long log_batch; | 1013 | unsigned long log_batch; |
1013 | pid_t log_start_pid; | 1014 | pid_t log_start_pid; |
1014 | bool log_multiple_pids; | 1015 | bool log_multiple_pids; |
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index ac8927bdc33d..d4132aad9ea1 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c | |||
@@ -919,6 +919,7 @@ static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, | |||
919 | atomic_set(&root->log_writers, 0); | 919 | atomic_set(&root->log_writers, 0); |
920 | root->log_batch = 0; | 920 | root->log_batch = 0; |
921 | root->log_transid = 0; | 921 | root->log_transid = 0; |
922 | root->last_log_commit = 0; | ||
922 | extent_io_tree_init(&root->dirty_log_pages, | 923 | extent_io_tree_init(&root->dirty_log_pages, |
923 | fs_info->btree_inode->i_mapping, GFP_NOFS); | 924 | fs_info->btree_inode->i_mapping, GFP_NOFS); |
924 | 925 | ||
@@ -1089,6 +1090,7 @@ int btrfs_add_log_tree(struct btrfs_trans_handle *trans, | |||
1089 | WARN_ON(root->log_root); | 1090 | WARN_ON(root->log_root); |
1090 | root->log_root = log_root; | 1091 | root->log_root = log_root; |
1091 | root->log_transid = 0; | 1092 | root->log_transid = 0; |
1093 | root->last_log_commit = 0; | ||
1092 | return 0; | 1094 | return 0; |
1093 | } | 1095 | } |
1094 | 1096 | ||
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 53fb1c997f0e..4599113ed72e 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c | |||
@@ -1087,8 +1087,10 @@ out_nolock: | |||
1087 | btrfs_end_transaction(trans, root); | 1087 | btrfs_end_transaction(trans, root); |
1088 | else | 1088 | else |
1089 | btrfs_commit_transaction(trans, root); | 1089 | btrfs_commit_transaction(trans, root); |
1090 | } else { | 1090 | } else if (ret != BTRFS_NO_LOG_SYNC) { |
1091 | btrfs_commit_transaction(trans, root); | 1091 | btrfs_commit_transaction(trans, root); |
1092 | } else { | ||
1093 | btrfs_end_transaction(trans, root); | ||
1092 | } | 1094 | } |
1093 | } | 1095 | } |
1094 | if (file->f_flags & O_DIRECT) { | 1096 | if (file->f_flags & O_DIRECT) { |
@@ -1138,6 +1140,13 @@ int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync) | |||
1138 | int ret = 0; | 1140 | int ret = 0; |
1139 | struct btrfs_trans_handle *trans; | 1141 | struct btrfs_trans_handle *trans; |
1140 | 1142 | ||
1143 | |||
1144 | /* we wait first, since the writeback may change the inode */ | ||
1145 | root->log_batch++; | ||
1146 | /* the VFS called filemap_fdatawrite for us */ | ||
1147 | btrfs_wait_ordered_range(inode, 0, (u64)-1); | ||
1148 | root->log_batch++; | ||
1149 | |||
1141 | /* | 1150 | /* |
1142 | * check the transaction that last modified this inode | 1151 | * check the transaction that last modified this inode |
1143 | * and see if its already been committed | 1152 | * and see if its already been committed |
@@ -1145,6 +1154,11 @@ int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync) | |||
1145 | if (!BTRFS_I(inode)->last_trans) | 1154 | if (!BTRFS_I(inode)->last_trans) |
1146 | goto out; | 1155 | goto out; |
1147 | 1156 | ||
1157 | /* | ||
1158 | * if the last transaction that changed this file was before | ||
1159 | * the current transaction, we can bail out now without any | ||
1160 | * syncing | ||
1161 | */ | ||
1148 | mutex_lock(&root->fs_info->trans_mutex); | 1162 | mutex_lock(&root->fs_info->trans_mutex); |
1149 | if (BTRFS_I(inode)->last_trans <= | 1163 | if (BTRFS_I(inode)->last_trans <= |
1150 | root->fs_info->last_trans_committed) { | 1164 | root->fs_info->last_trans_committed) { |
@@ -1154,13 +1168,6 @@ int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync) | |||
1154 | } | 1168 | } |
1155 | mutex_unlock(&root->fs_info->trans_mutex); | 1169 | mutex_unlock(&root->fs_info->trans_mutex); |
1156 | 1170 | ||
1157 | root->log_batch++; | ||
1158 | filemap_fdatawrite(inode->i_mapping); | ||
1159 | btrfs_wait_ordered_range(inode, 0, (u64)-1); | ||
1160 | root->log_batch++; | ||
1161 | |||
1162 | if (datasync && !(inode->i_state & I_DIRTY_PAGES)) | ||
1163 | goto out; | ||
1164 | /* | 1171 | /* |
1165 | * ok we haven't committed the transaction yet, lets do a commit | 1172 | * ok we haven't committed the transaction yet, lets do a commit |
1166 | */ | 1173 | */ |
@@ -1189,14 +1196,18 @@ int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync) | |||
1189 | */ | 1196 | */ |
1190 | mutex_unlock(&dentry->d_inode->i_mutex); | 1197 | mutex_unlock(&dentry->d_inode->i_mutex); |
1191 | 1198 | ||
1192 | if (ret > 0) { | 1199 | if (ret != BTRFS_NO_LOG_SYNC) { |
1193 | ret = btrfs_commit_transaction(trans, root); | 1200 | if (ret > 0) { |
1194 | } else { | ||
1195 | ret = btrfs_sync_log(trans, root); | ||
1196 | if (ret == 0) | ||
1197 | ret = btrfs_end_transaction(trans, root); | ||
1198 | else | ||
1199 | ret = btrfs_commit_transaction(trans, root); | 1201 | ret = btrfs_commit_transaction(trans, root); |
1202 | } else { | ||
1203 | ret = btrfs_sync_log(trans, root); | ||
1204 | if (ret == 0) | ||
1205 | ret = btrfs_end_transaction(trans, root); | ||
1206 | else | ||
1207 | ret = btrfs_commit_transaction(trans, root); | ||
1208 | } | ||
1209 | } else { | ||
1210 | ret = btrfs_end_transaction(trans, root); | ||
1200 | } | 1211 | } |
1201 | mutex_lock(&dentry->d_inode->i_mutex); | 1212 | mutex_lock(&dentry->d_inode->i_mutex); |
1202 | out: | 1213 | out: |
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index ef399a7794ff..5b9567caba0a 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -3480,6 +3480,7 @@ static noinline void init_btrfs_i(struct inode *inode) | |||
3480 | bi->generation = 0; | 3480 | bi->generation = 0; |
3481 | bi->sequence = 0; | 3481 | bi->sequence = 0; |
3482 | bi->last_trans = 0; | 3482 | bi->last_trans = 0; |
3483 | bi->last_sub_trans = 0; | ||
3483 | bi->logged_trans = 0; | 3484 | bi->logged_trans = 0; |
3484 | bi->delalloc_bytes = 0; | 3485 | bi->delalloc_bytes = 0; |
3485 | bi->reserved_bytes = 0; | 3486 | bi->reserved_bytes = 0; |
@@ -4980,7 +4981,9 @@ again: | |||
4980 | set_page_dirty(page); | 4981 | set_page_dirty(page); |
4981 | SetPageUptodate(page); | 4982 | SetPageUptodate(page); |
4982 | 4983 | ||
4983 | BTRFS_I(inode)->last_trans = root->fs_info->generation + 1; | 4984 | BTRFS_I(inode)->last_trans = root->fs_info->generation; |
4985 | BTRFS_I(inode)->last_sub_trans = BTRFS_I(inode)->root->log_transid; | ||
4986 | |||
4984 | unlock_extent(io_tree, page_start, page_end, GFP_NOFS); | 4987 | unlock_extent(io_tree, page_start, page_end, GFP_NOFS); |
4985 | 4988 | ||
4986 | out_unlock: | 4989 | out_unlock: |
@@ -5100,6 +5103,7 @@ struct inode *btrfs_alloc_inode(struct super_block *sb) | |||
5100 | if (!ei) | 5103 | if (!ei) |
5101 | return NULL; | 5104 | return NULL; |
5102 | ei->last_trans = 0; | 5105 | ei->last_trans = 0; |
5106 | ei->last_sub_trans = 0; | ||
5103 | ei->logged_trans = 0; | 5107 | ei->logged_trans = 0; |
5104 | ei->outstanding_extents = 0; | 5108 | ei->outstanding_extents = 0; |
5105 | ei->reserved_extents = 0; | 5109 | ei->reserved_extents = 0; |
diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h index 663c67404918..f68cbbe61e56 100644 --- a/fs/btrfs/transaction.h +++ b/fs/btrfs/transaction.h | |||
@@ -79,6 +79,7 @@ static inline void btrfs_set_inode_last_trans(struct btrfs_trans_handle *trans, | |||
79 | struct inode *inode) | 79 | struct inode *inode) |
80 | { | 80 | { |
81 | BTRFS_I(inode)->last_trans = trans->transaction->transid; | 81 | BTRFS_I(inode)->last_trans = trans->transaction->transid; |
82 | BTRFS_I(inode)->last_sub_trans = BTRFS_I(inode)->root->log_transid; | ||
82 | } | 83 | } |
83 | 84 | ||
84 | int btrfs_end_transaction(struct btrfs_trans_handle *trans, | 85 | int btrfs_end_transaction(struct btrfs_trans_handle *trans, |
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 6d9ec285644d..0a1bde268963 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c | |||
@@ -1980,6 +1980,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, | |||
1980 | int ret; | 1980 | int ret; |
1981 | struct btrfs_root *log = root->log_root; | 1981 | struct btrfs_root *log = root->log_root; |
1982 | struct btrfs_root *log_root_tree = root->fs_info->log_root_tree; | 1982 | struct btrfs_root *log_root_tree = root->fs_info->log_root_tree; |
1983 | u64 log_transid = 0; | ||
1983 | 1984 | ||
1984 | mutex_lock(&root->log_mutex); | 1985 | mutex_lock(&root->log_mutex); |
1985 | index1 = root->log_transid % 2; | 1986 | index1 = root->log_transid % 2; |
@@ -2018,6 +2019,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, | |||
2018 | btrfs_set_root_node(&log->root_item, log->node); | 2019 | btrfs_set_root_node(&log->root_item, log->node); |
2019 | 2020 | ||
2020 | root->log_batch = 0; | 2021 | root->log_batch = 0; |
2022 | log_transid = root->log_transid; | ||
2021 | root->log_transid++; | 2023 | root->log_transid++; |
2022 | log->log_transid = root->log_transid; | 2024 | log->log_transid = root->log_transid; |
2023 | root->log_start_pid = 0; | 2025 | root->log_start_pid = 0; |
@@ -2095,6 +2097,11 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, | |||
2095 | write_ctree_super(trans, root->fs_info->tree_root, 1); | 2097 | write_ctree_super(trans, root->fs_info->tree_root, 1); |
2096 | ret = 0; | 2098 | ret = 0; |
2097 | 2099 | ||
2100 | mutex_lock(&root->log_mutex); | ||
2101 | if (root->last_log_commit < log_transid) | ||
2102 | root->last_log_commit = log_transid; | ||
2103 | mutex_unlock(&root->log_mutex); | ||
2104 | |||
2098 | out_wake_log_root: | 2105 | out_wake_log_root: |
2099 | atomic_set(&log_root_tree->log_commit[index2], 0); | 2106 | atomic_set(&log_root_tree->log_commit[index2], 0); |
2100 | smp_mb(); | 2107 | smp_mb(); |
@@ -2862,6 +2869,21 @@ out: | |||
2862 | return ret; | 2869 | return ret; |
2863 | } | 2870 | } |
2864 | 2871 | ||
2872 | static int inode_in_log(struct btrfs_trans_handle *trans, | ||
2873 | struct inode *inode) | ||
2874 | { | ||
2875 | struct btrfs_root *root = BTRFS_I(inode)->root; | ||
2876 | int ret = 0; | ||
2877 | |||
2878 | mutex_lock(&root->log_mutex); | ||
2879 | if (BTRFS_I(inode)->logged_trans == trans->transid && | ||
2880 | BTRFS_I(inode)->last_sub_trans <= root->last_log_commit) | ||
2881 | ret = 1; | ||
2882 | mutex_unlock(&root->log_mutex); | ||
2883 | return ret; | ||
2884 | } | ||
2885 | |||
2886 | |||
2865 | /* | 2887 | /* |
2866 | * helper function around btrfs_log_inode to make sure newly created | 2888 | * helper function around btrfs_log_inode to make sure newly created |
2867 | * parent directories also end up in the log. A minimal inode and backref | 2889 | * parent directories also end up in the log. A minimal inode and backref |
@@ -2901,6 +2923,11 @@ int btrfs_log_inode_parent(struct btrfs_trans_handle *trans, | |||
2901 | if (ret) | 2923 | if (ret) |
2902 | goto end_no_trans; | 2924 | goto end_no_trans; |
2903 | 2925 | ||
2926 | if (inode_in_log(trans, inode)) { | ||
2927 | ret = BTRFS_NO_LOG_SYNC; | ||
2928 | goto end_no_trans; | ||
2929 | } | ||
2930 | |||
2904 | start_log_trans(trans, root); | 2931 | start_log_trans(trans, root); |
2905 | 2932 | ||
2906 | ret = btrfs_log_inode(trans, root, inode, inode_only); | 2933 | ret = btrfs_log_inode(trans, root, inode, inode_only); |
diff --git a/fs/btrfs/tree-log.h b/fs/btrfs/tree-log.h index d09c7609e16b..0776eacb5083 100644 --- a/fs/btrfs/tree-log.h +++ b/fs/btrfs/tree-log.h | |||
@@ -19,6 +19,9 @@ | |||
19 | #ifndef __TREE_LOG_ | 19 | #ifndef __TREE_LOG_ |
20 | #define __TREE_LOG_ | 20 | #define __TREE_LOG_ |
21 | 21 | ||
22 | /* return value for btrfs_log_dentry_safe that means we don't need to log it at all */ | ||
23 | #define BTRFS_NO_LOG_SYNC 256 | ||
24 | |||
22 | int btrfs_sync_log(struct btrfs_trans_handle *trans, | 25 | int btrfs_sync_log(struct btrfs_trans_handle *trans, |
23 | struct btrfs_root *root); | 26 | struct btrfs_root *root); |
24 | int btrfs_free_log(struct btrfs_trans_handle *trans, struct btrfs_root *root); | 27 | int btrfs_free_log(struct btrfs_trans_handle *trans, struct btrfs_root *root); |