* [RESEND PATCH] scsi: fix ata_port_wait_eh() hang caused by missing to wake up eh thread
@ 2016-10-09 14:21 Wei Fang
2016-10-09 15:24 ` Christoph Hellwig
0 siblings, 1 reply; 2+ messages in thread
From: Wei Fang @ 2016-10-09 14:21 UTC (permalink / raw)
To: jejb, martin.petersen; +Cc: linux-scsi, Wei Fang
->host_eh_schedule and ->shost_state may be accessed simultaneously as
below:
1.In ata port probe path:
ata_std_sched_eh()
scsi_schedule_eh()
...
spin_lock_irqsave(shost->host_lock, flags);
if (scsi_host_set_state(shost, SHOST_RECOVERY) == 0 ||
scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY) == 0) {
shost->host_eh_scheduled++;
scsi_eh_wakeup();
}
spin_unlock_irqrestore(shost->host_lock, flags);
...
2.In IO complete path:
scsi_device_unbusy()
...
atomic_dec(&shost->host_busy)
if (unlikely(scsi_host_in_recovery(shost) &&
(shost->host_failed || shost->host_eh_scheduled))) {
spin_lock_irqsave(shost->host_lock, flags);
scsi_eh_wakeup(shost);
spin_unlock_irqrestore(shost->host_lock, flags);
}
...
In the IO complete path, ->host_eh_schedule and ->shost_state are
accessed without ->host_lock, and obsoleted data may be got after
scsi_eh_wakeup() has been called in scsi_schedule_eh().
It's the case that causes the problem:
* One IO hasn't finished when scsi_schedule_eh() has been called
in ata port probe path
* scsi_eh_wakeup() has been called in scsi_schedule_eh() without
waking up eh thread because of ->host_busy != ->host_failed
* this IO completes and scsi_device_unbusy() is called
* obsoleted ->host_eh_schedule and ->shost_state are got, and eh
thread can't be woken up in this path too
Eh thread won't be woken up ever in this case, and a hung task
will happen:
INFO: task kworker/u64:0:6 blocked for more than 120 seconds.
...
Call trace:
[<ffff800000086b04>] __switch_to+0x78/0x90
[<ffff800000bf95bc>] __schedule+0x244/0x79c
[<ffff800000bf9b54>] schedule+0x40/0x98
[<ffff8000006cc990>] ata_port_wait_eh+0x8c/0x110
[<ffff80000064f764>] sas_ata_wait_eh+0x3c/0x44
[<ffff80000064f7e0>] sas_probe_sata_device+0x74/0xf8
[<ffff800000648320>] sas_add_device+0xac/0x1ac
[<ffff8000000d9458>] process_one_work+0x164/0x428
[<ffff8000000d9860>] worker_thread+0x144/0x4a8
[<ffff8000000dfbb0>] kthread+0xfc/0x110
After that, the host is in recovery state, and any IOs to this
host will be blocked.
Fix this by accessing ->host_eh_schedule and ->shost_state in
->host_lock. We have tested the IOPS throughput by fio and found
no performance degradation.
Signed-off-by: Wei Fang <fangwei1@huawei.com>
---
drivers/scsi/scsi_lib.c | 22 ++++++++++++++--------
1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 2cca9cf..5a72e1d 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -272,23 +272,27 @@ static void scsi_init_cmd_errh(struct scsi_cmnd *cmd)
cmd->cmd_len = scsi_command_size(cmd->cmnd);
}
+static void __scsi_eh_wakeup(struct Scsi_Host *shost)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ if (unlikely(scsi_host_in_recovery(shost) &&
+ (shost->host_failed || shost->host_eh_scheduled)))
+ scsi_eh_wakeup(shost);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+}
+
void scsi_device_unbusy(struct scsi_device *sdev)
{
struct Scsi_Host *shost = sdev->host;
struct scsi_target *starget = scsi_target(sdev);
- unsigned long flags;
atomic_dec(&shost->host_busy);
if (starget->can_queue > 0)
atomic_dec(&starget->target_busy);
- if (unlikely(scsi_host_in_recovery(shost) &&
- (shost->host_failed || shost->host_eh_scheduled))) {
- spin_lock_irqsave(shost->host_lock, flags);
- scsi_eh_wakeup(shost);
- spin_unlock_irqrestore(shost->host_lock, flags);
- }
-
+ __scsi_eh_wakeup(shost);
atomic_dec(&sdev->device_busy);
}
@@ -1457,6 +1461,7 @@ static inline int scsi_host_queue_ready(struct request_queue *q,
spin_unlock_irq(shost->host_lock);
out_dec:
atomic_dec(&shost->host_busy);
+ __scsi_eh_wakeup(shost);
return 0;
}
@@ -1931,6 +1936,7 @@ static int scsi_queue_rq(struct blk_mq_hw_ctx *hctx,
out_dec_host_busy:
atomic_dec(&shost->host_busy);
+ __scsi_eh_wakeup(shost);
out_dec_target_busy:
if (scsi_target(sdev)->can_queue > 0)
atomic_dec(&scsi_target(sdev)->target_busy);
--
1.9.3
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [RESEND PATCH] scsi: fix ata_port_wait_eh() hang caused by missing to wake up eh thread
2016-10-09 14:21 [RESEND PATCH] scsi: fix ata_port_wait_eh() hang caused by missing to wake up eh thread Wei Fang
@ 2016-10-09 15:24 ` Christoph Hellwig
0 siblings, 0 replies; 2+ messages in thread
From: Christoph Hellwig @ 2016-10-09 15:24 UTC (permalink / raw)
To: Wei Fang; +Cc: jejb, martin.petersen, linux-scsi
On Sun, Oct 09, 2016 at 10:21:45PM +0800, Wei Fang wrote:
> ->host_eh_schedule and ->shost_state may be accessed simultaneously as
> below:
But we can't simply take shost_lock in every completion handler, as
that would kill peformance.
>From a quick look the most sensible options seems to be to remove the
host_eh_scheduled field and replace it with another host state.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2016-10-09 15:25 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-10-09 14:21 [RESEND PATCH] scsi: fix ata_port_wait_eh() hang caused by missing to wake up eh thread Wei Fang
2016-10-09 15:24 ` Christoph Hellwig
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).