|
@@ -9412,6 +9412,8 @@ static int btrfs_rename_exchange(struct inode *old_dir,
|
|
|
u64 new_idx = 0;
|
|
|
u64 root_objectid;
|
|
|
int ret;
|
|
|
+ bool root_log_pinned = false;
|
|
|
+ bool dest_log_pinned = false;
|
|
|
|
|
|
/* we only allow rename subvolume link between subvolumes */
|
|
|
if (old_ino != BTRFS_FIRST_FREE_OBJECTID && root != dest)
|
|
@@ -9464,6 +9466,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
|
|
|
if (ret)
|
|
|
goto out_fail;
|
|
|
btrfs_pin_log_trans(root);
|
|
|
+ root_log_pinned = true;
|
|
|
}
|
|
|
|
|
|
/* And now for the dest. */
|
|
@@ -9479,6 +9482,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
|
|
|
if (ret)
|
|
|
goto out_fail;
|
|
|
btrfs_pin_log_trans(dest);
|
|
|
+ dest_log_pinned = true;
|
|
|
}
|
|
|
|
|
|
/* Update inode version and ctime/mtime. */
|
|
@@ -9557,17 +9561,47 @@ static int btrfs_rename_exchange(struct inode *old_dir,
|
|
|
if (new_inode->i_nlink == 1)
|
|
|
BTRFS_I(new_inode)->dir_index = new_idx;
|
|
|
|
|
|
- if (old_ino != BTRFS_FIRST_FREE_OBJECTID) {
|
|
|
+ if (root_log_pinned) {
|
|
|
parent = new_dentry->d_parent;
|
|
|
btrfs_log_new_name(trans, old_inode, old_dir, parent);
|
|
|
btrfs_end_log_trans(root);
|
|
|
+ root_log_pinned = false;
|
|
|
}
|
|
|
- if (new_ino != BTRFS_FIRST_FREE_OBJECTID) {
|
|
|
+ if (dest_log_pinned) {
|
|
|
parent = old_dentry->d_parent;
|
|
|
btrfs_log_new_name(trans, new_inode, new_dir, parent);
|
|
|
btrfs_end_log_trans(dest);
|
|
|
+ dest_log_pinned = false;
|
|
|
}
|
|
|
out_fail:
|
|
|
+ /*
|
|
|
+ * If we have pinned a log and an error happened, we unpin tasks
|
|
|
+ * trying to sync the log and force them to fallback to a transaction
|
|
|
+ * commit if the log currently contains any of the inodes involved in
|
|
|
+ * this rename operation (to ensure we do not persist a log with an
|
|
|
+ * inconsistent state for any of these inodes or leading to any
|
|
|
+ * inconsistencies when replayed). If the transaction was aborted, the
|
|
|
+ * abortion reason is propagated to userspace when attempting to commit
|
|
|
+ * the transaction. If the log does not contain any of these inodes, we
|
|
|
+ * allow the tasks to sync it.
|
|
|
+ */
|
|
|
+ if (ret && (root_log_pinned || dest_log_pinned)) {
|
|
|
+ if (btrfs_inode_in_log(old_dir, root->fs_info->generation) ||
|
|
|
+ btrfs_inode_in_log(new_dir, root->fs_info->generation) ||
|
|
|
+ btrfs_inode_in_log(old_inode, root->fs_info->generation) ||
|
|
|
+ (new_inode &&
|
|
|
+ btrfs_inode_in_log(new_inode, root->fs_info->generation)))
|
|
|
+ btrfs_set_log_full_commit(root->fs_info, trans);
|
|
|
+
|
|
|
+ if (root_log_pinned) {
|
|
|
+ btrfs_end_log_trans(root);
|
|
|
+ root_log_pinned = false;
|
|
|
+ }
|
|
|
+ if (dest_log_pinned) {
|
|
|
+ btrfs_end_log_trans(dest);
|
|
|
+ dest_log_pinned = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
ret = btrfs_end_transaction(trans, root);
|
|
|
out_notrans:
|
|
|
if (new_ino == BTRFS_FIRST_FREE_OBJECTID)
|