summaryrefslogtreecommitdiffstats
path: root/fs/btrfs/tree-log.c
diff options
context:
space:
mode:
authorFilipe Manana <fdmanana@suse.com>2018-06-11 14:24:28 -0400
committerDavid Sterba <dsterba@suse.com>2018-08-23 11:37:26 -0400
commitd4682ba03ef618b6ef4be7cedc7aacaf505d3a58 (patch)
tree5700c2336b2b53118c4869c447f084023ab8555e /fs/btrfs/tree-log.c
parent8ecebf4d767e2307a946c8905278d6358eda35c3 (diff)
Btrfs: sync log after logging new name
When we add a new name for an inode which was logged in the current transaction, we update the inode in the log so that its new name and ancestors are added to the log. However when we do this we do not persist the log, so the changes remain in memory only, and as a consequence, any ancestors that were created in the current transaction are updated such that future calls to btrfs_inode_in_log() return true. This leads to a subsequent fsync against such new ancestor directories returning immediately, without persisting the log, therefore after a power failure the new ancestor directories do not exist, despite fsync being called against them explicitly. Example: $ mkfs.btrfs -f /dev/sdb $ mount /dev/sdb /mnt $ mkdir /mnt/A $ mkdir /mnt/B $ mkdir /mnt/A/C $ touch /mnt/B/foo $ xfs_io -c "fsync" /mnt/B/foo $ ln /mnt/B/foo /mnt/A/C/foo $ xfs_io -c "fsync" /mnt/A <power failure> After the power failure, directory "A" does not exist, despite the explicit fsync on it. Instead of fixing this by changing the behaviour of the explicit fsync on directory "A" to persist the log instead of doing nothing, make the logging of the new file name (which happens when creating a hard link or renaming) persist the log. This approach not only is simpler, not requiring addition of new fields to the inode in memory structure, but also gives us the same behaviour as ext4, xfs and f2fs (possibly other filesystems too). A test case for fstests follows soon. Fixes: 12fcfd22fe5b ("Btrfs: tree logging unlink/rename fixes") Reported-by: Vijay Chidambaram <vvijay03@gmail.com> Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
Diffstat (limited to 'fs/btrfs/tree-log.c')
-rw-r--r--fs/btrfs/tree-log.c48
1 files changed, 42 insertions, 6 deletions
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 1650dc44a5e3..3c2ae0e4f25a 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -6025,14 +6025,25 @@ void btrfs_record_snapshot_destroy(struct btrfs_trans_handle *trans,
6025 * Call this after adding a new name for a file and it will properly 6025 * Call this after adding a new name for a file and it will properly
6026 * update the log to reflect the new name. 6026 * update the log to reflect the new name.
6027 * 6027 *
6028 * It will return zero if all goes well, and it will return 1 if a 6028 * @ctx can not be NULL when @sync_log is false, and should be NULL when it's
6029 * full transaction commit is required. 6029 * true (because it's not used).
6030 *
6031 * Return value depends on whether @sync_log is true or false.
6032 * When true: returns BTRFS_NEED_TRANS_COMMIT if the transaction needs to be
6033 * committed by the caller, and BTRFS_DONT_NEED_TRANS_COMMIT
6034 * otherwise.
6035 * When false: returns BTRFS_DONT_NEED_LOG_SYNC if the caller does not need to
6036 * to sync the log, BTRFS_NEED_LOG_SYNC if it needs to sync the log,
6037 * or BTRFS_NEED_TRANS_COMMIT if the transaction needs to be
6038 * committed (without attempting to sync the log).
6030 */ 6039 */
6031int btrfs_log_new_name(struct btrfs_trans_handle *trans, 6040int btrfs_log_new_name(struct btrfs_trans_handle *trans,
6032 struct btrfs_inode *inode, struct btrfs_inode *old_dir, 6041 struct btrfs_inode *inode, struct btrfs_inode *old_dir,
6033 struct dentry *parent) 6042 struct dentry *parent,
6043 bool sync_log, struct btrfs_log_ctx *ctx)
6034{ 6044{
6035 struct btrfs_fs_info *fs_info = trans->fs_info; 6045 struct btrfs_fs_info *fs_info = trans->fs_info;
6046 int ret;
6036 6047
6037 /* 6048 /*
6038 * this will force the logging code to walk the dentry chain 6049 * this will force the logging code to walk the dentry chain
@@ -6047,9 +6058,34 @@ int btrfs_log_new_name(struct btrfs_trans_handle *trans,
6047 */ 6058 */
6048 if (inode->logged_trans <= fs_info->last_trans_committed && 6059 if (inode->logged_trans <= fs_info->last_trans_committed &&
6049 (!old_dir || old_dir->logged_trans <= fs_info->last_trans_committed)) 6060 (!old_dir || old_dir->logged_trans <= fs_info->last_trans_committed))
6050 return 0; 6061 return sync_log ? BTRFS_DONT_NEED_TRANS_COMMIT :
6062 BTRFS_DONT_NEED_LOG_SYNC;
6063
6064 if (sync_log) {
6065 struct btrfs_log_ctx ctx2;
6066
6067 btrfs_init_log_ctx(&ctx2, &inode->vfs_inode);
6068 ret = btrfs_log_inode_parent(trans, inode, parent, 0, LLONG_MAX,
6069 LOG_INODE_EXISTS, &ctx2);
6070 if (ret == BTRFS_NO_LOG_SYNC)
6071 return BTRFS_DONT_NEED_TRANS_COMMIT;
6072 else if (ret)
6073 return BTRFS_NEED_TRANS_COMMIT;
6074
6075 ret = btrfs_sync_log(trans, inode->root, &ctx2);
6076 if (ret)
6077 return BTRFS_NEED_TRANS_COMMIT;
6078 return BTRFS_DONT_NEED_TRANS_COMMIT;
6079 }
6080
6081 ASSERT(ctx);
6082 ret = btrfs_log_inode_parent(trans, inode, parent, 0, LLONG_MAX,
6083 LOG_INODE_EXISTS, ctx);
6084 if (ret == BTRFS_NO_LOG_SYNC)
6085 return BTRFS_DONT_NEED_LOG_SYNC;
6086 else if (ret)
6087 return BTRFS_NEED_TRANS_COMMIT;
6051 6088
6052 return btrfs_log_inode_parent(trans, inode, parent, 0, LLONG_MAX, 6089 return BTRFS_NEED_LOG_SYNC;
6053 LOG_INODE_EXISTS, NULL);
6054} 6090}
6055 6091