Commit 8d077ede authored by Bart Van Assche's avatar Bart Van Assche Committed by Martin K. Petersen

scsi: ufs: Optimize the command queueing code

Remove the clock scaling lock from ufshcd_queuecommand() since it is a
performance bottleneck. Instead check the SCSI device budget bitmaps in the
code that waits for ongoing ufshcd_queuecommand() calls. A bit is set in
sdev->budget_map just before scsi_queue_rq() is called and a bit is cleared
from that bitmap if scsi_queue_rq() does not submit the request or after
the request has finished. See also the blk_mq_{get,put}_dispatch_budget()
calls in the block layer.

There is no risk for a livelock since the block layer delays queue reruns
if queueing a request fails because the SCSI host has been blocked.

Link: https://lore.kernel.org/r/20211203231950.193369-17-bvanassche@acm.org
Cc: Asutosh Das (asd) <asutoshd@codeaurora.org>
Reviewed-by: default avatarAsutosh Das <asutoshd@codeaurora.org>
Signed-off-by: default avatarBart Van Assche <bvanassche@acm.org>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 5675c381
...@@ -1070,13 +1070,31 @@ static bool ufshcd_is_devfreq_scaling_required(struct ufs_hba *hba, ...@@ -1070,13 +1070,31 @@ static bool ufshcd_is_devfreq_scaling_required(struct ufs_hba *hba,
return false; return false;
} }
/*
* Determine the number of pending commands by counting the bits in the SCSI
* device budget maps. This approach has been selected because a bit is set in
* the budget map before scsi_host_queue_ready() checks the host_self_blocked
* flag. The host_self_blocked flag can be modified by calling
* scsi_block_requests() or scsi_unblock_requests().
*/
static u32 ufshcd_pending_cmds(struct ufs_hba *hba)
{
struct scsi_device *sdev;
u32 pending = 0;
shost_for_each_device(sdev, hba->host)
pending += sbitmap_weight(&sdev->budget_map);
return pending;
}
static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
u64 wait_timeout_us) u64 wait_timeout_us)
{ {
unsigned long flags; unsigned long flags;
int ret = 0; int ret = 0;
u32 tm_doorbell; u32 tm_doorbell;
u32 tr_doorbell; u32 tr_pending;
bool timeout = false, do_last_check = false; bool timeout = false, do_last_check = false;
ktime_t start; ktime_t start;
...@@ -1094,8 +1112,8 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, ...@@ -1094,8 +1112,8 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
} }
tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL); tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL);
tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); tr_pending = ufshcd_pending_cmds(hba);
if (!tm_doorbell && !tr_doorbell) { if (!tm_doorbell && !tr_pending) {
timeout = false; timeout = false;
break; break;
} else if (do_last_check) { } else if (do_last_check) {
...@@ -1115,12 +1133,12 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, ...@@ -1115,12 +1133,12 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
do_last_check = true; do_last_check = true;
} }
spin_lock_irqsave(hba->host->host_lock, flags); spin_lock_irqsave(hba->host->host_lock, flags);
} while (tm_doorbell || tr_doorbell); } while (tm_doorbell || tr_pending);
if (timeout) { if (timeout) {
dev_err(hba->dev, dev_err(hba->dev,
"%s: timedout waiting for doorbell to clear (tm=0x%x, tr=0x%x)\n", "%s: timedout waiting for doorbell to clear (tm=0x%x, tr=0x%x)\n",
__func__, tm_doorbell, tr_doorbell); __func__, tm_doorbell, tr_pending);
ret = -EBUSY; ret = -EBUSY;
} }
out: out:
...@@ -2681,9 +2699,6 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) ...@@ -2681,9 +2699,6 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
WARN_ONCE(tag < 0, "Invalid tag %d\n", tag); WARN_ONCE(tag < 0, "Invalid tag %d\n", tag);
if (!down_read_trylock(&hba->clk_scaling_lock))
return SCSI_MLQUEUE_HOST_BUSY;
/* /*
* Allows the UFS error handler to wait for prior ufshcd_queuecommand() * Allows the UFS error handler to wait for prior ufshcd_queuecommand()
* calls. * calls.
...@@ -2772,8 +2787,6 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) ...@@ -2772,8 +2787,6 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
out: out:
rcu_read_unlock(); rcu_read_unlock();
up_read(&hba->clk_scaling_lock);
if (ufs_trigger_eh()) { if (ufs_trigger_eh()) {
unsigned long flags; unsigned long flags;
......
...@@ -778,6 +778,7 @@ struct ufs_hba_monitor { ...@@ -778,6 +778,7 @@ struct ufs_hba_monitor {
* @clk_list_head: UFS host controller clocks list node head * @clk_list_head: UFS host controller clocks list node head
* @pwr_info: holds current power mode * @pwr_info: holds current power mode
* @max_pwr_info: keeps the device max valid pwm * @max_pwr_info: keeps the device max valid pwm
* @clk_scaling_lock: used to serialize device commands and clock scaling
* @desc_size: descriptor sizes reported by device * @desc_size: descriptor sizes reported by device
* @urgent_bkops_lvl: keeps track of urgent bkops level for device * @urgent_bkops_lvl: keeps track of urgent bkops level for device
* @is_urgent_bkops_lvl_checked: keeps track if the urgent bkops level for * @is_urgent_bkops_lvl_checked: keeps track if the urgent bkops level for
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment