ext4: use transaction reservation for extent conversion in ext4_end_io
Later we would like to clear PageWriteback bit only after extent conversion from unwritten to written extents is performed. However it is not possible to start a transaction after PageWriteback is set because that violates lock ordering (and is easy to deadlock). So we have to reserve a transaction before locking pages and sending them for IO and later we use the transaction for extent conversion from ext4_end_io(). Reviewed-by: Zheng Liu <wenqing.lz@taobao.com> Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
This commit is contained in:
@@ -4566,10 +4566,9 @@ retry:
|
||||
* function, to convert the fallocated extents after IO is completed.
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int ext4_convert_unwritten_extents(struct inode *inode, loff_t offset,
|
||||
ssize_t len)
|
||||
int ext4_convert_unwritten_extents(handle_t *handle, struct inode *inode,
|
||||
loff_t offset, ssize_t len)
|
||||
{
|
||||
handle_t *handle;
|
||||
unsigned int max_blocks;
|
||||
int ret = 0;
|
||||
int ret2 = 0;
|
||||
@@ -4584,16 +4583,32 @@ int ext4_convert_unwritten_extents(struct inode *inode, loff_t offset,
|
||||
max_blocks = ((EXT4_BLOCK_ALIGN(len + offset, blkbits) >> blkbits) -
|
||||
map.m_lblk);
|
||||
/*
|
||||
* credits to insert 1 extent into extent tree
|
||||
* This is somewhat ugly but the idea is clear: When transaction is
|
||||
* reserved, everything goes into it. Otherwise we rather start several
|
||||
* smaller transactions for conversion of each extent separately.
|
||||
*/
|
||||
credits = ext4_chunk_trans_blocks(inode, max_blocks);
|
||||
if (handle) {
|
||||
handle = ext4_journal_start_reserved(handle,
|
||||
EXT4_HT_EXT_CONVERT);
|
||||
if (IS_ERR(handle))
|
||||
return PTR_ERR(handle);
|
||||
credits = 0;
|
||||
} else {
|
||||
/*
|
||||
* credits to insert 1 extent into extent tree
|
||||
*/
|
||||
credits = ext4_chunk_trans_blocks(inode, max_blocks);
|
||||
}
|
||||
while (ret >= 0 && ret < max_blocks) {
|
||||
map.m_lblk += ret;
|
||||
map.m_len = (max_blocks -= ret);
|
||||
handle = ext4_journal_start(inode, EXT4_HT_MAP_BLOCKS, credits);
|
||||
if (IS_ERR(handle)) {
|
||||
ret = PTR_ERR(handle);
|
||||
break;
|
||||
if (credits) {
|
||||
handle = ext4_journal_start(inode, EXT4_HT_MAP_BLOCKS,
|
||||
credits);
|
||||
if (IS_ERR(handle)) {
|
||||
ret = PTR_ERR(handle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret = ext4_map_blocks(handle, inode, &map,
|
||||
EXT4_GET_BLOCKS_IO_CONVERT_EXT);
|
||||
@@ -4604,10 +4619,13 @@ int ext4_convert_unwritten_extents(struct inode *inode, loff_t offset,
|
||||
inode->i_ino, map.m_lblk,
|
||||
map.m_len, ret);
|
||||
ext4_mark_inode_dirty(handle, inode);
|
||||
ret2 = ext4_journal_stop(handle);
|
||||
if (ret <= 0 || ret2 )
|
||||
if (credits)
|
||||
ret2 = ext4_journal_stop(handle);
|
||||
if (ret <= 0 || ret2)
|
||||
break;
|
||||
}
|
||||
if (!credits)
|
||||
ret2 = ext4_journal_stop(handle);
|
||||
return ret > 0 ? ret2 : ret;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user