Make concurrent invocations of scsi_device_set_state() safe.
Signed-off-by: Bart Van Assche <[email protected]>
Cc: James Bottomley <[email protected]>
Cc: Tejun Heo <[email protected]>
Cc: Hannes Reinecke <[email protected]>
Cc: Mike Christie <[email protected]>
---
drivers/scsi/scsi_lib.c | 24 ++++++++++++------------
drivers/scsi/scsi_sysfs.c | 9 ++++++++-
2 files changed, 20 insertions(+), 13 deletions(-)
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index cfc420b..f3d6e0d 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -2072,7 +2072,9 @@ EXPORT_SYMBOL(scsi_test_unit_ready);
* @state: state to change to.
*
* Returns zero if unsuccessful or an error if the requested
- * transition is illegal.
+ * transition is illegal. It is the responsibility of the caller to make
+ * sure that a call of this function does not race with other code that
+ * accesses the device state, e.g. by holding the host lock.
*/
int
scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state)
@@ -2433,21 +2435,19 @@ scsi_internal_device_block(struct scsi_device *sdev)
unsigned long flags;
int err = 0;
+ spin_lock_irqsave(q->queue_lock, flags);
err = scsi_device_set_state(sdev, SDEV_BLOCK);
- if (err) {
+ if (err)
err = scsi_device_set_state(sdev, SDEV_CREATED_BLOCK);
- if (err)
- return err;
+ if (err == 0) {
+ /*
+ * The device has transitioned to SDEV_BLOCK. Stop the
+ * block layer from calling the midlayer with this device's
+ * request queue.
+ */
+ blk_stop_queue(q);
}
-
- /*
- * The device has transitioned to SDEV_BLOCK. Stop the
- * block layer from calling the midlayer with this device's
- * request queue.
- */
- spin_lock_irqsave(q->queue_lock, flags);
- blk_stop_queue(q);
spin_unlock_irqrestore(q->queue_lock, flags);
return 0;
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 89195e2..3f5197e 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -960,8 +960,12 @@ void __scsi_remove_device(struct scsi_device *sdev)
unsigned long flags;
if (sdev->is_visible) {
- if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0)
+ spin_lock_irqsave(shost->host_lock, flags);
+ if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0) {
+ spin_unlock_irqrestore(shost->host_lock, flags);
return;
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
bsg_unregister_queue(sdev->request_queue);
device_unregister(&sdev->sdev_dev);
@@ -975,7 +979,10 @@ void __scsi_remove_device(struct scsi_device *sdev)
* scsi_run_queue() invocations have finished before tearing down the
* device.
*/
+ spin_lock_irqsave(shost->host_lock, flags);
scsi_device_set_state(sdev, SDEV_DEL);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
blk_cleanup_queue(sdev->request_queue);
cancel_work_sync(&sdev->requeue_work);
--
1.7.10.4
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html