mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2025-11-05 22:04:53 +03:00
zio can deadlock during device removal
When doing a device removal on a pool with gang blocks, the zio pipeline
can deadlock when trying to free blocks from a device which is being
removed with a stack similar to this:
0xffff8ab9a13a1740 UNINTERRUPTIBLE 4
__schedule+0x2e5
__schedule+0x2e5
schedule+0x33
schedule_preempt_disabled+0xe
__mutex_lock.isra.12+0x2a7
__mutex_lock.isra.12+0x2a7
__mutex_lock_slowpath+0x13
mutex_lock+0x2c
free_from_removing_vdev+0x61
metaslab_free_impl+0xd6
metaslab_free_dva+0x5e
metaslab_free+0x196
zio_free_sync+0xe4
zio_free_gang+0x38
zio_gang_tree_issue+0x42
zio_gang_tree_issue+0xa2
zio_gang_issue+0x6d
zio_execute+0x94
zio_execute+0x94
taskq_thread+0x23b
kthread+0x120
ret_from_fork+0x1f
Since there are gang blocks we have to read the gang members as part of
the free. This can be seen with a zio dependency tree that looks like
this:
sdb> echo 0xffff900c24f8a700 | zio -rc | zio
ADDRESS TYPE STAGE WAITER
0xffff900c24f8a700 NULL CHECKSUM_VERIFY 0xffff900ddfd31740
0xffff900c24f8c920 FREE GANG_ASSEMBLE -
0xffff900d93d435a0 READ DONE
In the illustration above we are processing frees but because of gang
block we have to read the constituents blocks. Once we finish the READ
in the zio pipeline we will execute the parent. In this case the parent
is a FREE but the zio taskq is a READ and we continue to process the
pipeline leading to the stack above. In the stack above, we are blocked
waiting for the svr_lock so as a result a READ interrupt taskq thread
is now consumed. Eventually, all of the READ taskq threads end up
blocked and we're unable to complete any read requests.
In zio_notify_parent there is an optimization to continue to use
the taskq thread to exectue the parent's pipeline. To resolve the
deadlock above, we only allow this optimization if the parent's
zio type matches the child which just completed.
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Matthew Ahrens <mahrens@delphix.com>
Signed-off-by: George Wilson <gwilson@delphix.com>
External-issue: DLPX-80130
Closes #14236
This commit is contained in:
parent
d7cf06a25d
commit
ffd2e15d65
@ -724,7 +724,9 @@ zio_notify_parent(zio_t *pio, zio_t *zio, enum zio_wait_type wait,
|
||||
|
||||
/*
|
||||
* If we can tell the caller to execute this parent next, do
|
||||
* so. Otherwise dispatch the parent zio as its own task.
|
||||
* so. We only do this if the parent's zio type matches the
|
||||
* child's type. Otherwise dispatch the parent zio in its
|
||||
* own taskq.
|
||||
*
|
||||
* Having the caller execute the parent when possible reduces
|
||||
* locking on the zio taskq's, reduces context switch
|
||||
@ -743,7 +745,8 @@ zio_notify_parent(zio_t *pio, zio_t *zio, enum zio_wait_type wait,
|
||||
* parent-child relationships, as we do with the "mega zio"
|
||||
* of writes for spa_sync(), and the chain of ZIL blocks.
|
||||
*/
|
||||
if (next_to_executep != NULL && *next_to_executep == NULL) {
|
||||
if (next_to_executep != NULL && *next_to_executep == NULL &&
|
||||
pio->io_type == zio->io_type) {
|
||||
*next_to_executep = pio;
|
||||
} else {
|
||||
zio_taskq_dispatch(pio, type, B_FALSE);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user