aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFilipe Manana <fdmanana@suse.com>2016-05-04 21:02:27 -0400
committerFilipe Manana <fdmanana@suse.com>2016-05-12 20:59:26 -0400
commit86e8aa0e772caba5f0e0471d5f836b2b997dcb3e (patch)
tree6de82edb458fd5928ed52fb9b70b3bcc07f3128b
parentc990161888f387db136856337c237aa8d5003292 (diff)
Btrfs: unpin logs if rename exchange operation fails
If rename exchange operations fail at some point after we pinned any of the logs, we end up aborting the current transaction but never unpin the logs, which leaves concurrent tasks that are trying to sync the logs (as part of an fsync request from user space) blocked forever and preventing the filesystem from being unmountable. Fix this by safely unpinning the log. Signed-off-by: Filipe Manana <fdmanana@suse.com>
-rw-r--r--fs/btrfs/inode.c38
1 files changed, 36 insertions, 2 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 98c119b02916..c92d9b83bb38 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -9412,6 +9412,8 @@ static int btrfs_rename_exchange(struct inode *old_dir,
9412 u64 new_idx = 0; 9412 u64 new_idx = 0;
9413 u64 root_objectid; 9413 u64 root_objectid;
9414 int ret; 9414 int ret;
9415 bool root_log_pinned = false;
9416 bool dest_log_pinned = false;
9415 9417
9416 /* we only allow rename subvolume link between subvolumes */ 9418 /* we only allow rename subvolume link between subvolumes */
9417 if (old_ino != BTRFS_FIRST_FREE_OBJECTID && root != dest) 9419 if (old_ino != BTRFS_FIRST_FREE_OBJECTID && root != dest)
@@ -9464,6 +9466,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
9464 if (ret) 9466 if (ret)
9465 goto out_fail; 9467 goto out_fail;
9466 btrfs_pin_log_trans(root); 9468 btrfs_pin_log_trans(root);
9469 root_log_pinned = true;
9467 } 9470 }
9468 9471
9469 /* And now for the dest. */ 9472 /* And now for the dest. */
@@ -9479,6 +9482,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
9479 if (ret) 9482 if (ret)
9480 goto out_fail; 9483 goto out_fail;
9481 btrfs_pin_log_trans(dest); 9484 btrfs_pin_log_trans(dest);
9485 dest_log_pinned = true;
9482 } 9486 }
9483 9487
9484 /* Update inode version and ctime/mtime. */ 9488 /* Update inode version and ctime/mtime. */
@@ -9557,17 +9561,47 @@ static int btrfs_rename_exchange(struct inode *old_dir,
9557 if (new_inode->i_nlink == 1) 9561 if (new_inode->i_nlink == 1)
9558 BTRFS_I(new_inode)->dir_index = new_idx; 9562 BTRFS_I(new_inode)->dir_index = new_idx;
9559 9563
9560 if (old_ino != BTRFS_FIRST_FREE_OBJECTID) { 9564 if (root_log_pinned) {
9561 parent = new_dentry->d_parent; 9565 parent = new_dentry->d_parent;
9562 btrfs_log_new_name(trans, old_inode, old_dir, parent); 9566 btrfs_log_new_name(trans, old_inode, old_dir, parent);
9563 btrfs_end_log_trans(root); 9567 btrfs_end_log_trans(root);
9568 root_log_pinned = false;
9564 } 9569 }
9565 if (new_ino != BTRFS_FIRST_FREE_OBJECTID) { 9570 if (dest_log_pinned) {
9566 parent = old_dentry->d_parent; 9571 parent = old_dentry->d_parent;
9567 btrfs_log_new_name(trans, new_inode, new_dir, parent); 9572 btrfs_log_new_name(trans, new_inode, new_dir, parent);
9568 btrfs_end_log_trans(dest); 9573 btrfs_end_log_trans(dest);
9574 dest_log_pinned = false;
9569 } 9575 }
9570out_fail: 9576out_fail:
9577 /*
9578 * If we have pinned a log and an error happened, we unpin tasks
9579 * trying to sync the log and force them to fallback to a transaction
9580 * commit if the log currently contains any of the inodes involved in
9581 * this rename operation (to ensure we do not persist a log with an
9582 * inconsistent state for any of these inodes or leading to any
9583 * inconsistencies when replayed). If the transaction was aborted, the
9584 * abortion reason is propagated to userspace when attempting to commit
9585 * the transaction. If the log does not contain any of these inodes, we
9586 * allow the tasks to sync it.
9587 */
9588 if (ret && (root_log_pinned || dest_log_pinned)) {
9589 if (btrfs_inode_in_log(old_dir, root->fs_info->generation) ||
9590 btrfs_inode_in_log(new_dir, root->fs_info->generation) ||
9591 btrfs_inode_in_log(old_inode, root->fs_info->generation) ||
9592 (new_inode &&
9593 btrfs_inode_in_log(new_inode, root->fs_info->generation)))
9594 btrfs_set_log_full_commit(root->fs_info, trans);
9595
9596 if (root_log_pinned) {
9597 btrfs_end_log_trans(root);
9598 root_log_pinned = false;
9599 }
9600 if (dest_log_pinned) {
9601 btrfs_end_log_trans(dest);
9602 dest_log_pinned = false;
9603 }
9604 }
9571 ret = btrfs_end_transaction(trans, root); 9605 ret = btrfs_end_transaction(trans, root);
9572out_notrans: 9606out_notrans:
9573 if (new_ino == BTRFS_FIRST_FREE_OBJECTID) 9607 if (new_ino == BTRFS_FIRST_FREE_OBJECTID)