Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

btrfs: fix clone / extent-same deadlocks

Clone and extent same lock their source and target inodes in opposite order.
In addition to this, the range locking in clone doesn't take ordering into
account. Fix this by having clone use the same locking helpers as
btrfs-extent-same.

In addition, I do a small cleanup of the locking helpers, removing a case
(both inodes being the same) which was poorly accounted for and never
actually used by the callers.

Signed-off-by: Mark Fasheh <mfasheh@suse.de>
Reviewed-by: David Sterba <dsterba@suse.cz>
Signed-off-by: Chris Mason <clm@fb.com>

authored by

Mark Fasheh and committed by
Chris Mason
293a8489 4a3560c4

+8 -26
+8 -26
fs/btrfs/ioctl.c
··· 2852 2852 swap(inode1, inode2); 2853 2853 2854 2854 mutex_lock_nested(&inode1->i_mutex, I_MUTEX_PARENT); 2855 - if (inode1 != inode2) 2856 - mutex_lock_nested(&inode2->i_mutex, I_MUTEX_CHILD); 2855 + mutex_lock_nested(&inode2->i_mutex, I_MUTEX_CHILD); 2857 2856 } 2858 2857 2859 2858 static void btrfs_double_extent_unlock(struct inode *inode1, u64 loff1, ··· 2870 2871 swap(loff1, loff2); 2871 2872 } 2872 2873 lock_extent_range(inode1, loff1, len); 2873 - if (inode1 != inode2) 2874 - lock_extent_range(inode2, loff2, len); 2874 + lock_extent_range(inode2, loff2, len); 2875 2875 } 2876 2876 2877 2877 struct cmp_pages { ··· 3795 3797 goto out_fput; 3796 3798 3797 3799 if (!same_inode) { 3798 - if (inode < src) { 3799 - mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT); 3800 - mutex_lock_nested(&src->i_mutex, I_MUTEX_CHILD); 3801 - } else { 3802 - mutex_lock_nested(&src->i_mutex, I_MUTEX_PARENT); 3803 - mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD); 3804 - } 3800 + btrfs_double_inode_lock(src, inode); 3805 3801 } else { 3806 3802 mutex_lock(&src->i_mutex); 3807 3803 } ··· 3845 3853 3846 3854 lock_extent_range(src, lock_start, lock_len); 3847 3855 } else { 3848 - lock_extent_range(src, off, len); 3849 - lock_extent_range(inode, destoff, len); 3856 + btrfs_double_extent_lock(src, off, inode, destoff, len); 3850 3857 } 3851 3858 3852 3859 ret = btrfs_clone(src, inode, off, olen, len, destoff, 0); ··· 3856 3865 3857 3866 unlock_extent(&BTRFS_I(src)->io_tree, lock_start, lock_end); 3858 3867 } else { 3859 - unlock_extent(&BTRFS_I(src)->io_tree, off, off + len - 1); 3860 - unlock_extent(&BTRFS_I(inode)->io_tree, destoff, 3861 - destoff + len - 1); 3868 + btrfs_double_extent_unlock(src, off, inode, destoff, len); 3862 3869 } 3863 3870 /* 3864 3871 * Truncate page cache pages so that future reads will see the cloned ··· 3865 3876 truncate_inode_pages_range(&inode->i_data, destoff, 3866 3877 PAGE_CACHE_ALIGN(destoff + len) - 1); 3867 3878 out_unlock: 3868 - if (!same_inode) { 3869 - if (inode < src) { 3870 - mutex_unlock(&src->i_mutex); 3871 - mutex_unlock(&inode->i_mutex); 3872 - } else { 3873 - mutex_unlock(&inode->i_mutex); 3874 - mutex_unlock(&src->i_mutex); 3875 - } 3876 - } else { 3879 + if (!same_inode) 3880 + btrfs_double_inode_unlock(src, inode); 3881 + else 3877 3882 mutex_unlock(&src->i_mutex); 3878 - } 3879 3883 out_fput: 3880 3884 fdput(src_file); 3881 3885 out_drop_write: