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 ba0cffbd0bb6e..46bf7e9d00aba 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> @@ -6410,7 +6409,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; @@ -6550,11 +6549,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 cdfb67b22317b..780ccde2c3127 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 @@ -878,6 +879,8 @@ struct ata_port { struct timer_list fastdrain_timer; unsigned long fastdrain_cnt;
+ async_cookie_t cookie; + int em_message_type; void *private_data;