ext4: Fix deadlock on inode reallocation
Subsystems (2)
Jan Kara
reviewerPATCH 1/1 ext4: Fix deadlock on inode reallocation
Currently there is a race in ext4 when reallocating freed inode resulting in a deadlock:
Task1 Task2ext4_evict_inode()
handle = ext4_journal_start();
...
if (IS_SYNC(inode))
handle->h_sync = 1;
ext4_free_inode()
ext4_new_inode()
handle = ext4_journal_start() finds the bit in inode bitmap
already clear
insert_inode_locked()
waits for inode to be
removed from the hash.
ext4_journal_stop(handle)
jbd2_journal_stop(handle)
jbd2_log_wait_commit(journal, tid);
- deadlocks waiting for transaction handle Task2 holds
Fix the problem by removing inode from the hash already in ext4_clear_inode() by which time all IO for the inode is done so reuse is already fine but we are still before possibly blocking on transaction commit.
--- fs/ext4/super.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
Ted, this is a regression recently introduced by VFS changes in insert_inode_locked() but I think it's best fixed in ext4. If you agree, it would be nice to merge this so that it makes it to 7.0 release. Thanks!
fs/ext4/super.c +21 -0
@@ -1527,6 +1527,27 @@ void ext4_clear_inode(struct inode *inode)invalidate_inode_buffers (inode);clear_inode (inode);ext4_discard_preallocations (inode); +/* +* We must remove the inode from the hash before ext4_free_inode() +* clears the bit in inode bitmap as otherwise another process reusing +* the inode will block in insert_inode_hash() waiting for inode +* eviction to complete while holding transaction handle open, but +* ext4_evict_inode() still running for that inode could block waiting +* for transaction commit if the inode is marked as IS_SYNC => deadlock. +* +* Removing the inode from the hash here is safe. There are two cases +* to consider: +* 1) The inode still has references to it (i_nlink > 0). In that case +* we are keeping the inode and once we remove the inode from the hash, +* iget() can create the new inode structure for the same inode number +* and we are fine with that as all IO on behalf of the inode is +* finished. +* 2) We are deleting the inode (i_nlink == 0). In that case inode +* number cannot be reused until ext4_free_inode() clears the bit in +* the inode bitmap, at which point all IO is done and reuse is fine +* again. +*/ +remove_inode_hash (inode);ext4_es_remove_extent (inode,0 ,EXT_MAX_BLOCKS );dquot_drop (inode);if (EXT4_I (inode)->jinode) {
Signature · 1 line
2.51.0
can this get any traction?
master as-is contains a change which uncovered the ext4 problem. either the deadlock needs to be fixed in time for the release or the other change needs to get temporarily reverted.
interested parties can find proposed revert here:
https://lore.kernel.org/linux-fsdevel/[email protected]/
Quoted 76 lines · Jan Kara
Currently there is a race in ext4 when reallocating freed inode resulting in a deadlock:
Task1 Task2
ext4_evict_inode()
handle =ext4_journal_start();
...
if (IS_SYNC(inode))handle->h_sync = 1;
ext4_free_inode()
ext4_new_inode()
handle =ext4_journal_start()finds the bit in inode bitmap
already clear
insert_inode_locked()
waits for inode to be
removed from the hash.
ext4_journal_stop(handle)
jbd2_journal_stop(handle)
jbd2_log_wait_commit(journal, tid);
- deadlocks waiting for transaction handle Task2 holdsFix the problem by removing inode from the hash already in
ext4_clear_inode()by which time all IO for the inode is done so reuse is already fine but we are still before possibly blocking on transaction commit.Reported-by "Lai, Yi" <[email protected]>Fixes 88ec797c4680 ("fs: make insert_inode_locked() wait for inode destruction")Signed-off-by Jan Kara <[email protected]>--- fs/ext4/super.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)Ted, this is a regression recently introduced by VFS changes in
insert_inode_locked()but I think it's best fixed in ext4. If you agree, it would be nice to merge this so that it makes it to 7.0 release. Thanks!fs/ext4/super.c +21
-1@@ -1527,6 +1527,27 @@ void ext4_clear_inode(struct inode *inode)invalidate_inode_buffers (inode);clear_inode (inode);ext4_discard_preallocations (inode); +/* +* We must remove the inode from the hash before ext4_free_inode() +* clears the bit in inode bitmap as otherwise another process reusing +* the inode will block in insert_inode_hash() waiting for inode +* eviction to complete while holding transaction handle open, but +* ext4_evict_inode() still running for that inode could block waiting +* for transaction commit if the inode is marked as IS_SYNC => deadlock. +* +* Removing the inode from the hash here is safe. There are two cases +* to consider: +* 1) The inode still has references to it (i_nlink > 0). In that case +* we are keeping the inode and once we remove the inode from the hash, +* iget() can create the new inode structure for the same inode number +* and we are fine with that as all IO on behalf of the inode is +* finished. +* 2) We are deleting the inode (i_nlink == 0). In that case inode +* number cannot be reused until ext4_free_inode() clears the bit in +* the inode bitmap, at which point all IO is done and reuse is fine +* again. +*/ +remove_inode_hash (inode);ext4_es_remove_extent (inode,0 ,EXT_MAX_BLOCKS );dquot_drop (inode);if (EXT4_I (inode)->jinode) { --2.51.0
Jan Kara
reviewerQuoted 1 line · Mateusz Guzik
can this get any traction?
I've talked to Ted today and is currently picking up fixes into his tree so we should get some action soon. Ted, here is one patch which needs either fast-tracking to 7.0 or we need to revert a VFS change to avoid deadlocks on ext4.
Honza
Quoted 84 lines
master as-is contains a change which uncovered the ext4 problem. either the deadlock needs to be fixed in time for the release or the other change needs to get temporarily reverted.
interested parties can find proposed revert here:
https://lore.kernel.org/linux-fsdevel/[email protected]/Quoted 76 lines · Jan Kara
Currently there is a race in ext4 when reallocating freed inode resulting in a deadlock:
Task1 Task2
ext4_evict_inode()
handle =ext4_journal_start();
...
if (IS_SYNC(inode))handle->h_sync = 1;
ext4_free_inode()
ext4_new_inode()
handle =ext4_journal_start()finds the bit in inode bitmap
already clear
insert_inode_locked()
waits for inode to be
removed from the hash.
ext4_journal_stop(handle)
jbd2_journal_stop(handle)
jbd2_log_wait_commit(journal, tid);
- deadlocks waiting for transaction handle Task2 holdsFix the problem by removing inode from the hash already in
ext4_clear_inode()by which time all IO for the inode is done so reuse is already fine but we are still before possibly blocking on transaction commit.Reported-by "Lai, Yi" <[email protected]>Fixes 88ec797c4680 ("fs: make insert_inode_locked() wait for inode destruction")Signed-off-by Jan Kara <[email protected]>--- fs/ext4/super.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)Ted, this is a regression recently introduced by VFS changes in
insert_inode_locked()but I think it's best fixed in ext4. If you agree, it would be nice to merge this so that it makes it to 7.0 release. Thanks!fs/ext4/super.c +21
-1@@ -1527,6 +1527,27 @@ void ext4_clear_inode(struct inode *inode)invalidate_inode_buffers (inode);clear_inode (inode);ext4_discard_preallocations (inode); +/* +* We must remove the inode from the hash before ext4_free_inode() +* clears the bit in inode bitmap as otherwise another process reusing +* the inode will block in insert_inode_hash() waiting for inode +* eviction to complete while holding transaction handle open, but +* ext4_evict_inode() still running for that inode could block waiting +* for transaction commit if the inode is marked as IS_SYNC => deadlock. +* +* Removing the inode from the hash here is safe. There are two cases +* to consider: +* 1) The inode still has references to it (i_nlink > 0). In that case +* we are keeping the inode and once we remove the inode from the hash, +* iget() can create the new inode structure for the same inode number +* and we are fine with that as all IO on behalf of the inode is +* finished. +* 2) We are deleting the inode (i_nlink == 0). In that case inode +* number cannot be reused until ext4_free_inode() clears the bit in +* the inode bitmap, at which point all IO is done and reuse is fine +* again. +*/ +remove_inode_hash (inode);ext4_es_remove_extent (inode,0 ,EXT_MAX_BLOCKS );dquot_drop (inode);if (EXT4_I (inode)->jinode) { --2.51.0
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Quoted 22 lines · Jan Kara
Currently there is a race in ext4 when reallocating freed inode resulting in a deadlock:
Task1 Task2
ext4_evict_inode()
handle =ext4_journal_start();
...
if (IS_SYNC(inode))handle->h_sync = 1;
ext4_free_inode()
ext4_new_inode()
handle =ext4_journal_start()finds the bit in inode bitmap
already clear
insert_inode_locked()
waits for inode to be
removed from the hash.
ext4_journal_stop(handle)
jbd2_journal_stop(handle)
jbd2_log_wait_commit(journal, tid);
- deadlocks waiting for transaction handle Task2 holds
Quoted 1 line
[...]
Applied, thanks!
[1/1] ext4: Fix deadlock on inode reallocation
commit: 0c90eed1b95335eba4f546e6742a8e4503d79349
Best regards,
Signature · 1 line
Theodore Ts'o <[email protected]>