Two minor fixes for UFS driver
cover Two minor fixes for UFS driver
This series contains two minor fixes, one for clk gating and one for PMC/UIC cmd completion timeout.
Can Guo (2):
scsi: ufs: Fix unbalanced scsi_block_reqs_cnt caused by ufshcd_hold()
scsi: ufs: Try to save power mode change and UIC cmd completion
timeout
drivers/scsi/ufs/ufshcd.c | 32 +++++++++++++++++++++++++++----- drivers/scsi/ufs/ufshcd.h | 2 ++ 2 files changed, 29 insertions(+), 5 deletions(-)
Signature · 1 line
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
PATCH 1/2 scsi: ufs: Fix unbalanced scsi_block_reqs_cnt caused by ufshcd_hold()
The scsi_block_reqs_cnt increased in ufshcd_hold() is supposed to be decreased back in ufshcd_ungate_work() in a paired way. However, if specific ufshcd_hold/release sequences are met, it is possible that scsi_block_reqs_cnt is increased twice but only one ungate work is queued. To make sure scsi_block_reqs_cnt is handled by ufshcd_hold() and ufshcd_ungate_work() in a paired way, increase it only if queue_work() returns true.
--- drivers/scsi/ufs/ufshcd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/scsi/ufs/ufshcd.c +3 -3
@@ -1634,12 +1634,12 @@ int ufshcd_hold(struct ufs_hba *hba, bool async)*/ /* fallthrough */ case CLKS_OFF : -ufshcd_scsi_block_requests (hba); hba->clk_gating.state =REQ_CLKS_ON ;trace_ufshcd_clk_gating (dev_name (hba->dev), hba->clk_gating.state); -queue_work (hba->clk_gating.clk_gating_workq, - &hba->clk_gating.ungate_work); +if (queue_work (hba->clk_gating.clk_gating_workq, + &hba->clk_gating.ungate_work)) +ufshcd_scsi_block_requests (hba);/* * fall through to check if we should wait for this * work to be done or not.
Signature · 1 line
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
Re: [PATCH v1 1/2] scsi: ufs: Fix unbalanced scsi_block_reqs_cnt caused by ufshcd_hold()
Hi Can,
Quoted 5 lines · Can Guo
The
scsi_block_reqs_cntincreased inufshcd_hold()is supposed to be decreased back inufshcd_ungate_work()in a paired way. However, if specific ufshcd_hold/release sequences are met, it is possible thatscsi_block_reqs_cntis increased twice but only one ungate work is queued. To make surescsi_block_reqs_cntis handled byufshcd_hold()and
Just curious that how could this be possible? Would you have some failed examples?
Quoted 19 lines
ufshcd_ungate_work()in a paired way, increase it only ifqueue_work()returns true.Signed-off-by Can Guo <[email protected]>Reviewed-by Hongwu Su <[email protected]>--- drivers/scsi/ufs/ufshcd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)drivers/scsi/ufs/ufshcd.c +0
-1@@ -1634,12 +1634,12 @@ int ufshcd_hold(struct ufs_hba *hba, bool async)*/ /* fallthrough */ case CLKS_OFF : -ufshcd_scsi_block_requests (hba); hba->clk_gating.state =REQ_CLKS_ON ;
drivers/scsi/ufs/ufshcd.c int ufshcd_hold(struct ufs_hba *hba, bool async) · line 1645
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +3
-2trace_ufshcd_clk_gating (dev_name (hba->dev), hba->clk_gating.state); -queue_work (hba->clk_gating.clk_gating_workq, - &hba->clk_gating.ungate_work); +if (queue_work (hba->clk_gating.clk_gating_workq, + &hba->clk_gating.ungate_work)) +ufshcd_scsi_block_requests (hba);/* * fall through to check if we should wait for this * work to be done or not.
Thanks,
Stanley Chu
Re: [PATCH v1 1/2] scsi: ufs: Fix unbalanced scsi_block_reqs_cnt caused by ufshcd_hold()
Quoted 12 lines · Stanley Chu
Hi Can,
Quoted 5 lines · Can Guo
The
scsi_block_reqs_cntincreased inufshcd_hold()is supposed to be decreased back inufshcd_ungate_work()in a paired way. However, if specific ufshcd_hold/release sequences are met, it is possible thatscsi_block_reqs_cntis increased twice but only one ungate work is queued. To make surescsi_block_reqs_cntis handled byufshcd_hold()andJust curious that how could this be possible? Would you have some failed examples?
[1] One gate_work() is in the workqueue, not yet executed, now clk state == REQ_CLKS_OFF.
[2] ufshcd_queuecommand() calls ufshcd_hold(async == ture) -> active_req++ -> scsi_block_reqs_cnt++ -> REQ_CLKS_ON -> queue ungate work -> active_req-- -> return -EAGAIN.
[3] Now gate_work() starts to run, but since the clk state is REQ_CLKS_ON, gate_work() just sets clk state to CLKS_ON and bail.
[3] Someone calls ufshcd_hold(async == false) -> do something -> ufshcd_release() -> clk state is changed to REQ_CLKS_OFF. Note that, till now, ungate_work() is still in the work queue, not executed yet.
[4] Now, if someone calls ufshcd_hold(), we will hit the issue.
Above sequence is a very common clk gate/ungate sequence. The issue is because ungate_work is queued but cannot be executed in time. In my case, I see the ungate_work is somehow delayed for about 150ms. This change has been tested by customers on multiple platforms. And you can tell from the code that it won't break anything. :)
Thanks,
Can Guo.
Quoted 33 lines
Quoted 29 lines
ufshcd_ungate_work()in a paired way, increase it only ifqueue_work()returns true.Signed-off-by Can Guo <[email protected]>Reviewed-by Hongwu Su <[email protected]>--- drivers/scsi/ufs/ufshcd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)drivers/scsi/ufs/ufshcd.c +3
-3@@ -1634,12 +1634,12 @@ int ufshcd_hold(struct ufs_hba *hba, boolasync) */ /* fallthrough */ case CLKS_OFF : -ufshcd_scsi_block_requests (hba); hba->clk_gating.state =REQ_CLKS_ON ;trace_ufshcd_clk_gating (dev_name (hba->dev), hba->clk_gating.state); -queue_work (hba->clk_gating.clk_gating_workq, - &hba->clk_gating.ungate_work); +if (queue_work (hba->clk_gating.clk_gating_workq, + &hba->clk_gating.ungate_work)) +ufshcd_scsi_block_requests (hba);/* * fall through to check if we should wait for this Quoted 1 line
* work to be done or not.
Thanks,
Stanley Chu
Stanley Chu
Reviewed-byRe: [PATCH v1 1/2] scsi: ufs: Fix unbalanced scsi_block_reqs_cnt caused by ufshcd_hold()
Hi Can,
Quoted 28 lines · Can Guo
Quoted 10 lines · Stanley Chu
Hi Can,
Quoted 6 lines · Can Guo
The
scsi_block_reqs_cntincreased inufshcd_hold()is supposed to be decreased back inufshcd_ungate_work()in a paired way. However, if specific ufshcd_hold/release sequences are met, it is possible thatscsi_block_reqs_cntis increased twice but only one ungate work is queued. To make surescsi_block_reqs_cntis handled byufshcd_hold()andQuoted 4 lines
Just curious that how could this be possible? Would you have some failed
examples?[1] One
gate_work()is in the workqueue, not yet executed, now clk state ==REQ_CLKS_OFF.
[2]ufshcd_queuecommand()callsufshcd_hold(async == ture)-> active_req++ -> scsi_block_reqs_cnt++ ->REQ_CLKS_ON-> queue ungate work -> active_req-- -> return-EAGAIN.
[3] Nowgate_work()starts to run, but since the clk state isREQ_CLKS_ON,gate_work()just sets clk state toCLKS_ONand bail.
[3] Someone callsufshcd_hold(async == false)-> do something ->ufshcd_release()-> clk state is changed toREQ_CLKS_OFF. Note that, till now,ungate_work()is still in the work queue, not executed yet.
[4] Now, if someone callsufshcd_hold(), we will hit the issue.
Quoted 5 lines
Above sequence is a very common clk gate/ungate sequence. The issue is because
ungate_workis queued but cannot be executed in time. In my case, I see theungate_workis somehow delayed for about 150ms. This change has been tested by customers on multiple platforms. And you can tell from the code that it won't break anything. :)
Thanks so much for the details. Looks good to me.
Quoted 38 lines
Thanks,
Can Guo.
Quoted 33 lines
Quoted 29 lines
ufshcd_ungate_work()in a paired way, increase it only ifqueue_work()returns true.Signed-off-by Can Guo <[email protected]>Reviewed-by Hongwu Su <[email protected]>--- drivers/scsi/ufs/ufshcd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)drivers/scsi/ufs/ufshcd.c +3
-3@@ -1634,12 +1634,12 @@ int ufshcd_hold(struct ufs_hba *hba, boolasync) */ /* fallthrough */ case CLKS_OFF : -ufshcd_scsi_block_requests (hba); hba->clk_gating.state =REQ_CLKS_ON ;trace_ufshcd_clk_gating (dev_name (hba->dev), hba->clk_gating.state); -queue_work (hba->clk_gating.clk_gating_workq, - &hba->clk_gating.ungate_work); +if (queue_work (hba->clk_gating.clk_gating_workq, + &hba->clk_gating.ungate_work)) +ufshcd_scsi_block_requests (hba);/* * fall through to check if we should wait for this Quoted 1 line
* work to be done or not.
Thanks,
Stanley Chu
RE: [EXT] [PATCH v1 1/2] scsi: ufs: Fix unbalanced scsi_block_reqs_cnt caused by ufshcd_hold()
In reply to Can GuoQuoted 3 lines
Signed-off-by Can Guo <[email protected]>Reviewed-by Hongwu Su <[email protected]>
Re: [PATCH v1 1/2] scsi: ufs: Fix unbalanced scsi_block_reqs_cnt caused by ufshcd_hold()
In reply to Can GuoQuoted 11 lines · Can Guo
The
scsi_block_reqs_cntincreased inufshcd_hold()is supposed to be decreased back inufshcd_ungate_work()in a paired way. However, if specific ufshcd_hold/release sequences are met, it is possible thatscsi_block_reqs_cntis increased twice but only one ungate work is queued. To make surescsi_block_reqs_cntis handled byufshcd_hold()andufshcd_ungate_work()in a paired way, increase it only ifqueue_work()returns true.Signed-off-by Can Guo <[email protected]>Reviewed-by Hongwu Su <[email protected]>---
Quoted 24 lines
drivers/scsi/ufs/ufshcd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)drivers/scsi/ufs/ufshcd.c +3
-3@@ -1634,12 +1634,12 @@ int ufshcd_hold(struct ufs_hba *hba, bool async)*/ /* fallthrough */ case CLKS_OFF : -ufshcd_scsi_block_requests (hba); hba->clk_gating.state =REQ_CLKS_ON ;trace_ufshcd_clk_gating (dev_name (hba->dev), hba->clk_gating.state); -queue_work (hba->clk_gating.clk_gating_workq, - &hba->clk_gating.ungate_work); +if (queue_work (hba->clk_gating.clk_gating_workq, + &hba->clk_gating.ungate_work)) +ufshcd_scsi_block_requests (hba);/* * fall through to check if we should wait for this * work to be done or not.
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, Linux Foundation Collaborative Project
PATCH 2/2 scsi: ufs: Try to save power mode change and UIC cmd completion timeout
In reply to Can GuoUse the uic_cmd->cmd_active as a flag to track the lifecycle of an UIC cmd. The flag is set before send the UIC cmd and cleared in IRQ handler. When a PMC or UIC cmd completion timeout happens, if the flag is not set, instead of returning timeout error, we still treat it as a successful operation. This is to deal with the scenario in which completion has been raised but the one waiting for the completion cannot be awaken in time due to kernel scheduling problem.
--- drivers/scsi/ufs/ufshcd.c | 26 ++++++++++++++++++++++++-- drivers/scsi/ufs/ufshcd.h | 2 ++ 2 files changed, 26 insertions(+), 2 deletions(-)
drivers/scsi/ufs/ufshcd.c +24 -2
@@ -2122,10 +2122,20 @@ ufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)unsigned long flags;if (wait_for_completion_timeout (&uic_cmd->done, -msecs_to_jiffies (UIC_CMD_TIMEOUT ))) +msecs_to_jiffies (UIC_CMD_TIMEOUT ))) { ret = uic_cmd->argument2 &MASK_UIC_COMMAND_RESULT ; -else + }else { ret = -ETIMEDOUT ; +dev_err (hba->dev, +"uic cmd 0x%x with arg3 0x%x completion timeout\n" , + uic_cmd->command, uic_cmd->argument3); + +if (!uic_cmd->cmd_active) { +dev_err (hba->dev,"%s: UIC cmd has been completed, return the result\n" , + __func__); + ret = uic_cmd->argument2 &MASK_UIC_COMMAND_RESULT ; + } + }spin_lock_irqsave (hba->host->host_lock, flags); hba->active_uic_cmd =NULL ;
@@ -2157,6 +2167,7 @@ __ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd,if (completion)init_completion (&uic_cmd->done); + uic_cmd->cmd_active =1 ;ufshcd_dispatch_uic_cmd (hba, uic_cmd);return 0 ;
@@ -3828,10 +3839,18 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd)dev_err (hba->dev,"pwr ctrl cmd 0x%x with mode 0x%x completion timeout\n" , cmd->command, cmd->argument3); + +if (!cmd->cmd_active) { +dev_err (hba->dev,"%s: Power Mode Change operation has been completed, go check UPMCRS\n" , + __func__); + goto check_upmcrs; + } + ret = -ETIMEDOUT ; goto out; } +check_upmcrs: status =ufshcd_get_upmcrs (hba);if (status !=PWR_LOCAL ) { dev_err(hba->dev,
@@ -4923,11 +4942,14 @@ static irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)ufshcd_get_uic_cmd_result (hba); hba->active_uic_cmd->argument3 =ufshcd_get_dme_attr_val (hba); +if (!hba->uic_async_done) + hba->active_uic_cmd->cmd_active =0 ;complete (&hba->active_uic_cmd->done); retval =IRQ_HANDLED ; }if ((intr_status &UFSHCD_UIC_PWR_MASK ) && hba->uic_async_done) { + hba->active_uic_cmd->cmd_active =0 ;complete (hba->uic_async_done); retval =IRQ_HANDLED ; }
drivers/scsi/ufs/ufshcd.h +2 -0
@@ -64,6 +64,7 @@ enum dev_cmd_type {* @argument1: UIC command argument 1 * @argument2: UIC command argument 2 * @argument3: UIC command argument 3 +* @cmd_active: Indicate if UIC command is outstanding * @done: UIC command completion */ struct uic_command {
@@ -71,6 +72,7 @@ struct uic_command {u32 argument1;u32 argument2;u32 argument3; +int cmd_active;struct completion done; };
Signature · 1 line
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
Re: [PATCH v1 2/2] scsi: ufs: Try to save power mode change and UIC cmd completion timeout
Hi Can,
Except for below nit, otherwise looks good to me.
Quoted 69 lines · Can Guo
Use the
uic_cmd->cmd_activeas a flag to track the lifecycle of an UIC cmd. The flag is set before send the UIC cmd and cleared in IRQ handler. When a PMC or UIC cmd completion timeout happens, if the flag is not set, instead of returning timeout error, we still treat it as a successful operation. This is to deal with the scenario in which completion has been raised but the one waiting for the completion cannot be awaken in time due to kernel scheduling problem.Signed-off-by Can Guo <[email protected]>--- drivers/scsi/ufs/ufshcd.c | 26 ++++++++++++++++++++++++-- drivers/scsi/ufs/ufshcd.h | 2 ++ 2 files changed, 26 insertions(+), 2 deletions(-)drivers/scsi/ufs/ufshcd.c +21
-2@@ -2122,10 +2122,20 @@ ufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)unsigned long flags;if (wait_for_completion_timeout (&uic_cmd->done, -msecs_to_jiffies (UIC_CMD_TIMEOUT ))) +msecs_to_jiffies (UIC_CMD_TIMEOUT ))) { ret = uic_cmd->argument2 &MASK_UIC_COMMAND_RESULT ; -else + }else { ret = -ETIMEDOUT ; +dev_err (hba->dev, +"uic cmd 0x%x with arg3 0x%x completion timeout\n" , + uic_cmd->command, uic_cmd->argument3); + +if (!uic_cmd->cmd_active) { +dev_err (hba->dev,"%s: UIC cmd has been completed, return the result\n" , + __func__); + ret = uic_cmd->argument2 &MASK_UIC_COMMAND_RESULT ; + } + }spin_lock_irqsave (hba->host->host_lock, flags); hba->active_uic_cmd =NULL ;@@ -2157,6 +2167,7 @@ __ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd,if (completion)init_completion (&uic_cmd->done); + uic_cmd->cmd_active =1 ;ufshcd_dispatch_uic_cmd (hba, uic_cmd);return 0 ;@@ -3828,10 +3839,18 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd)dev_err (hba->dev,"pwr ctrl cmd 0x%x with mode 0x%x completion timeout\n" , cmd->command, cmd->argument3); + +if (!cmd->cmd_active) { +dev_err (hba->dev,"%s: Power Mode Change operation has been completed, go check UPMCRS\n" , + __func__); + goto check_upmcrs; + } + ret = -ETIMEDOUT ; goto out; } +check_upmcrs: status =ufshcd_get_upmcrs (hba);if (status !=PWR_LOCAL ) { dev_err(hba->dev,
drivers/scsi/ufs/ufshcd.c static irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status) · line 4945
Quoted 4 lines
drivers/scsi/ufs/ufshcd.c +1
-0@@ -4923,11 +4942,14 @@ static irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)ufshcd_get_uic_cmd_result (hba); hba->active_uic_cmd->argument3 =ufshcd_get_dme_attr_val (hba); +if (!hba->uic_async_done)
Is this check necessary?
Quoted 23 lines
drivers/scsi/ufs/ufshcd.c +2
-0+ hba->active_uic_cmd->cmd_active =0 ;complete (&hba->active_uic_cmd->done); retval =IRQ_HANDLED ; }if ((intr_status &UFSHCD_UIC_PWR_MASK ) && hba->uic_async_done) { + hba->active_uic_cmd->cmd_active =0 ;complete (hba->uic_async_done); retval =IRQ_HANDLED ; }drivers/scsi/ufs/ufshcd.h +1
-0@@ -64,6 +64,7 @@ enum dev_cmd_type {* @argument1: UIC command argument 1 * @argument2: UIC command argument 2 * @argument3: UIC command argument 3 +* @cmd_active: Indicate if UIC command is outstanding * @done: UIC command completion */ struct uic_command {
drivers/scsi/ufs/ufshcd.h struct uic_command { · line 78
Quoted 7 lines
drivers/scsi/ufs/ufshcd.h +1
-0@@ -71,6 +72,7 @@ struct uic_command {u32 argument1;u32 argument2;u32 argument3; +int cmd_active;struct completion done; };
Thanks,
Stanley Chu
Re: [PATCH v1 2/2] scsi: ufs: Try to save power mode change and UIC cmd completion timeout
Hi Stanley,
Quoted 76 lines · Stanley Chu
Hi Can,
Except for below nit, otherwise looks good to me.
Reviewed-by Stanley Chu <[email protected]>Quoted 69 lines · Can Guo
Use the
uic_cmd->cmd_activeas a flag to track the lifecycle of an UIC cmd. The flag is set before send the UIC cmd and cleared in IRQ handler. When a PMC or UIC cmd completion timeout happens, if the flag is not set, instead of returning timeout error, we still treat it as a successful operation. This is to deal with the scenario in which completion has been raised but the one waiting for the completion cannot be awaken in time due to kernel scheduling problem.Signed-off-by Can Guo <[email protected]>--- drivers/scsi/ufs/ufshcd.c | 26 ++++++++++++++++++++++++-- drivers/scsi/ufs/ufshcd.h | 2 ++ 2 files changed, 26 insertions(+), 2 deletions(-)drivers/scsi/ufs/ufshcd.c +12
-2@@ -2122,10 +2122,20 @@ ufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)unsigned long flags;if (wait_for_completion_timeout (&uic_cmd->done, -msecs_to_jiffies (UIC_CMD_TIMEOUT ))) +msecs_to_jiffies (UIC_CMD_TIMEOUT ))) { ret = uic_cmd->argument2 &MASK_UIC_COMMAND_RESULT ; -else + }else { ret = -ETIMEDOUT ; +dev_err (hba->dev, +"uic cmd 0x%x with arg3 0x%x completion timeout\n" , + uic_cmd->command, uic_cmd->argument3); + +if (!uic_cmd->cmd_active) { +dev_err (hba->dev,"%s: UIC cmd has been completed, return the result\n", + __func__); + ret = uic_cmd->argument2 &MASK_UIC_COMMAND_RESULT ; + } + }drivers/scsi/ufs/ufshcd.c +1
-0spin_lock_irqsave (hba->host->host_lock, flags); hba->active_uic_cmd =NULL ;@@ -2157,6 +2167,7 @@ __ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd, if (completion) init_completion(&uic_cmd->done); + uic_cmd->cmd_active =1 ;ufshcd_dispatch_uic_cmd (hba, uic_cmd);drivers/scsi/ufs/ufshcd.c +7
-0return 0 ;@@ -3828,10 +3839,18 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd)dev_err (hba->dev,"pwr ctrl cmd 0x%x with mode 0x%x completion timeout\n" , cmd->command, cmd->argument3); + +if (!cmd->cmd_active) { + dev_err(hba->dev, "%s: Power Mode Change operation has been completed, go check UPMCRS\n", + __func__); + goto check_upmcrs; + } + ret = -ETIMEDOUT ; goto out; }
Quoted 13 lines
drivers/scsi/ufs/ufshcd.c static irqreturn_t ufshcd_uic_cmd_compl(struct · line 4946
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +1
-0+check_upmcrs: status =ufshcd_get_upmcrs (hba);drivers/scsi/ufs/ufshcd.c +1
-0if (status !=PWR_LOCAL ) { dev_err(hba->dev,@@ -4923,11 +4942,14 @@ static irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)ufshcd_get_uic_cmd_result (hba ); hba->active_uic_cmd->argument3 =ufshcd_get_dme_attr_val (hba); +if (!hba->uic_async_done)Is this check necessary?
Thanks for your quick response.
In the case of PMC, UIC cmd completion IRQ comes first, then power status change IRQ comes next. In this case, an UIC cmd's lifecyle ends only after the power status change IRQ comes [1].
I guess you may want to say that in current code since we have masked UIC cmd completion IRQ in the case of a PMC operation, so no need to check it here since we won't be here anyways before power status change IRQ comes. So, removing the check here definitely works, and then we won't even need below line as well.
if ((intr_status & UFSHCD_UIC_PWR_MASK) && hba->uic_async_done) {
+ hba->active_uic_cmd->cmd_active = 0;
complete(hba->uic_async_done);
retval = IRQ_HANDLED;If my guess is right, my opinion is that the current change may be more readable and comprehensive as it strictly follows my description in [1]. What do you think?
Thanks,
Can Guo.
Quoted 35 lines
Quoted 23 lines
drivers/scsi/ufs/ufshcd.c +2
-0+ hba->active_uic_cmd->cmd_active =0 ;complete (&hba->active_uic_cmd->done); retval =IRQ_HANDLED ; }if ((intr_status &UFSHCD_UIC_PWR_MASK ) && hba->uic_async_done) { + hba->active_uic_cmd->cmd_active =0 ;complete (hba->uic_async_done); retval =IRQ_HANDLED ;drivers/scsi/ufs/ufshcd.c +0
-0}drivers/scsi/ufs/ufshcd.h +1
-0@@ -64,6 +64,7 @@ enum dev_cmd_type {* @argument1: UIC command argument 1 * @argument2: UIC command argument 2 * @argument3: UIC command argument 3 +* @cmd_active: Indicate if UIC command is outstanding * @done: UIC command completion */ struct uic_command {drivers/scsi/ufs/ufshcd.h struct uic_command { · line 78
Quoted 7 lines
drivers/scsi/ufs/ufshcd.h +1
-0@@ -71,6 +72,7 @@ struct uic_command {u32 argument1;u32 argument2;u32 argument3; +int cmd_active;struct completion done; };Thanks,
Stanley Chu
Re: [PATCH v1 2/2] scsi: ufs: Try to save power mode change and UIC cmd completion timeout
Hi Can,
Quoted 105 lines · Can Guo
Hi Stanley,
Quoted 82 lines · Stanley Chu
Hi Can,
Except for below nit, otherwise looks good to me.
Reviewed-by Stanley Chu <[email protected]>Quoted 75 lines · Can Guo
Use the
uic_cmd->cmd_activeas a flag to track the lifecycle of an UIC cmd.
The flag is set before send the UIC cmd and cleared in IRQ handler. When a
PMC or UIC cmd completion timeout happens, if the flag is not set, instead
of returning timeout error, we still treat it as a successful operation.
This is to deal with the scenario in which completion has been raised but
the one waiting for the completion cannot be awaken in time due to kernel
scheduling problem.Signed-off-by Can Guo <[email protected]>--- drivers/scsi/ufs/ufshcd.c | 26 ++++++++++++++++++++++++-- drivers/scsi/ufs/ufshcd.h | 2 ++ 2 files changed, 26 insertions(+), 2 deletions(-)drivers/scsi/ufs/ufshcd.c +12
-2@@ -2122,10 +2122,20 @@ ufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)unsigned long flags;if (wait_for_completion_timeout (&uic_cmd->done, -msecs_to_jiffies (UIC_CMD_TIMEOUT ))) +msecs_to_jiffies (UIC_CMD_TIMEOUT ))) { ret = uic_cmd->argument2 &MASK_UIC_COMMAND_RESULT ; -else + }else { ret = -ETIMEDOUT ; +dev_err (hba->dev, +"uic cmd 0x%x with arg3 0x%x completion timeout\n" , + uic_cmd->command, uic_cmd->argument3); + +if (!uic_cmd->cmd_active) { +dev_err (hba->dev,"%s: UIC cmd has been completed, return the result\n", + __func__); + ret = uic_cmd->argument2 &MASK_UIC_COMMAND_RESULT ; + } + }drivers/scsi/ufs/ufshcd.c +1
-0spin_lock_irqsave (hba->host->host_lock, flags); hba->active_uic_cmd =NULL ;@@ -2157,6 +2167,7 @@ __ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd, if (completion) init_completion(&uic_cmd->done); + uic_cmd->cmd_active =1 ;ufshcd_dispatch_uic_cmd (hba, uic_cmd);drivers/scsi/ufs/ufshcd.c +7
-0return 0 ;@@ -3828,10 +3839,18 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd)dev_err (hba->dev,"pwr ctrl cmd 0x%x with mode 0x%x completion timeout\n" , cmd->command, cmd->argument3); + +if (!cmd->cmd_active) { + dev_err(hba->dev, "%s: Power Mode Change operation has been completed, go check UPMCRS\n", + __func__); + goto check_upmcrs; + } + ret = -ETIMEDOUT ; goto out; }Quoted 13 lines
drivers/scsi/ufs/ufshcd.c static irqreturn_t ufshcd_uic_cmd_compl(struct · line 4946
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +1
-0+check_upmcrs: status =ufshcd_get_upmcrs (hba);drivers/scsi/ufs/ufshcd.c +1
-0if (status !=PWR_LOCAL ) { dev_err(hba->dev,@@ -4923,11 +4942,14 @@ static irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)ufshcd_get_uic_cmd_result (hba ); hba->active_uic_cmd->argument3 =ufshcd_get_dme_attr_val (hba); +if (!hba->uic_async_done)Is this check necessary?
Thanks for your quick response.
In the case of PMC, UIC cmd completion IRQ comes first, then power status change IRQ comes next. In this case, an UIC cmd's lifecyle ends only after the power status change IRQ comes [1].
Quoted 6 lines
I guess you may want to say that in current code since we have masked UIC cmd completion IRQ in the case of a PMC operation, so no need to check it here since we won't be here anyways before power status change IRQ comes. So, removing the check here definitely works, and then we won't even need below line as well.
You read my mind : )
Quoted 8 lines
if ((intr_status & UFSHCD_UIC_PWR_MASK) && hba->uic_async_done) {(quoted diff) +1
-0+ hba->active_uic_cmd->cmd_active = 0;complete(hba->uic_async_done); retval = IRQ_HANDLED;If my guess is right, my opinion is that the current change may be more readable and comprehensive as it strictly follows my description in [1]. What do you think?
Both looks fine to me.
Thanks for the detailed description.
Stanley Chu
Quoted 40 lines
Thanks,
Can Guo.
Quoted 35 lines
Quoted 23 lines
drivers/scsi/ufs/ufshcd.c +2
-0+ hba->active_uic_cmd->cmd_active =0 ;complete (&hba->active_uic_cmd->done); retval =IRQ_HANDLED ; }if ((intr_status &UFSHCD_UIC_PWR_MASK ) && hba->uic_async_done) { + hba->active_uic_cmd->cmd_active =0 ;complete (hba->uic_async_done); retval =IRQ_HANDLED ;drivers/scsi/ufs/ufshcd.c +0
-0}drivers/scsi/ufs/ufshcd.h +1
-0@@ -64,6 +64,7 @@ enum dev_cmd_type {* @argument1: UIC command argument 1 * @argument2: UIC command argument 2 * @argument3: UIC command argument 3 +* @cmd_active: Indicate if UIC command is outstanding * @done: UIC command completion */ struct uic_command {drivers/scsi/ufs/ufshcd.h struct uic_command { · line 78
Quoted 7 lines
drivers/scsi/ufs/ufshcd.h +1
-0@@ -71,6 +72,7 @@ struct uic_command {u32 argument1;u32 argument2;u32 argument3; +int cmd_active;struct completion done; };Thanks,
Stanley Chu
PATCH 1/1 scsi: ufs: Try to save power mode change and UIC cmd completion timeout
In reply to Can GuoUse the uic_cmd->cmd_active as a flag to track the lifecycle of an UIC cmd. The flag is set before send the UIC cmd and cleared after the completion is raised in IRQ handler. For a power mode change operation, including hibern8 enter/exit, the flag is cleared only after hba->uic_async_done completion is raised. When completion timeout happens, if the flag is cleared, instead of returning timeout error, simply ignore it.
--- drivers/scsi/ufs/ufshcd.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-)
drivers/scsi/ufs/ufshcd.c +24 -2
@@ -2131,10 +2131,20 @@ ufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)unsigned long flags;if (wait_for_completion_timeout (&uic_cmd->done, -msecs_to_jiffies (UIC_CMD_TIMEOUT ))) +msecs_to_jiffies (UIC_CMD_TIMEOUT ))) { ret = uic_cmd->argument2 &MASK_UIC_COMMAND_RESULT ; -else + }else { ret = -ETIMEDOUT ; +dev_err (hba->dev, +"uic cmd 0x%x with arg3 0x%x completion timeout\n" , + uic_cmd->command, uic_cmd->argument3); + +if (!uic_cmd->cmd_active) { +dev_err (hba->dev,"%s: UIC cmd has been completed, return the result\n" , + __func__); + ret = uic_cmd->argument2 &MASK_UIC_COMMAND_RESULT ; + } + }spin_lock_irqsave (hba->host->host_lock, flags); hba->active_uic_cmd =NULL ;
@@ -2166,6 +2176,7 @@ __ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd,if (completion)init_completion (&uic_cmd->done); + uic_cmd->cmd_active =1 ;ufshcd_dispatch_uic_cmd (hba, uic_cmd);return 0 ;
@@ -3944,10 +3955,18 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd)dev_err (hba->dev,"pwr ctrl cmd 0x%x with mode 0x%x completion timeout\n" , cmd->command, cmd->argument3); + +if (!cmd->cmd_active) { +dev_err (hba->dev,"%s: Power Mode Change operation has been completed, go check UPMCRS\n" , + __func__); + goto check_upmcrs; + } + ret = -ETIMEDOUT ; goto out; } +check_upmcrs: status =ufshcd_get_upmcrs (hba);if (status !=PWR_LOCAL ) { dev_err(hba->dev,
@@ -5060,11 +5079,14 @@ static irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status) hba->active_uic_cmd->argument3 =ufshcd_get_dme_attr_val (hba);complete (&hba->active_uic_cmd->done); +if (!hba->uic_async_done) + hba->active_uic_cmd->cmd_active =0 ; retval =IRQ_HANDLED ; }if ((intr_status &UFSHCD_UIC_PWR_MASK ) && hba->uic_async_done) {complete (hba->uic_async_done); + hba->active_uic_cmd->cmd_active =0 ; retval =IRQ_HANDLED ; }return retval;
Signature · 1 line
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
Re: [PATCH] scsi: ufs: Try to save power mode change and UIC cmd completion timeout
Sorry, please ignore this specific change, which is wrongly sent out.
Quoted 88 lines · Can Guo
Use the
uic_cmd->cmd_activeas a flag to track the lifecycle of an UIC cmd. The flag is set before send the UIC cmd and cleared after the completion is raised in IRQ handler. For a power mode change operation, including hibern8 enter/exit, the flag is cleared only afterhba->uic_async_donecompletion is raised. When completion timeout happens, if the flag is cleared, instead of returning timeout error, simply ignore it.Change-Id Ie3cd6ae6221a44619925fb2cf78136a5617fdd5dSigned-off-by Can Guo <[email protected]>--- drivers/scsi/ufs/ufshcd.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-)drivers/scsi/ufs/ufshcd.c +12
-2@@ -2131,10 +2131,20 @@ ufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)unsigned long flags;if (wait_for_completion_timeout (&uic_cmd->done, -msecs_to_jiffies (UIC_CMD_TIMEOUT ))) +msecs_to_jiffies (UIC_CMD_TIMEOUT ))) { ret = uic_cmd->argument2 &MASK_UIC_COMMAND_RESULT ; -else + }else { ret = -ETIMEDOUT ; +dev_err (hba->dev, +"uic cmd 0x%x with arg3 0x%x completion timeout\n" , + uic_cmd->command, uic_cmd->argument3); + +if (!uic_cmd->cmd_active) { +dev_err (hba->dev,"%s: UIC cmd has been completed, return the result\n", + __func__); + ret = uic_cmd->argument2 &MASK_UIC_COMMAND_RESULT ; + } + }drivers/scsi/ufs/ufshcd.c +1
-0spin_lock_irqsave (hba->host->host_lock, flags); hba->active_uic_cmd =NULL ;@@ -2166,6 +2176,7 @@ __ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd, if (completion) init_completion(&uic_cmd->done); + uic_cmd->cmd_active =1 ;ufshcd_dispatch_uic_cmd (hba, uic_cmd);drivers/scsi/ufs/ufshcd.c +8
-0return 0 ;@@ -3944,10 +3955,18 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd)dev_err (hba->dev,"pwr ctrl cmd 0x%x with mode 0x%x completion timeout\n" , cmd->command, cmd->argument3); + +if (!cmd->cmd_active) { + dev_err(hba->dev, "%s: Power Mode Change operation has been completed, go check UPMCRS\n", + __func__); + goto check_upmcrs; + } + ret = -ETIMEDOUT ; goto out; } +check_upmcrs: status =ufshcd_get_upmcrs (hba);drivers/scsi/ufs/ufshcd.c +3
-0if (status !=PWR_LOCAL ) { dev_err(hba->dev,@@ -5060,11 +5079,14 @@ static irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status) hba->active_uic_cmd->argument3 =ufshcd_get_dme_attr_val (hba);complete (&hba->active_uic_cmd->done); +if (!hba->uic_async_done) + hba->active_uic_cmd->cmd_active =0 ; retval =IRQ_HANDLED ; }if ((intr_status &UFSHCD_UIC_PWR_MASK ) && hba->uic_async_done) {complete (hba->uic_async_done); + hba->active_uic_cmd->cmd_active =0 ; retval =IRQ_HANDLED ; }return retval;
Martin K. Petersen
AppliedQuoted 9 lines · Can Guo
This series contains two minor fixes, one for clk gating and one for PMC/UIC cmd completion timeout.
Can Guo (2):
scsi: ufs: Fix unbalancedscsi_block_reqs_cntcaused byufshcd_hold()
scsi: ufs: Try to save power mode change and UIC cmd completion
timeout[...]
Applied to 5.10/scsi-fixes, thanks!
[1/2] scsi: ufs: Fix unbalanced scsi_block_reqs_cnt caused by ufshcd_hold()
https://git.kernel.org/mkp/scsi/c/da3fecb00403 [2/2] scsi: ufs: Try to save power mode change and UIC cmd completion timeout https://git.kernel.org/mkp/scsi/c/0f52fcb99ea2
Signature · 1 line
Martin K. Petersen Oracle Linux Engineering