Commit 55bd725a authored by Aneesh Kumar K.V's avatar Aneesh Kumar K.V Committed by Theodore Ts'o

ext4: Fix locking hierarchy violation in ext4_fallocate()

ext4_fallocate() was trying to acquire i_data_sem outside of
jbd2_start_transaction/jbd2_journal_stop, which violates ext4's locking
hierarchy.  So we take i_mutex to prevent writes and truncates during
the complete fallocate operation, and use ext4_get_block_wrap() which
acquires and releases i_data_sem for each block allocation.
Signed-off-by: default avatarAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: default avatarMingming Cao <cmm@us.ibm.com>
Signed-off-by: default avatar"Theodore Ts'o" <tytso@mit.edu>
parent 642be6ec
...@@ -2623,7 +2623,7 @@ long ext4_fallocate(struct inode *inode, int mode, loff_t offset, loff_t len) ...@@ -2623,7 +2623,7 @@ long ext4_fallocate(struct inode *inode, int mode, loff_t offset, loff_t len)
* modify 1 super block, 1 block bitmap and 1 group descriptor. * modify 1 super block, 1 block bitmap and 1 group descriptor.
*/ */
credits = EXT4_DATA_TRANS_BLOCKS(inode->i_sb) + 3; credits = EXT4_DATA_TRANS_BLOCKS(inode->i_sb) + 3;
down_write((&EXT4_I(inode)->i_data_sem)); mutex_lock(&inode->i_mutex);
retry: retry:
while (ret >= 0 && ret < max_blocks) { while (ret >= 0 && ret < max_blocks) {
block = block + ret; block = block + ret;
...@@ -2634,7 +2634,7 @@ retry: ...@@ -2634,7 +2634,7 @@ retry:
break; break;
} }
ret = ext4_ext_get_blocks(handle, inode, block, ret = ext4_get_blocks_wrap(handle, inode, block,
max_blocks, &map_bh, max_blocks, &map_bh,
EXT4_CREATE_UNINITIALIZED_EXT, 0); EXT4_CREATE_UNINITIALIZED_EXT, 0);
WARN_ON(ret <= 0); WARN_ON(ret <= 0);
...@@ -2680,7 +2680,6 @@ retry: ...@@ -2680,7 +2680,6 @@ retry:
if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
goto retry; goto retry;
up_write((&EXT4_I(inode)->i_data_sem));
/* /*
* Time to update the file size. * Time to update the file size.
* Update only when preallocation was requested beyond the file size. * Update only when preallocation was requested beyond the file size.
...@@ -2692,21 +2691,18 @@ retry: ...@@ -2692,21 +2691,18 @@ retry:
* if no error, we assume preallocation succeeded * if no error, we assume preallocation succeeded
* completely * completely
*/ */
mutex_lock(&inode->i_mutex);
i_size_write(inode, offset + len); i_size_write(inode, offset + len);
EXT4_I(inode)->i_disksize = i_size_read(inode); EXT4_I(inode)->i_disksize = i_size_read(inode);
mutex_unlock(&inode->i_mutex);
} else if (ret < 0 && nblocks) { } else if (ret < 0 && nblocks) {
/* Handle partial allocation scenario */ /* Handle partial allocation scenario */
loff_t newsize; loff_t newsize;
mutex_lock(&inode->i_mutex);
newsize = (nblocks << blkbits) + i_size_read(inode); newsize = (nblocks << blkbits) + i_size_read(inode);
i_size_write(inode, EXT4_BLOCK_ALIGN(newsize, blkbits)); i_size_write(inode, EXT4_BLOCK_ALIGN(newsize, blkbits));
EXT4_I(inode)->i_disksize = i_size_read(inode); EXT4_I(inode)->i_disksize = i_size_read(inode);
mutex_unlock(&inode->i_mutex);
} }
} }
mutex_unlock(&inode->i_mutex);
return ret > 0 ? ret2 : ret; return ret > 0 ? ret2 : ret;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment