From 4d6350b3b0579ac019f3f8444f59a950e9f301c8 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 17 Feb 2022 14:48:13 -0800 Subject: [PATCH] Fix lock ordering in fallocate We were seeing ABBA deadlocks on the dio_count wait and extent_sem between fallocate and reads. It turns out that fallocate got lock ordering wrong. This brings fallocate in line with the rest of the adherents to the lock heirarchy. Most importantly, the extent_sem is used after the dio_count. While we're at it we bring the i_mutex down to just before the cluster lock for consistency. Signed-off-by: Zach Brown --- kmod/src/data.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/kmod/src/data.c b/kmod/src/data.c index aae6f601..2c0822db 100644 --- a/kmod/src/data.c +++ b/kmod/src/data.c @@ -983,9 +983,6 @@ long scoutfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len) u64 last; s64 ret; - mutex_lock(&inode->i_mutex); - down_write(&si->extent_sem); - /* XXX support more flags */ if (mode & ~(FALLOC_FL_KEEP_SIZE)) { ret = -EOPNOTSUPP; @@ -1003,18 +1000,22 @@ long scoutfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len) goto out; } + mutex_lock(&inode->i_mutex); + ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_WRITE, SCOUTFS_LKF_REFRESH_INODE, inode, &lock); if (ret) - goto out; + goto out_mutex; inode_dio_wait(inode); + down_write(&si->extent_sem); + if (!(mode & FALLOC_FL_KEEP_SIZE) && (offset + len > i_size_read(inode))) { ret = inode_newsize_ok(inode, offset + len); if (ret) - goto out; + goto out_extent; } iblock = offset >> SCOUTFS_BLOCK_SM_SHIFT; @@ -1024,7 +1025,7 @@ long scoutfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len) ret = scoutfs_inode_index_lock_hold(inode, &ind_locks, false, true); if (ret) - goto out; + goto out_extent; ret = fallocate_extents(sb, inode, iblock, last, lock); @@ -1050,17 +1051,19 @@ long scoutfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len) } if (ret <= 0) - goto out; + goto out_extent; iblock += ret; ret = 0; } -out: - scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE); +out_extent: up_write(&si->extent_sem); +out_mutex: + scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE); mutex_unlock(&inode->i_mutex); +out: trace_scoutfs_data_fallocate(sb, ino, mode, offset, len, ret); return ret; }