Although the power management code never calls the system-wide and runtime
suspend callbacks concurrently, runtime power state changes can happen
while the system is being suspended or resumed. See also the dpm_suspend()
and dpm_resume() calls in hibernation_snapshot(). Make sure the sd driver
supports this. This patch avoids that the following call trace is reported
during system-wide suspend:

WARNING: CPU: 0 PID: 701 at drivers/scsi/scsi_lib.c:3047 
scsi_device_quiesce+0x4b/0xd0
Workqueue: events_unbound async_run_entry_fn
RIP: 0010:scsi_device_quiesce+0x4b/0xd0
Call Trace:
 scsi_bus_suspend_common+0x71/0xe0
 scsi_bus_freeze+0x15/0x20
 dpm_run_callback+0x88/0x360
 __device_suspend+0x1c4/0x840
 async_suspend+0x1f/0xb0
 async_run_entry_fn+0x6e/0x2c0
 process_one_work+0x4ae/0xa20
 worker_thread+0x63/0x5a0
 kthread+0x1cf/0x1f0
 ret_from_fork+0x24/0x30

Fixes: cd84a62e0078 ("block, scsi: Change the preempt-only flag into a counter")
Cc: Lee Duncan <ldun...@suse.com>
Cc: Hannes Reinecke <h...@suse.com>
Cc: Luis Chamberlain <mcg...@kernel.org>
Cc: Johannes Thumshirn <jthumsh...@suse.de>
Cc: Christoph Hellwig <h...@lst.de>
Cc: Greg Kroah-Hartman <gre...@linuxfoundation.org>
Cc: Dan Williams <dan.j.willi...@intel.com>
Cc: sta...@vger.kernel.org
Signed-off-by: Bart Van Assche <bvanass...@acm.org>
---
 drivers/scsi/scsi_lib.c    | 15 ++++++---------
 include/scsi/scsi_device.h |  1 -
 2 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 7db3c5fae469..6c18a61176e5 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -3052,11 +3052,12 @@ scsi_device_quiesce(struct scsi_device *sdev)
        int err;
 
        /*
-        * It is allowed to call scsi_device_quiesce() multiple times from
-        * the same context but concurrent scsi_device_quiesce() calls are
-        * not allowed.
+        * Since all scsi_device_quiesce() and scsi_device_resume() calls
+        * are serialized it is safe here to check the device state without
+        * holding the SCSI device state mutex.
         */
-       WARN_ON_ONCE(sdev->quiesced_by && sdev->quiesced_by != current);
+       if (sdev->sdev_state == SDEV_QUIESCE)
+               return 0;
 
        blk_set_preempt_only(q);
 
@@ -3072,9 +3073,7 @@ scsi_device_quiesce(struct scsi_device *sdev)
 
        mutex_lock(&sdev->state_mutex);
        err = scsi_device_set_state(sdev, SDEV_QUIESCE);
-       if (err == 0)
-               sdev->quiesced_by = current;
-       else
+       if (err)
                blk_clear_preempt_only(q);
        mutex_unlock(&sdev->state_mutex);
 
@@ -3098,8 +3097,6 @@ void scsi_device_resume(struct scsi_device *sdev)
         * device deleted during suspend)
         */
        mutex_lock(&sdev->state_mutex);
-       WARN_ON_ONCE(!sdev->quiesced_by);
-       sdev->quiesced_by = NULL;
        blk_clear_preempt_only(sdev->request_queue);
        if (sdev->sdev_state == SDEV_QUIESCE)
                scsi_device_set_state(sdev, SDEV_RUNNING);
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index 202f4d6a4342..ef86c8adc5d5 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -226,7 +226,6 @@ struct scsi_device {
        unsigned char           access_state;
        struct mutex            state_mutex;
        enum scsi_device_state sdev_state;
-       struct task_struct      *quiesced_by;
        unsigned long           sdev_data[0];
 } __attribute__((aligned(sizeof(unsigned long))));
 
-- 
2.19.1.568.g152ad8e336-goog

Reply via email to