]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
scsi: sd: Revert "Rely on the driver core for asynchronous probing"
authorBart Van Assche <bvanassche@acm.org>
Mon, 29 Apr 2019 18:21:53 +0000 (11:21 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Mon, 29 Apr 2019 21:13:12 +0000 (17:13 -0400)
Hibernation hangs as follows due to commit 21e6ba3f0e02 when using SATA:

Call Trace:
 __schedule+0x464/0xe70
 schedule+0x4e/0xd0
 blk_queue_enter+0x5fe/0x7e0
 generic_make_request+0x313/0x950
 submit_bio+0x9b/0x250
 submit_bio_wait+0xc9/0x110
 hib_submit_io+0x17d/0x1c0
 write_page+0x61/0xa0
 swap_write_page+0x4b/0x1f0
 swsusp_write+0x2f9/0x3d0
 hibernate.cold.10+0x108/0x231
 state_store+0xf7/0x100
 kobj_attr_store+0x37/0x50
 sysfs_kf_write+0x87/0xa0
 kernfs_fop_write+0x186/0x240
 __vfs_write+0x4d/0x90
 vfs_write+0xfa/0x260
 ksys_write+0xb9/0x1a0
 __x64_sys_write+0x43/0x50
 do_syscall_64+0x71/0x210
 entry_SYSCALL_64_after_hwframe+0x49/0xbe

Hence revert commit 21e6ba3f0e02.

Cc: Pavel Machek <pavel@ucw.cz>
Reported-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/scsi.c
drivers/scsi/scsi_pm.c
drivers/scsi/scsi_priv.h
drivers/scsi/sd.c

index 41b25486e303afcdf7d63debdac0e92995de1036..99a7b9f520ae28eab51ff410796cf96e41b34384 100644 (file)
@@ -85,6 +85,19 @@ unsigned int scsi_logging_level;
 EXPORT_SYMBOL(scsi_logging_level);
 #endif
 
+/* sd, scsi core and power management need to coordinate flushing async actions */
+ASYNC_DOMAIN(scsi_sd_probe_domain);
+EXPORT_SYMBOL(scsi_sd_probe_domain);
+
+/*
+ * Separate domain (from scsi_sd_probe_domain) to maximize the benefit of
+ * asynchronous system resume operations.  It is marked 'exclusive' to avoid
+ * being included in the async_synchronize_full() that is invoked by
+ * dpm_resume()
+ */
+ASYNC_DOMAIN_EXCLUSIVE(scsi_sd_pm_domain);
+EXPORT_SYMBOL(scsi_sd_pm_domain);
+
 /**
  * scsi_put_command - Free a scsi command block
  * @cmd: command block to free
@@ -807,6 +820,7 @@ static void __exit exit_scsi(void)
        scsi_exit_devinfo();
        scsi_exit_procfs();
        scsi_exit_queue();
+       async_unregister_domain(&scsi_sd_probe_domain);
 }
 
 subsys_initcall(init_scsi);
index 560baaad71d5c1a76410544c888b234a0791c562..7639df91b1108495d9af0ba9b8133d51450e759d 100644 (file)
@@ -55,6 +55,9 @@ static int scsi_dev_type_suspend(struct device *dev,
        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
        int err;
 
+       /* flush pending in-flight resume operations, suspend is synchronous */
+       async_synchronize_full_domain(&scsi_sd_pm_domain);
+
        err = scsi_device_quiesce(to_scsi_device(dev));
        if (err == 0) {
                err = cb(dev, pm);
@@ -151,7 +154,18 @@ static int scsi_bus_resume_common(struct device *dev,
        else
                fn = NULL;
 
-       if (!fn) {
+       if (fn) {
+               async_schedule_domain(fn, dev, &scsi_sd_pm_domain);
+
+               /*
+                * If a user has disabled async probing a likely reason
+                * is due to a storage enclosure that does not inject
+                * staggered spin-ups.  For safety, make resume
+                * synchronous as well in that case.
+                */
+               if (strncmp(scsi_scan_type, "async", 5) != 0)
+                       async_synchronize_full_domain(&scsi_sd_pm_domain);
+       } else {
                pm_runtime_disable(dev);
                pm_runtime_set_active(dev);
                pm_runtime_enable(dev);
@@ -161,7 +175,11 @@ static int scsi_bus_resume_common(struct device *dev,
 
 static int scsi_bus_prepare(struct device *dev)
 {
-       if (scsi_is_host_device(dev)) {
+       if (scsi_is_sdev_device(dev)) {
+               /* sd probing uses async_schedule.  Wait until it finishes. */
+               async_synchronize_full_domain(&scsi_sd_probe_domain);
+
+       } else if (scsi_is_host_device(dev)) {
                /* Wait until async scanning is finished */
                scsi_complete_async_scans();
        }
index b1edf15704c0fe08bd53aa62a5368b3ffd857db4..5f21547b2ad26bce1e5385e5720279ae8ff8a563 100644 (file)
@@ -174,6 +174,9 @@ static inline int scsi_autopm_get_host(struct Scsi_Host *h) { return 0; }
 static inline void scsi_autopm_put_host(struct Scsi_Host *h) {}
 #endif /* CONFIG_PM */
 
+extern struct async_domain scsi_sd_pm_domain;
+extern struct async_domain scsi_sd_probe_domain;
+
 /* scsi_dh.c */
 #ifdef CONFIG_SCSI_DH
 void scsi_dh_add_device(struct scsi_device *sdev);
index f29c0ca8a5f18e1dd0be9262bde25872bb4bb610..2b2bc4b49d78a36c737cd9e70666b900ec0fc2b2 100644 (file)
@@ -567,7 +567,6 @@ static struct scsi_driver sd_template = {
                .name           = "sd",
                .owner          = THIS_MODULE,
                .probe          = sd_probe,
-               .probe_type     = PROBE_PREFER_ASYNCHRONOUS,
                .remove         = sd_remove,
                .shutdown       = sd_shutdown,
                .pm             = &sd_pm_ops,
@@ -3285,8 +3284,12 @@ static int sd_format_disk_name(char *prefix, int index, char *buf, int buflen)
        return 0;
 }
 
-static void sd_probe_part2(struct scsi_disk *sdkp)
+/*
+ * The asynchronous part of sd_probe
+ */
+static void sd_probe_async(void *data, async_cookie_t cookie)
 {
+       struct scsi_disk *sdkp = data;
        struct scsi_device *sdp;
        struct gendisk *gd;
        u32 index;
@@ -3340,6 +3343,7 @@ static void sd_probe_part2(struct scsi_disk *sdkp)
        sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n",
                  sdp->removable ? "removable " : "");
        scsi_autopm_put_device(sdp);
+       put_device(&sdkp->dev);
 }
 
 /**
@@ -3431,7 +3435,8 @@ static int sd_probe(struct device *dev)
        get_device(dev);
        dev_set_drvdata(dev, sdkp);
 
-       sd_probe_part2(sdkp);
+       get_device(&sdkp->dev); /* prevent release before async_schedule */
+       async_schedule_domain(sd_probe_async, sdkp, &scsi_sd_probe_domain);
 
        return 0;
 
@@ -3466,6 +3471,8 @@ static int sd_remove(struct device *dev)
        devt = disk_devt(sdkp->disk);
        scsi_autopm_get_device(sdkp->device);
 
+       async_synchronize_full_domain(&scsi_sd_pm_domain);
+       async_synchronize_full_domain(&scsi_sd_probe_domain);
        device_del(&sdkp->dev);
        del_gendisk(sdkp->disk);
        sd_shutdown(dev);