| From ec1c8c38536d57810de9e8d0afeb91f995cfbe90 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Wed, 3 Jun 2020 15:48:19 +0800 |
| Subject: libata: Use per port sync for detach |
| |
| From: Kai-Heng Feng <kai.heng.feng@canonical.com> |
| |
| [ Upstream commit b5292111de9bb70cba3489075970889765302136 ] |
| |
| Commit 130f4caf145c ("libata: Ensure ata_port probe has completed before |
| detach") may cause system freeze during suspend. |
| |
| Using async_synchronize_full() in PM callbacks is wrong, since async |
| callbacks that are already scheduled may wait for not-yet-scheduled |
| callbacks, causes a circular dependency. |
| |
| Instead of using big hammer like async_synchronize_full(), use async |
| cookie to make sure port probe are synced, without affecting other |
| scheduled PM callbacks. |
| |
| Fixes: 130f4caf145c ("libata: Ensure ata_port probe has completed before detach") |
| Suggested-by: John Garry <john.garry@huawei.com> |
| Signed-off-by: Kai-Heng Feng <kai.heng.feng@canonical.com> |
| Tested-by: John Garry <john.garry@huawei.com> |
| BugLink: https://bugs.launchpad.net/bugs/1867983 |
| Signed-off-by: Jens Axboe <axboe@kernel.dk> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/ata/libata-core.c | 11 +++++------ |
| include/linux/libata.h | 3 +++ |
| 2 files changed, 8 insertions(+), 6 deletions(-) |
| |
| diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c |
| index 33eb5e342a7a9..a3a65f5490c02 100644 |
| --- a/drivers/ata/libata-core.c |
| +++ b/drivers/ata/libata-core.c |
| @@ -57,7 +57,6 @@ |
| #include <linux/workqueue.h> |
| #include <linux/scatterlist.h> |
| #include <linux/io.h> |
| -#include <linux/async.h> |
| #include <linux/log2.h> |
| #include <linux/slab.h> |
| #include <linux/glob.h> |
| @@ -6536,7 +6535,7 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) |
| /* perform each probe asynchronously */ |
| for (i = 0; i < host->n_ports; i++) { |
| struct ata_port *ap = host->ports[i]; |
| - async_schedule(async_port_probe, ap); |
| + ap->cookie = async_schedule(async_port_probe, ap); |
| } |
| |
| return 0; |
| @@ -6676,11 +6675,11 @@ void ata_host_detach(struct ata_host *host) |
| { |
| int i; |
| |
| - /* Ensure ata_port probe has completed */ |
| - async_synchronize_full(); |
| - |
| - for (i = 0; i < host->n_ports; i++) |
| + for (i = 0; i < host->n_ports; i++) { |
| + /* Ensure ata_port probe has completed */ |
| + async_synchronize_cookie(host->ports[i]->cookie + 1); |
| ata_port_detach(host->ports[i]); |
| + } |
| |
| /* the host is dead now, dissociate ACPI */ |
| ata_acpi_dissociate(host); |
| diff --git a/include/linux/libata.h b/include/linux/libata.h |
| index 93838d98e3f38..5c9a44e3a0278 100644 |
| --- a/include/linux/libata.h |
| +++ b/include/linux/libata.h |
| @@ -38,6 +38,7 @@ |
| #include <linux/acpi.h> |
| #include <linux/cdrom.h> |
| #include <linux/sched.h> |
| +#include <linux/async.h> |
| |
| /* |
| * Define if arch has non-standard setup. This is a _PCI_ standard |
| @@ -884,6 +885,8 @@ struct ata_port { |
| struct timer_list fastdrain_timer; |
| unsigned long fastdrain_cnt; |
| |
| + async_cookie_t cookie; |
| + |
| int em_message_type; |
| void *private_data; |
| |
| -- |
| 2.25.1 |
| |