aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFilipe Manana <fdmanana@suse.com>2016-04-29 06:34:22 -0400
committerFilipe Manana <fdmanana@suse.com>2016-05-12 20:59:18 -0400
commit3dc9e8f76720fbbd9c56a11775932733fe13d214 (patch)
treeda778eb649c5ba5fada99372055777fd62ae09f7
parent9cfa3e34e20e6798a671236000d9e97c8aa5d318 (diff)
Btrfs: unpin log if rename operation fails
If rename operations fail at some point after we pinned the log, we end up aborting the current transaction but never unpin the log, which leaves concurrent tasks that are trying to sync the log (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.c28
1 files changed, 27 insertions, 1 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 3991d8a74f61..4594a263a5f8 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -9406,6 +9406,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
9406 u64 root_objectid; 9406 u64 root_objectid;
9407 int ret; 9407 int ret;
9408 u64 old_ino = btrfs_ino(old_inode); 9408 u64 old_ino = btrfs_ino(old_inode);
9409 bool log_pinned = false;
9409 9410
9410 if (btrfs_ino(new_dir) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID) 9411 if (btrfs_ino(new_dir) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)
9411 return -EPERM; 9412 return -EPERM;
@@ -9493,6 +9494,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
9493 * we unlink the name but before we add the new name back in. 9494 * we unlink the name but before we add the new name back in.
9494 */ 9495 */
9495 btrfs_pin_log_trans(root); 9496 btrfs_pin_log_trans(root);
9497 log_pinned = true;
9496 } 9498 }
9497 9499
9498 inode_inc_iversion(old_dir); 9500 inode_inc_iversion(old_dir);
@@ -9559,12 +9561,36 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
9559 if (old_inode->i_nlink == 1) 9561 if (old_inode->i_nlink == 1)
9560 BTRFS_I(old_inode)->dir_index = index; 9562 BTRFS_I(old_inode)->dir_index = index;
9561 9563
9562 if (old_ino != BTRFS_FIRST_FREE_OBJECTID) { 9564 if (log_pinned) {
9563 struct dentry *parent = new_dentry->d_parent; 9565 struct dentry *parent = new_dentry->d_parent;
9566
9564 btrfs_log_new_name(trans, old_inode, old_dir, parent); 9567 btrfs_log_new_name(trans, old_inode, old_dir, parent);
9565 btrfs_end_log_trans(root); 9568 btrfs_end_log_trans(root);
9569 log_pinned = false;
9566 } 9570 }
9567out_fail: 9571out_fail:
9572 /*
9573 * If we have pinned the log and an error happened, we unpin tasks
9574 * trying to sync the log and force them to fallback to a transaction
9575 * commit if the log currently contains any of the inodes involved in
9576 * this rename operation (to ensure we do not persist a log with an
9577 * inconsistent state for any of these inodes or leading to any
9578 * inconsistencies when replayed). If the transaction was aborted, the
9579 * abortion reason is propagated to userspace when attempting to commit
9580 * the transaction. If the log does not contain any of these inodes, we
9581 * allow the tasks to sync it.
9582 */
9583 if (ret && log_pinned) {
9584 if (btrfs_inode_in_log(old_dir, root->fs_info->generation) ||
9585 btrfs_inode_in_log(new_dir, root->fs_info->generation) ||
9586 btrfs_inode_in_log(old_inode, root->fs_info->generation) ||
9587 (new_inode &&
9588 btrfs_inode_in_log(new_inode, root->fs_info->generation)))
9589 btrfs_set_log_full_commit(root->fs_info, trans);
9590
9591 btrfs_end_log_trans(root);
9592 log_pinned = false;
9593 }
9568 btrfs_end_transaction(trans, root); 9594 btrfs_end_transaction(trans, root);
9569out_notrans: 9595out_notrans:
9570 if (old_ino == BTRFS_FIRST_FREE_OBJECTID) 9596 if (old_ino == BTRFS_FIRST_FREE_OBJECTID)