Fix deadlock with request based dm and some drivers
Subsystems (2)
PATCH 1/1 Fix deadlock with request based dm and some drivers
Hi Alasdair,
I recently fixed a deadlock for a customer we have with the below patch. I have queued it up in my tree as not to lose it. Can I have an ack from you, or do you want to submit it yourself? I've marked it stable as well.
From 8ca211056519ac06bc96fb134dca1f8eb2141407 Mon Sep 17 00:00:00 2001
From: Jens Axboe <[email protected]>
Date: Tue, 6 Nov 2012 12:24:26 +0100
Subject: [PATCH] dm: fix deadlock with request based dm and queue request_fn
recursion
Request based dm attempts to re-run the request queue off the request completion path. If used with a driver that potentially does end_io from its request_fn, we could deadlock trying to recurse back into request dispatch. Fix this by punting the request queue run to kblockd.
Tested to fix a quickly reproducible deadlock in such a scenario.
--- drivers/md/dm.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/md/dm.c +7 -1
@@ -740,8 +740,14 @@ static void rq_completed(struct mapped_device *md, int rw, int run_queue)if (!md_in_flight (md))wake_up (&md->wait); +/* +* Run this off this callpath, as drivers could invoke end_io while +* inside their request_fn (and holding the queue lock). Calling +* back into ->request_fn() could deadlock attempting to grab the +* queue lock again. +*/ if (run_queue) -blk_run_queue (md->queue); +blk_run_queue_async (md->queue);/* * dm_put() must be at the end of this function. See the comment above
Signature · 5 lines
1.7.12.rc3 -- Jens Axboe
(Cc to dm-devel)
Quoted 4 lines · Jens Axboe
I recently fixed a deadlock for a customer we have with the below patch. I have queued it up in my tree as not to lose it. Can I have an ack from you, or do you want to submit it yourself? I've marked it stable as well.
Could you tell details about how the deadlock happened?
dm's end_io always throws the completion handling to softirq:
end_clone_request()
dm_complete_request()
blk_complete_request()
then it is processed in softirq context:
dm_softirq_done()
dm_done()
dm_end_request()
rq_completed(run_queue=true)
blk_run_queue()Since queue_lock is always held with interrupt disabled, I couldn't see why it could deadlock.
Request-based dm followed the completion model of scsi mid layer. So similar code path exists in scsi, too. For example:
scsi_request_fn()
scsi_kill_request()
blk_complete_request()
then:
scsi_softirq_done()
scsi_io_completion()
scsi_next_command()
scsi_run_queue()
spin_lock queue_lock
__blk_run_queue()If calling run-queue from softirq_done_fn() can cause deadlock, I'm afraid the problem is not limited to dm.
Quoted 40 lines
From
8ca211056519ac06bc96fb134dca1f8eb2141407Mon Sep 17 00:00:00 2001
From: Jens Axboe <[email protected]>
Date: Tue, 6 Nov 2012 12:24:26 +0100
Subject: [PATCH] dm: fix deadlock with request based dm and queuerequest_fn
recursionRequest based dm attempts to re-run the request queue off the request completion path. If used with a driver that potentially does
end_iofrom itsrequest_fn, we could deadlock trying to recurse back into request dispatch. Fix this by punting the request queue run to kblockd.Tested to fix a quickly reproducible deadlock in such a scenario.
Signed-off-by Jens Axboe <[email protected]>--- drivers/md/dm.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)drivers/md/dm.c +7
-1@@ -740,8 +740,14 @@ static void rq_completed(struct mapped_device *md, int rw, int run_queue)if (!md_in_flight (md))wake_up (&md->wait); +/* +* Run this off this callpath, as drivers could invoke end_io while +* inside their request_fn (and holding the queue lock). Calling +* back into ->request_fn() could deadlock attempting to grab the +* queue lock again. +*/ if (run_queue) -blk_run_queue (md->queue); +blk_run_queue_async (md->queue);/* * dm_put() must be at the end of this function. See the comment above
Signature · 1 line
Jun'ichi Nomura, NEC Corporation
Quoted 33 lines · Jun'ichi Nomura
(Cc to dm-devel)
Quoted 4 lines · Jens Axboe
I recently fixed a deadlock for a customer we have with the below patch. I have queued it up in my tree as not to lose it. Can I have an ack from you, or do you want to submit it yourself? I've marked it stable as well.
Could you tell details about how the deadlock happened?
dm's
end_ioalways throws the completion handling to softirq:
end_clone_request()
dm_complete_request()
blk_complete_request()then it is processed in softirq context:
dm_softirq_done()dm_done() dm_end_request() rq_completed(run_queue=true) blk_run_queue()Since
queue_lockis always held with interrupt disabled, I couldn't see why it could deadlock.Request-based dm followed the completion model of scsi mid layer. So similar code path exists in scsi, too. For example:
scsi_request_fn()
scsi_kill_request()
blk_complete_request()
then:
scsi_softirq_done()
Quoted 8 lines
scsi_io_completion() scsi_next_command() scsi_run_queue() spin_lock queue_lock __blk_run_queue()If calling run-queue from
softirq_done_fn()can cause deadlock, I'm afraid the problem is not limited to dm.
dm_softirq_done()
rq_completed()
blk_run_queue()
dm_request_fn()
dm_dispatch_request()
blk_insert_cloned_request()
__elv_add_request()
elv_insert()
blk_run_queue()
...Basically you recurse back into the request handler, since it ends up running the queue. But I see I've been too focused on the older release, since we don't actually do that anymore after the plugging rewrite. So it should actually be safe in current kernels and hence the patch only needed for the stable series where that is not the case.
Signature · 1 line
Jens Axboe
Quoted 40 lines · Jens Axboe
Quoted 39 lines · Jun'ichi Nomura
(Cc to dm-devel)
Quoted 4 lines · Jens Axboe
I recently fixed a deadlock for a customer we have with the below patch. I have queued it up in my tree as not to lose it. Can I have an ack from you, or do you want to submit it yourself? I've marked it stable as well.
Could you tell details about how the deadlock happened?
dm's
end_ioalways throws the completion handling to softirq:
end_clone_request()
dm_complete_request()
blk_complete_request()then it is processed in softirq context:
dm_softirq_done()dm_done() dm_end_request() rq_completed(run_queue=true) blk_run_queue()Since
queue_lockis always held with interrupt disabled, I couldn't see why it could deadlock.Request-based dm followed the completion model of scsi mid layer. So similar code path exists in scsi, too. For example:
scsi_request_fn()
scsi_kill_request()
blk_complete_request()
then:
scsi_softirq_done()scsi_io_completion() scsi_next_command() scsi_run_queue() spin_lock queue_lock __blk_run_queue()
Quoted 6 lines
Quoted 2 lines
If calling run-queue from
softirq_done_fn()can cause deadlock, I'm afraid the problem is not limited to dm.
dm_softirq_done()rq_completed() blk_run_queue()
^^ this is for dm's queue
Quoted 6 lines
dm_request_fn()
dm_dispatch_request()
blk_insert_cloned_request()
__elv_add_request()
elv_insert()
blk_run_queue()^^ this is for lower device's queue
Quoted 4 lines
...Basically you recurse back into the request handler, since it ends up running the queue.
But the queues are different as commented inline above. So it should be ok. (from deadlock point of view)
Quoted 4 lines
But I see I've been too focused on the older release,
since we don't actually do that anymore after the plugging rewrite. So it should actually be safe in current kernels and hence the patch only needed for the stable series where that is not the case.
BTW, using blk_run_queue_async() in softirq_done_fn() might be good from performance point of view? It may reduce latency of the softirq and have an effect of batching request_fn calls.
Signature · 1 line
Jun'ichi Nomura, NEC Corporation
Quoted 54 lines · Jun'ichi Nomura
Quoted 40 lines · Jens Axboe
Quoted 39 lines · Jun'ichi Nomura
(Cc to dm-devel)
Quoted 4 lines · Jens Axboe
I recently fixed a deadlock for a customer we have with the below patch. I have queued it up in my tree as not to lose it. Can I have an ack from you, or do you want to submit it yourself? I've marked it stable as well.
Could you tell details about how the deadlock happened?
dm's
end_ioalways throws the completion handling to softirq:
end_clone_request()
dm_complete_request()
blk_complete_request()then it is processed in softirq context:
dm_softirq_done()dm_done() dm_end_request() rq_completed(run_queue=true) blk_run_queue()Since
queue_lockis always held with interrupt disabled, I couldn't see why it could deadlock.Request-based dm followed the completion model of scsi mid layer. So similar code path exists in scsi, too. For example:
scsi_request_fn()
scsi_kill_request()
blk_complete_request()
then:
scsi_softirq_done()scsi_io_completion() scsi_next_command() scsi_run_queue() spin_lock queue_lock __blk_run_queue()Quoted 6 lines
Quoted 2 lines
If calling run-queue from
softirq_done_fn()can cause deadlock, I'm afraid the problem is not limited to dm.
dm_softirq_done()rq_completed() blk_run_queue()^^ this is for dm's queue
Quoted 6 lines
dm_request_fn() dm_dispatch_request() blk_insert_cloned_request() __elv_add_request() elv_insert() blk_run_queue()
Quoted 8 lines
^^ this is for lower device's queue
Quoted 4 lines
...Basically you recurse back into the request handler, since it ends up running the queue.
But the queues are different as commented inline above. So it should be ok. (from deadlock point of view)
It's still not OK, some drivers end up doing spin_unlock_irq() in their request_fn. Running unknown request_fn from ipi/irq is a bad idea, imho, it'll quickly cause problems.
Quoted 8 lines
Quoted 4 lines
But I see I've been too focused on the older release,
since we don't actually do that anymore after the plugging rewrite. So it should actually be safe in current kernels and hence the patch only needed for the stable series where that is not the case.BTW, using
blk_run_queue_async()insoftirq_done_fn()might be good from performance point of view? It may reduce latency of the softirq and have an effect of batchingrequest_fncalls.
Yes, I argued the same for the original people who saw the problem. It is quite an extensive and expensive path to have off the IPI handler. So from that point of view, I'd recommend the patch as well.
Signature · 1 line
Jens Axboe
Quoted 61 lines · Jens Axboe
Quoted 60 lines · Jun'ichi Nomura
Quoted 40 lines · Jens Axboe
Quoted 39 lines · Jun'ichi Nomura
(Cc to dm-devel)
Quoted 4 lines · Jens Axboe
I recently fixed a deadlock for a customer we have with the below patch. I have queued it up in my tree as not to lose it. Can I have an ack from you, or do you want to submit it yourself? I've marked it stable as well.
Could you tell details about how the deadlock happened?
dm's
end_ioalways throws the completion handling to softirq:
end_clone_request()
dm_complete_request()
blk_complete_request()then it is processed in softirq context:
dm_softirq_done()dm_done() dm_end_request() rq_completed(run_queue=true) blk_run_queue()Since
queue_lockis always held with interrupt disabled, I couldn't see why it could deadlock.Request-based dm followed the completion model of scsi mid layer. So similar code path exists in scsi, too. For example:
scsi_request_fn()
scsi_kill_request()
blk_complete_request()
then:
scsi_softirq_done()scsi_io_completion() scsi_next_command() scsi_run_queue() spin_lock queue_lock __blk_run_queue()Quoted 6 lines
Quoted 2 lines
If calling run-queue from
softirq_done_fn()can cause deadlock, I'm afraid the problem is not limited to dm.
dm_softirq_done()rq_completed() blk_run_queue()^^ this is for dm's queue
Quoted 6 lines
dm_request_fn() dm_dispatch_request() blk_insert_cloned_request() __elv_add_request() elv_insert() blk_run_queue()^^ this is for lower device's queue
Quoted 4 lines
...Basically you recurse back into the request handler, since it ends up running the queue.
Quoted 6 lines
Quoted 2 lines
But the queues are different as commented inline above. So it should be ok. (from deadlock point of view)
It's still not OK, some drivers end up doing
spin_unlock_irq()in theirrequest_fn. Running unknownrequest_fnfrom ipi/irq is a bad idea, imho, it'll quickly cause problems.
Though I still don't think there is actual deadlock because dm's queue lock is released before dm_dispatch_request(), I see your point why it's potentially bad.
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
But I see I've been too focused on the older release,
since we don't actually do that anymore after the plugging rewrite. So it should actually be safe in current kernels and hence the patch only needed for the stable series where that is not the case.
Quoted 7 lines
Quoted 3 lines
BTW, using
blk_run_queue_async()insoftirq_done_fn()might be good from performance point of view? It may reduce latency of the softirq and have an effect of batchingrequest_fncalls.Yes, I argued the same for the original people who saw the problem. It is quite an extensive and expensive path to have off the IPI handler. So from that point of view, I'd recommend the patch as well.
Thank you for confirmation.
If it's good for performance, the patch is fine with me.
Signature · 1 line
Jun'ichi Nomura, NEC Corporation
Quoted 1 line · Jun'ichi Nomura
If it's good for performance, the patch is fine with me.
Alasdair