Skip to content

Commit 3dc9e8f

Browse files
committed
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 <[email protected]>
1 parent 9cfa3e3 commit 3dc9e8f

File tree

1 file changed

+27
-1
lines changed

1 file changed

+27
-1
lines changed

fs/btrfs/inode.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9406,6 +9406,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
94069406
u64 root_objectid;
94079407
int ret;
94089408
u64 old_ino = btrfs_ino(old_inode);
9409+
bool log_pinned = false;
94099410

94109411
if (btrfs_ino(new_dir) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)
94119412
return -EPERM;
@@ -9493,6 +9494,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
94939494
* we unlink the name but before we add the new name back in.
94949495
*/
94959496
btrfs_pin_log_trans(root);
9497+
log_pinned = true;
94969498
}
94979499

94989500
inode_inc_iversion(old_dir);
@@ -9559,12 +9561,36 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
95599561
if (old_inode->i_nlink == 1)
95609562
BTRFS_I(old_inode)->dir_index = index;
95619563

9562-
if (old_ino != BTRFS_FIRST_FREE_OBJECTID) {
9564+
if (log_pinned) {
95639565
struct dentry *parent = new_dentry->d_parent;
9566+
95649567
btrfs_log_new_name(trans, old_inode, old_dir, parent);
95659568
btrfs_end_log_trans(root);
9569+
log_pinned = false;
95669570
}
95679571
out_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+
}
95689594
btrfs_end_transaction(trans, root);
95699595
out_notrans:
95709596
if (old_ino == BTRFS_FIRST_FREE_OBJECTID)

0 commit comments

Comments
 (0)