Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit af07e4dd authored by Bart Van Assche's avatar Bart Van Assche Committed by Greg Kroah-Hartman
Browse files

scsi: block: Fix a race in the runtime power management code

commit fa4d0f1992a96f6d7c988ef423e3127e613f6ac9 upstream.

With the current implementation the following race can happen:

 * blk_pre_runtime_suspend() calls blk_freeze_queue_start() and
   blk_mq_unfreeze_queue().

 * blk_queue_enter() calls blk_queue_pm_only() and that function returns
   true.

 * blk_queue_enter() calls blk_pm_request_resume() and that function does
   not call pm_request_resume() because the queue runtime status is
   RPM_ACTIVE.

 * blk_pre_runtime_suspend() changes the queue status into RPM_SUSPENDING.

Fix this race by changing the queue runtime status into RPM_SUSPENDING
before switching q_usage_counter to atomic mode.

Link: https://lore.kernel.org/r/20201209052951.16136-2-bvanassche@acm.org


Fixes: 986d413b ("blk-mq: Enable support for runtime power management")
Cc: Ming Lei <ming.lei@redhat.com>
Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Cc: stable <stable@vger.kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarHannes Reinecke <hare@suse.de>
Reviewed-by: default avatarJens Axboe <axboe@kernel.dk>
Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Acked-by: default avatarStanley Chu <stanley.chu@mediatek.com>
Co-developed-by: default avatarCan Guo <cang@codeaurora.org>
Signed-off-by: default avatarCan Guo <cang@codeaurora.org>
Signed-off-by: default avatarBart Van Assche <bvanassche@acm.org>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 9ce7ac5e
Loading
Loading
Loading
Loading
+9 −6
Original line number Diff line number Diff line
@@ -67,6 +67,10 @@ int blk_pre_runtime_suspend(struct request_queue *q)

	WARN_ON_ONCE(q->rpm_status != RPM_ACTIVE);

	spin_lock_irq(&q->queue_lock);
	q->rpm_status = RPM_SUSPENDING;
	spin_unlock_irq(&q->queue_lock);

	/*
	 * Increase the pm_only counter before checking whether any
	 * non-PM blk_queue_enter() calls are in progress to avoid that any
@@ -89,15 +93,14 @@ int blk_pre_runtime_suspend(struct request_queue *q)
	/* Switch q_usage_counter back to per-cpu mode. */
	blk_mq_unfreeze_queue(q);

	if (ret < 0) {
		spin_lock_irq(&q->queue_lock);
	if (ret < 0)
		q->rpm_status = RPM_ACTIVE;
		pm_runtime_mark_last_busy(q->dev);
	else
		q->rpm_status = RPM_SUSPENDING;
		spin_unlock_irq(&q->queue_lock);

	if (ret)
		blk_clear_pm_only(q);
	}

	return ret;
}