RE: [PATCH v32 4/4] scsi: ufs: Add HPB 2.0 support

2021-04-20 Thread Avri Altman
> Hi,
> > if (dev_info->wspecversion >= UFS_DEV_HPB_SUPPORT_VERSION &&
> > (b_ufs_feature_sup & UFS_DEV_HPB_SUPPORT)) {
> > -   dev_info->hpb_enabled = true;
> > +   bool hpb_en = false;
> > +
> > ufshpb_get_dev_info(hba, desc_buf);
> > +
> > +   if (!ufshpb_is_legacy(hba))
> > +   err = ufshcd_query_flag_retry(hba,
> > + 
> > UPIU_QUERY_OPCODE_READ_FLAG,
> > + 
> > QUERY_FLAG_IDN_HPB_EN, 0,
> > + _en);
> > +
> > +   if (ufshpb_is_legacy(hba) || (!err && hpb_en))
> > +   dev_info->hpb_enabled = true;
> > }
> I think there is a confusion concerning fHPBEn flag.
> The spec say: "If host wants to enable HPB, host set the fHPBEn flag as ‘1’."
> And its default value is '0'.
> So upon successful init, we should set this flag and not read it.
After some further thinking, I wish to withdraw from my comment above.
The spec doesn't really say how this flag should be used, but provides the 
"system"
With a mean to disable hpb.

So I tend to agree with your interpretation, that this flag should be 
configured by the OEMs,
Along with all other hpb configurables, should they choose to enable it.

As it is of a persistent nature, we might even consider in the future,
to add some logic that will allow to use this flag to disable hpb.

Thanks,
Avri

> 
> I wouldn't rush to fix it however, before we see what Martin/Greg are planning
> for this feature.
> Thanks,
> Avri


[PATCH v4 2/2] mmc: block: Update ext_csd.cache_ctrl if it was written

2021-04-20 Thread Avri Altman
The cache function can be turned ON and OFF by writing to the CACHE_CTRL
byte (EXT_CSD byte [33]).  However,  card->ext_csd.cache_ctrl is only
set on init if cache size > 0.

Fix that by explicitly setting ext_csd.cache_ctrl on ext-csd write.

Signed-off-by: Avri Altman 
Acked-by: Adrian Hunter 
---
 drivers/mmc/core/block.c | 12 
 1 file changed, 12 insertions(+)

diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index 24e1ecbdd510..7e70f11e85e2 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -572,6 +572,18 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, 
struct mmc_blk_data *md,
main_md->part_curr = value & EXT_CSD_PART_CONFIG_ACC_MASK;
}
 
+   /*
+* Make sure to update CACHE_CTRL in case it was changed. The cache
+* will get turned back on if the card is re-initialized, e.g.
+* suspend/resume or hw reset in recovery.
+*/
+   if ((MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_CACHE_CTRL) &&
+   (cmd.opcode == MMC_SWITCH)) {
+   u8 value = MMC_EXTRACT_VALUE_FROM_ARG(cmd.arg) & 1;
+
+   card->ext_csd.cache_ctrl = value;
+   }
+
/*
 * According to the SD specs, some commands require a delay after
 * issuing the command.
-- 
2.25.1



[PATCH v4 1/2] mmc: block: Issue flush only if allowed

2021-04-20 Thread Avri Altman
The cache may be flushed to the nonvolatile storage by writing to
FLUSH_CACHE byte (EXT_CSD byte [32]). When in command queueing mode, the
cache may be flushed by issuing a CMDQ_TASK_ DEV_MGMT (CMD48) with a
FLUSH_CACHE op-code.  Either way, verify that The cache function is
turned ON before doing so.

fixes: 1e8e55b67030 (mmc: block: Add CQE support)

Reported-by: Brendan Peter 
Tested-by: Brendan Peter 
Signed-off-by: Avri Altman 
---
 drivers/mmc/core/block.c   | 9 +
 drivers/mmc/core/mmc.c | 2 +-
 drivers/mmc/core/mmc_ops.h | 5 +
 3 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index 8bfd4d95b386..24e1ecbdd510 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -2186,6 +2186,11 @@ static int mmc_blk_wait_for_idle(struct mmc_queue *mq, 
struct mmc_host *host)
return mmc_blk_rw_wait(mq, NULL);
 }
 
+static bool mmc_blk_cache_disabled(struct mmc_card *card)
+{
+   return mmc_card_mmc(card) && !mmc_flush_allowed(card);
+}
+
 enum mmc_issued mmc_blk_mq_issue_rq(struct mmc_queue *mq, struct request *req)
 {
struct mmc_blk_data *md = mq->blkdata;
@@ -2225,6 +2230,10 @@ enum mmc_issued mmc_blk_mq_issue_rq(struct mmc_queue 
*mq, struct request *req)
case MMC_ISSUE_ASYNC:
switch (req_op(req)) {
case REQ_OP_FLUSH:
+   if (mmc_blk_cache_disabled(mq->card)) {
+   blk_mq_end_request(req, BLK_STS_OK);
+   return MMC_REQ_FINISHED;
+   }
ret = mmc_blk_cqe_issue_flush(mq, req);
break;
case REQ_OP_READ:
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 9ad4aa537867..e3da62ffcb5e 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -2037,7 +2037,7 @@ static int _mmc_flush_cache(struct mmc_card *card)
 {
int err = 0;
 
-   if (card->ext_csd.cache_size > 0 && card->ext_csd.cache_ctrl & 1) {
+   if (mmc_flush_allowed(card)) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
 EXT_CSD_FLUSH_CACHE, 1,
 CACHE_FLUSH_TIMEOUT_MS);
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 5782fdf4e8e9..2682bf66708a 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -19,6 +19,11 @@ enum mmc_busy_cmd {
 struct mmc_host;
 struct mmc_card;
 
+static inline bool mmc_flush_allowed(struct mmc_card *card)
+{
+   return card->ext_csd.cache_size > 0 && card->ext_csd.cache_ctrl & 1;
+}
+
 int mmc_select_card(struct mmc_card *card);
 int mmc_deselect_cards(struct mmc_host *host);
 int mmc_set_dsr(struct mmc_host *host);
-- 
2.25.1



[PATCH v4 0/2] Do not flush cache when it is disabled

2021-04-20 Thread Avri Altman
v3 -> v4
 - Attend Adrian's comments
 - Add Acked-by tag

v2 -> v3:
 - rebase onto recent cache changes

v1 -> v2:
 - Attend Adrian's comments

Cache is a temporary storage space in an eMMC device. Volatile by
nature, the cache should in typical case reduce the access time compared
to an access to the main nonvolatile storage.

The cache function can be turned ON and OFF. Once OFF, the host is not
expected to issue a flush-cache command to the device.

Avri Altman (2):
  mmc: block: Issue flush only if allowed
  mmc: block: Update ext_csd.cache_ctrl if it was written

 drivers/mmc/core/block.c   | 21 +
 drivers/mmc/core/mmc.c |  2 +-
 drivers/mmc/core/mmc_ops.h |  5 +
 3 files changed, 27 insertions(+), 1 deletion(-)

-- 
2.25.1



RE: [PATCH v3 1/2] mmc: block: Issue flush only if allowed

2021-04-20 Thread Avri Altman
 
> On 20/04/21 8:53 am, Avri Altman wrote:
> > The cache may be flushed to the nonvolatile storage by writing to
> > FLUSH_CACHE byte (EXT_CSD byte [32]). When in command queueing mode,
> the
> > cache may be flushed by issuing a CMDQ_TASK_ DEV_MGMT (CMD48) with a
> > FLUSH_CACHE op-code.  Either way, verify that The cache function is
> > turned ON before doing so.
> >
> > fixes: 1e8e55b67030 (mmc: block: Add CQE support)
> >
> > Reported-by: Brendan Peter 
> > Tested-by: Brendan Peter 
> > Signed-off-by: Avri Altman 
> > ---
> >  drivers/mmc/core/block.c   | 7 +++
> >  drivers/mmc/core/mmc.c | 2 +-
> >  drivers/mmc/core/mmc_ops.h | 5 +
> >  3 files changed, 13 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
> > index 8bfd4d95b386..5b6501fc9fb7 100644
> > --- a/drivers/mmc/core/block.c
> > +++ b/drivers/mmc/core/block.c
> > @@ -1476,6 +1476,11 @@ static int mmc_blk_cqe_issue_flush(struct
> mmc_queue *mq, struct request *req)
> >   struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req);
> >   struct mmc_request *mrq = mmc_blk_cqe_prep_dcmd(mqrq, req);
> >
> > + if (mmc_card_mmc(mq->card) && !mmc_flush_allowed(mq->card)) {
> > + blk_mq_end_request(req, BLK_STS_OK);
> > + return -EPERM;
> > + }
> > +
> >   mrq->cmd->opcode = MMC_SWITCH;
> >   mrq->cmd->arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
> >   (EXT_CSD_FLUSH_CACHE << 16) |
> > @@ -2226,6 +2231,8 @@ enum mmc_issued mmc_blk_mq_issue_rq(struct
> mmc_queue *mq, struct request *req)
> >   switch (req_op(req)) {
> >   case REQ_OP_FLUSH:
> >   ret = mmc_blk_cqe_issue_flush(mq, req);
> > + if (ret == -EPERM)
> > + return MMC_REQ_FINISHED;
> 
> Using an error code for this case seems a little fragile.
> 
> How about something like:
> 
> case REQ_OP_FLUSH:
> if (mmc_blk_cache_disabled(mq->card)) {
> blk_mq_end_request(req, BLK_STS_OK);
> return MMC_REQ_FINISHED;
> }
> ret = mmc_blk_cqe_issue_flush(mq, req);
> 
> 
> static bool mmc_blk_cache_disabled(struct mmc_card *card)
> {
> return mmc_card_mmc(mq->card) && !mmc_flush_allowed(card);
> }
Done.

Thanks,
Avri


[PATCH v3 2/2] mmc: block: Update ext_csd.cache_ctrl if it was written

2021-04-19 Thread Avri Altman
The cache function can be turned ON and OFF by writing to the CACHE_CTRL
byte (EXT_CSD byte [33]).  However,  card->ext_csd.cache_ctrl is only
set on init if cache size > 0.

Fix that by explicitly setting ext_csd.cache_ctrl on ext-csd write.

Signed-off-by: Avri Altman 
---
 drivers/mmc/core/block.c | 12 
 1 file changed, 12 insertions(+)

diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index 5b6501fc9fb7..8b07ed5e08de 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -572,6 +572,18 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, 
struct mmc_blk_data *md,
main_md->part_curr = value & EXT_CSD_PART_CONFIG_ACC_MASK;
}
 
+   /*
+* Make sure to update CACHE_CTRL in case it was changed. The cache
+* will get turned back on if the card is re-initialized, e.g.
+* suspend/resume or hw reset in recovery.
+*/
+   if ((MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_CACHE_CTRL) &&
+   (cmd.opcode == MMC_SWITCH)) {
+   u8 value = MMC_EXTRACT_VALUE_FROM_ARG(cmd.arg) & 1;
+
+   card->ext_csd.cache_ctrl = value;
+   }
+
/*
 * According to the SD specs, some commands require a delay after
 * issuing the command.
-- 
2.25.1



[PATCH v3 1/2] mmc: block: Issue flush only if allowed

2021-04-19 Thread Avri Altman
The cache may be flushed to the nonvolatile storage by writing to
FLUSH_CACHE byte (EXT_CSD byte [32]). When in command queueing mode, the
cache may be flushed by issuing a CMDQ_TASK_ DEV_MGMT (CMD48) with a
FLUSH_CACHE op-code.  Either way, verify that The cache function is
turned ON before doing so.

fixes: 1e8e55b67030 (mmc: block: Add CQE support)

Reported-by: Brendan Peter 
Tested-by: Brendan Peter 
Signed-off-by: Avri Altman 
---
 drivers/mmc/core/block.c   | 7 +++
 drivers/mmc/core/mmc.c | 2 +-
 drivers/mmc/core/mmc_ops.h | 5 +
 3 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index 8bfd4d95b386..5b6501fc9fb7 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -1476,6 +1476,11 @@ static int mmc_blk_cqe_issue_flush(struct mmc_queue *mq, 
struct request *req)
struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req);
struct mmc_request *mrq = mmc_blk_cqe_prep_dcmd(mqrq, req);
 
+   if (mmc_card_mmc(mq->card) && !mmc_flush_allowed(mq->card)) {
+   blk_mq_end_request(req, BLK_STS_OK);
+   return -EPERM;
+   }
+
mrq->cmd->opcode = MMC_SWITCH;
mrq->cmd->arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
(EXT_CSD_FLUSH_CACHE << 16) |
@@ -2226,6 +2231,8 @@ enum mmc_issued mmc_blk_mq_issue_rq(struct mmc_queue *mq, 
struct request *req)
switch (req_op(req)) {
case REQ_OP_FLUSH:
ret = mmc_blk_cqe_issue_flush(mq, req);
+   if (ret == -EPERM)
+   return MMC_REQ_FINISHED;
break;
case REQ_OP_READ:
case REQ_OP_WRITE:
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 9ad4aa537867..e3da62ffcb5e 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -2037,7 +2037,7 @@ static int _mmc_flush_cache(struct mmc_card *card)
 {
int err = 0;
 
-   if (card->ext_csd.cache_size > 0 && card->ext_csd.cache_ctrl & 1) {
+   if (mmc_flush_allowed(card)) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
 EXT_CSD_FLUSH_CACHE, 1,
 CACHE_FLUSH_TIMEOUT_MS);
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 5782fdf4e8e9..2682bf66708a 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -19,6 +19,11 @@ enum mmc_busy_cmd {
 struct mmc_host;
 struct mmc_card;
 
+static inline bool mmc_flush_allowed(struct mmc_card *card)
+{
+   return card->ext_csd.cache_size > 0 && card->ext_csd.cache_ctrl & 1;
+}
+
 int mmc_select_card(struct mmc_card *card);
 int mmc_deselect_cards(struct mmc_host *host);
 int mmc_set_dsr(struct mmc_host *host);
-- 
2.25.1



[PATCH v3 0/2] Do not flush cache when it is disabled

2021-04-19 Thread Avri Altman
v2 -> v3:
 - rebase onto recent cache changes

v1 -> v2:
 - Attend Adrian's comments

Cache is a temporary storage space in an eMMC device. Volatile by
nature, the cache should in typical case reduce the access time compared
to an access to the main nonvolatile storage.

The cache function can be turned ON and OFF. Once OFF, the host is not
expected to issue a flush-cache command to the device.

Avri Altman (2):
  mmc: block: Issue flush only if allowed
  mmc: block: Update ext_csd.cache_ctrl if it was written

 drivers/mmc/core/block.c   | 19 +++
 drivers/mmc/core/mmc.c |  2 +-
 drivers/mmc/core/mmc_ops.h |  5 +
 3 files changed, 25 insertions(+), 1 deletion(-)

-- 
2.25.1



RE: [PATCH v2 1/2] mmc: block: Issue flush only if allowed

2021-04-19 Thread Avri Altman
> 
> On Sun, 18 Apr 2021 at 08:00, Avri Altman  wrote:
> >
> > The cache may be flushed to the nonvolatile storage by writing to
> > FLUSH_CACHE byte (EXT_CSD byte [32]). When in command queueing mode,
> the
> > cache may be flushed by issuing a CMDQ_TASK_ DEV_MGMT (CMD48) with a
> > FLUSH_CACHE op-code.  Either way, verify that The cache function is
> > turned ON before doing so.
> 
> Avri, thanks for your patch. Overall this looks good to me.
> 
> However things are becoming more and more messy in these layers of the
> mmc core. In particular, I would like us to take advantage of the
> bus_ops callbacks, when eMMC and/or SD specific features need
> different implementations.
> 
> I have posted a patch [1], that moves the eMMC cache flushing into a
> bus_ops callback. Would you mind rebasing this series on top of that?
Sure.

Thanks,
Avri

> 
> Kind regards
> Uffe
> 
> [1]
> https://patchwork.kernel.org/project/linux-
> mmc/patch/20210419122943.68234-1-ulf.hans...@linaro.org/



RE: [PATCH v2 1/2] mmc: block: Issue flush only if allowed

2021-04-19 Thread Avri Altman
> 
> On Sun, 18 Apr 2021 at 08:00, Avri Altman  wrote:
> >
> > The cache may be flushed to the nonvolatile storage by writing to
> > FLUSH_CACHE byte (EXT_CSD byte [32]). When in command queueing mode,
> the
> > cache may be flushed by issuing a CMDQ_TASK_ DEV_MGMT (CMD48) with a
> > FLUSH_CACHE op-code.  Either way, verify that The cache function is
> > turned ON before doing so.
> 
> Avri, thanks for your patch. Overall this looks good to me.
> 
> However things are becoming more and more messy in these layers of the
> mmc core. In particular, I would like us to take advantage of the
> bus_ops callbacks, when eMMC and/or SD specific features need
> different implementations.
> 
> I have posted a patch [1], that moves the eMMC cache flushing into a
> bus_ops callback. Would you mind rebasing this series on top of that?
Sure.

Thanks,
Avri

> 
> Kind regards
> Uffe
> 
> [1]
> https://patchwork.kernel.org/project/linux-
> mmc/patch/20210419122943.68234-1-ulf.hans...@linaro.org/



RE: [PATCH] mmc: core: Move eMMC cache flushing to a new bus_ops callback

2021-04-19 Thread Avri Altman
 
> To prepare to add internal cache management for SD cards, let's start by
> moving the eMMC specific code into a new ->flush_cache() bus_ops callback.
> 
> In this way, it becomes more straight-forward to add the SD specific parts,
> as subsequent changes are about to show.
> 
> Signed-off-by: Ulf Hansson 
Reviewed-by: Avri Altman 


RE: [PATCH v32 4/4] scsi: ufs: Add HPB 2.0 support

2021-04-19 Thread Avri Altman
Hi,
> if (dev_info->wspecversion >= UFS_DEV_HPB_SUPPORT_VERSION &&
> (b_ufs_feature_sup & UFS_DEV_HPB_SUPPORT)) {
> -   dev_info->hpb_enabled = true;
> +   bool hpb_en = false;
> +
> ufshpb_get_dev_info(hba, desc_buf);
> +
> +   if (!ufshpb_is_legacy(hba))
> +   err = ufshcd_query_flag_retry(hba,
> + 
> UPIU_QUERY_OPCODE_READ_FLAG,
> + QUERY_FLAG_IDN_HPB_EN, 
> 0,
> + _en);
> +
> +   if (ufshpb_is_legacy(hba) || (!err && hpb_en))
> +   dev_info->hpb_enabled = true;
> }
I think there is a confusion concerning fHPBEn flag.
The spec say: "If host wants to enable HPB, host set the fHPBEn flag as ‘1’."
And its default value is '0'.
So upon successful init, we should set this flag and not read it.

I wouldn't rush to fix it however, before we see what Martin/Greg are planning 
for this feature.
Thanks,
Avri


RE: [PATCH] scsi: ufs: Check for bkops in runtime suspend

2021-04-18 Thread Avri Altman
> On 18/04/21 10:21 am, Avri Altman wrote:
> > The UFS driver allowed BKOPS and WB Flush operations to be completed on
> > Runtime suspend. Adding the DeepSleep support, this is no longer true:
> > the driver will ignore BKOPS and WB Flush states, and force a link state
> > transition to UIC_LINK_OFF_STATE.
> >
> > Do not ignore BKOPS and WB Flush on runtme suspend flow.
> >
> > fixes: fe1d4c2ebcae (scsi: ufs: Add DeepSleep feature)
> >
> > Suggested-by: Alex Lemberg 
> > Signed-off-by: Avri Altman 
> > ---
> >  drivers/scsi/ufs/ufshcd.c | 3 ++-
> >  1 file changed, 2 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
> > index 58d7f264c664..1a0cac670aba 100644
> > --- a/drivers/scsi/ufs/ufshcd.c
> > +++ b/drivers/scsi/ufs/ufshcd.c
> > @@ -8755,7 +8755,8 @@ static int ufshcd_suspend(struct ufs_hba *hba,
> enum ufs_pm_op pm_op)
> >* In the case of DeepSleep, the device is expected to remain powered
> >* with the link off, so do not check for bkops.
> >*/
> > - check_for_bkops = !ufshcd_is_ufs_dev_deepsleep(hba);
> > + check_for_bkops = !ufshcd_is_ufs_dev_deepsleep(hba) ||
> > +   hba->dev_info.b_rpm_dev_flush_capable;
> 
> Can you explain this some more? If hba->dev_info.b_rpm_dev_flush_capable
> is true, then ufshcd_set_dev_pwr_mode() was not called, so
> ufshcd_is_ufs_dev_deepsleep() is false i.e. the same result for the
> condition above.
You are correct.  Sorry for the confusion.

> 
> However, it is assumed DeepSleep has the link off so that a full reset
> and restore is done upon resume, which is necessary to exit DeepSleep.
> So if you wanted to have DeepSleep with the link on, then the resume
> logic would also need changes.
No need.  We just wanted to verify that on runtime suspend, if bkops is allowed 
and required,
The device will get the extra idle time it needs.
As this is the case, no change is needed and I will just drop it.
Again, sorry for the confusion.

Thanks,
Avri

> 
> >   ret = ufshcd_link_state_transition(hba, req_link_state, 
> > check_for_bkops);
> >   if (ret)
> >   goto set_dev_active;
> >



[PATCH] scsi: ufs: Check for bkops in runtime suspend

2021-04-18 Thread Avri Altman
The UFS driver allowed BKOPS and WB Flush operations to be completed on
Runtime suspend. Adding the DeepSleep support, this is no longer true:
the driver will ignore BKOPS and WB Flush states, and force a link state
transition to UIC_LINK_OFF_STATE.

Do not ignore BKOPS and WB Flush on runtme suspend flow.

fixes: fe1d4c2ebcae (scsi: ufs: Add DeepSleep feature)

Suggested-by: Alex Lemberg 
Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshcd.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 58d7f264c664..1a0cac670aba 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -8755,7 +8755,8 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum 
ufs_pm_op pm_op)
 * In the case of DeepSleep, the device is expected to remain powered
 * with the link off, so do not check for bkops.
 */
-   check_for_bkops = !ufshcd_is_ufs_dev_deepsleep(hba);
+   check_for_bkops = !ufshcd_is_ufs_dev_deepsleep(hba) ||
+ hba->dev_info.b_rpm_dev_flush_capable;
ret = ufshcd_link_state_transition(hba, req_link_state, 
check_for_bkops);
if (ret)
goto set_dev_active;
-- 
2.25.1



[PATCH v2 2/2] mmc: block: Update ext_csd.cache_ctrl if it was written

2021-04-18 Thread Avri Altman
The cache function can be turned ON and OFF by writing to the CACHE_CTRL
byte (EXT_CSD byte [33]).  However,  card->ext_csd.cache_ctrl is only
set on init if cache size > 0.

Fix that by explicitly setting ext_csd.cache_ctrl on ext-csd write.

Signed-off-by: Avri Altman 
---
 drivers/mmc/core/block.c | 12 
 1 file changed, 12 insertions(+)

diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index 6800feb70c92..0372702231ce 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -572,6 +572,18 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, 
struct mmc_blk_data *md,
main_md->part_curr = value & EXT_CSD_PART_CONFIG_ACC_MASK;
}
 
+   /*
+* Make sure to update CACHE_CTRL in case it was changed. The cache
+* will get turned back on if the card is re-initialized, e.g.
+* suspend/resume or hw reset in recovery.
+*/
+   if ((MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_CACHE_CTRL) &&
+   (cmd.opcode == MMC_SWITCH)) {
+   u8 value = MMC_EXTRACT_VALUE_FROM_ARG(cmd.arg) & 1;
+
+   card->ext_csd.cache_ctrl = value;
+   }
+
/*
 * According to the SD specs, some commands require a delay after
 * issuing the command.
-- 
2.25.1



[PATCH v2 1/2] mmc: block: Issue flush only if allowed

2021-04-18 Thread Avri Altman
The cache may be flushed to the nonvolatile storage by writing to
FLUSH_CACHE byte (EXT_CSD byte [32]). When in command queueing mode, the
cache may be flushed by issuing a CMDQ_TASK_ DEV_MGMT (CMD48) with a
FLUSH_CACHE op-code.  Either way, verify that The cache function is
turned ON before doing so.

fixes: 1e8e55b67030 (mmc: block: Add CQE support)

Reported-by: Brendan Peter 
Tested-by: Brendan Peter 
Signed-off-by: Avri Altman 
---
 drivers/mmc/core/block.c   | 7 +++
 drivers/mmc/core/mmc_ops.c | 4 +---
 drivers/mmc/core/mmc_ops.h | 5 +
 3 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index fe5892d30778..6800feb70c92 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -1476,6 +1476,11 @@ static int mmc_blk_cqe_issue_flush(struct mmc_queue *mq, 
struct request *req)
struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req);
struct mmc_request *mrq = mmc_blk_cqe_prep_dcmd(mqrq, req);
 
+   if (mmc_card_mmc(mq->card) && !mmc_flush_allowed(mq->card)) {
+   blk_mq_end_request(req, BLK_STS_OK);
+   return -EPERM;
+   }
+
mrq->cmd->opcode = MMC_SWITCH;
mrq->cmd->arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
(EXT_CSD_FLUSH_CACHE << 16) |
@@ -2226,6 +2231,8 @@ enum mmc_issued mmc_blk_mq_issue_rq(struct mmc_queue *mq, 
struct request *req)
switch (req_op(req)) {
case REQ_OP_FLUSH:
ret = mmc_blk_cqe_issue_flush(mq, req);
+   if (ret == -EPERM)
+   return MMC_REQ_FINISHED;
break;
case REQ_OP_READ:
case REQ_OP_WRITE:
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index f413474f0f80..9c2a665be034 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -967,9 +967,7 @@ int mmc_flush_cache(struct mmc_card *card)
 {
int err = 0;
 
-   if (mmc_card_mmc(card) &&
-   (card->ext_csd.cache_size > 0) &&
-   (card->ext_csd.cache_ctrl & 1)) {
+   if (mmc_card_mmc(card) && mmc_flush_allowed(card)) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
 EXT_CSD_FLUSH_CACHE, 1,
 MMC_CACHE_FLUSH_TIMEOUT_MS);
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 632009260e51..bf2b315addd7 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -19,6 +19,11 @@ enum mmc_busy_cmd {
 struct mmc_host;
 struct mmc_card;
 
+static inline bool mmc_flush_allowed(struct mmc_card *card)
+{
+   return card->ext_csd.cache_size > 0 && (card->ext_csd.cache_ctrl & 1);
+}
+
 int mmc_select_card(struct mmc_card *card);
 int mmc_deselect_cards(struct mmc_host *host);
 int mmc_set_dsr(struct mmc_host *host);
-- 
2.25.1



[PATCH v2 0/2] Do not flush cache when it is disabled

2021-04-18 Thread Avri Altman
v1 -> v2:
 - Attend Adrian's comments

Cache is a temporary storage space in an eMMC device. Volatile by
nature, the cache should in typical case reduce the access time compared
to an access to the main nonvolatile storage.

The cache function can be turned ON and OFF. Once OFF, the host is not
expected to issue a flush-cache command to the device.

Avri Altman (2):
  mmc: block: Issue flush only if allowed
  mmc: block: Update ext_csd.cache_ctrl if it was written

 drivers/mmc/core/block.c   | 19 +++
 drivers/mmc/core/mmc_ops.c |  4 +---
 drivers/mmc/core/mmc_ops.h |  5 +
 3 files changed, 25 insertions(+), 3 deletions(-)

-- 
2.25.1



RE: [PATCH v32 4/4] scsi: ufs: Add HPB 2.0 support

2021-04-14 Thread Avri Altman
> From: Daejun Park 
> 
> @@ -1692,6 +2188,7 @@ static void ufshpb_hpb_lu_prepared(struct ufs_hba
> *hba)
> ufshpb_set_state(hpb, HPB_PRESENT);
> if ((hpb->lu_pinned_end - hpb->lu_pinned_start) > 0)
> queue_work(ufshpb_wq, >map_work);
> +   ufshpb_issue_umap_all_req(hpb);
> HPB-WRITE-BUFFER with buffer-id = 0x3h is supported in device control mode
> only
> so I think it is necessary to distinguish device mode and host mode.
You are correct.
Since Daejun's driver's scope is device mode only - see ufshpb_get_dev_info,
This needs to be fixed in the host mode series.
I will fix it in my next version.

Thanks,
Avri

> } else {
> dev_err(hba->dev, "destroy HPB lu %d\n", hpb->lun);
> ufshpb_destroy_lu(hba, sdev);
> 



RE: [PATCH] mmc: add quirk to disable eMMC cache for Micron eMMC v5.0 cards

2021-04-13 Thread Avri Altman
Scott hi,
Can you take a look at 
https://lore.kernel.org/lkml/20210322133645.4901-1-avri.alt...@wdc.com/
Is there a chance that it will work for you as well?

Thanks,
Avri

> -Original Message-
> From: Scott Branden 
> Sent: Tuesday, 13 April 2021 18:46
> To: Ulf Hansson ; Bean Huo (beanhuo)
> ; Luca Porzio (lporzio) ;
> tedir...@micron.com
> Cc: BCM Kernel Feedback ; linux-
> mmc ; Linux Kernel Mailing List  ker...@vger.kernel.org>; Vladimir Olovyannikov
> 
> Subject: Re: [PATCH] mmc: add quirk to disable eMMC cache for Micron eMMC
> v5.0 cards
> 
> +Travis Dirkes (with corrected email),
> 
>  Could you provide more details on the Micron data error.
> 
> > On 2021-04-12 12:44 a.m., Ulf Hansson wrote:
> >> + Bean Huo, Luca Porzio
> >>
> >> On Thu, 8 Apr 2021 at 19:59, Scott Branden
>  wrote:
> >>>
> >>> From: Vladimir Olovyannikov 
> >>>
> >>> In certain rare combination of operations, Micron eMMC v5.0 cards
> >>> may experience data errors if internal cache is enabled.
> >>> This may lead to eMMC related data errors.
> >>> Introduce a quirk to disable cache on these eMMC cards.
> >>
> >> Can you please elaborate on this, what combinations of operations are
> >> you referring to - and what kind of data errors?
> >>
> >> I have also looped in some of the Micron guys, to let them chim in.
> >>
> >>>
> >>> Signed-off-by: Vladimir Olovyannikov
> 
> >>> Signed-off-by: Scott Branden 
> >>
> >> Kind regards
> >> Uffe
> >>
> >>> ---
> >>>  drivers/mmc/core/card.h   | 5 +
> >>>  drivers/mmc/core/mmc.c| 4 ++--
> >>>  drivers/mmc/core/quirks.h | 8 
> >>>  include/linux/mmc/card.h  | 1 +
> >>>  4 files changed, 16 insertions(+), 2 deletions(-)
> >>>
> >>> diff --git a/drivers/mmc/core/card.h b/drivers/mmc/core/card.h
> >>> index 7bd392d55cfa..22cea63ac359 100644
> >>> --- a/drivers/mmc/core/card.h
> >>> +++ b/drivers/mmc/core/card.h
> >>> @@ -222,4 +222,9 @@ static inline int mmc_card_broken_hpi(const
> struct mmc_card *c)
> >>> return c->quirks & MMC_QUIRK_BROKEN_HPI;
> >>>  }
> >>>
> >>> +static inline int mmc_card_broken_cache(const struct mmc_card *c)
> >>> +{
> >>> +   return c->quirks & MMC_QUIRK_BROKEN_CACHE;
> >>> +}
> >>> +
> >>>  #endif
> >>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> >>> index 8741271d3971..cd83b7f0e59c 100644
> >>> --- a/drivers/mmc/core/mmc.c
> >>> +++ b/drivers/mmc/core/mmc.c
> >>> @@ -1820,12 +1820,12 @@ static int mmc_init_card(struct mmc_host
> *host, u32 ocr,
> >>>  * sudden power failure tests. Let's extend the timeout to a 
> >>> minimum of
> >>>  * DEFAULT_CACHE_EN_TIMEOUT_MS and do it for all cards.
> >>>  */
> >>> -   if (card->ext_csd.cache_size > 0) {
> >>> +   if (!mmc_card_broken_cache(card) && card->ext_csd.cache_size > 0)
> {
> >>> unsigned int timeout_ms = MIN_CACHE_EN_TIMEOUT_MS;
> >>>
> >>> timeout_ms = max(card->ext_csd.generic_cmd6_time,
> timeout_ms);
> >>> err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> >>> -   EXT_CSD_CACHE_CTRL, 1, timeout_ms);
> >>> +EXT_CSD_CACHE_CTRL, 1, timeout_ms);
> >>> if (err && err != -EBADMSG)
> >>> goto free_card;
> >>>
> >>> diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h
> >>> index d68e6e513a4f..23972d87c82a 100644
> >>> --- a/drivers/mmc/core/quirks.h
> >>> +++ b/drivers/mmc/core/quirks.h
> >>> @@ -116,6 +116,14 @@ static const struct mmc_fixup __maybe_unused
> mmc_ext_csd_fixups[] = {
> >>> MMC_FIXUP_EXT_CSD_REV(CID_NAME_ANY,
> CID_MANFID_NUMONYX,
> >>>   0x014e, add_quirk, MMC_QUIRK_BROKEN_HPI, 6),
> >>>
> >>> +   /*
> >>> +* In certain rare combination of operations, Micron eMMC v5.0 
> >>> cards
> >>> +* may experience data errors if internal cache is enabled.
> >>> +* Disabling cache for these cards eliminates the issue.
> >>> +*/
> >>> +   MMC_FIXUP_EXT_CSD_REV(CID_NAME_ANY,
> CID_MANFID_MICRON,
> >>> + 0x014e, add_quirk, MMC_QUIRK_BROKEN_CACHE, 
> >>> 7),
> >>> +
> >>> END_FIXUP
> >>>  };
> >>>
> >>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> >>> index f9ad35dd6012..22f256a4e54e 100644
> >>> --- a/include/linux/mmc/card.h
> >>> +++ b/include/linux/mmc/card.h
> >>> @@ -270,6 +270,7 @@ struct mmc_card {
> >>>  #define MMC_QUIRK_BROKEN_IRQ_POLLING   (1<<11) /* Polling
> SDIO_CCCR_INTx could create a fake interrupt */
> >>>  #define MMC_QUIRK_TRIM_BROKEN  (1<<12) /* Skip trim */
> >>>  #define MMC_QUIRK_BROKEN_HPI   (1<<13) /* Disable broken HPI
> support */
> >>> +#define MMC_QUIRK_BROKEN_CACHE (1<<14) /* Disable broken
> cache */
> >>>
> >>> boolreenable_cmdq;  /* Re-enable Command 
> >>> Queue */
> >>>
> >>> --
> >>> 2.17.1
> >>>
> >



[PATCH v8 09/11] scsi: ufshpb: Limit the number of inflight map requests

2021-04-11 Thread Avri Altman
In host control mode the host is the originator of map requests. To not
flood the device with map requests, use a simple throttling mechanism
that limits the number of inflight map requests.

Signed-off-by: Avri Altman 
Reviewed-by: Daejun Park 
---
 drivers/scsi/ufs/ufshpb.c | 11 +++
 drivers/scsi/ufs/ufshpb.h |  1 +
 2 files changed, 12 insertions(+)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 8d067cf72ae2..0f72ea3f5d71 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -21,6 +21,7 @@
 #define READ_TO_MS 1000
 #define READ_TO_EXPIRIES 100
 #define POLLING_INTERVAL_MS 200
+#define THROTTLE_MAP_REQ_DEFAULT 1
 
 /* memory management */
 static struct kmem_cache *ufshpb_mctx_cache;
@@ -741,6 +742,14 @@ static struct ufshpb_req *ufshpb_get_map_req(struct 
ufshpb_lu *hpb,
struct ufshpb_req *map_req;
struct bio *bio;
 
+   if (hpb->is_hcm &&
+   hpb->num_inflight_map_req >= THROTTLE_MAP_REQ_DEFAULT) {
+   dev_info(>sdev_ufs_lu->sdev_dev,
+"map_req throttle. inflight %d throttle %d",
+hpb->num_inflight_map_req, THROTTLE_MAP_REQ_DEFAULT);
+   return NULL;
+   }
+
map_req = ufshpb_get_req(hpb, srgn->rgn_idx, REQ_OP_SCSI_IN, false);
if (!map_req)
return NULL;
@@ -755,6 +764,7 @@ static struct ufshpb_req *ufshpb_get_map_req(struct 
ufshpb_lu *hpb,
 
map_req->rb.srgn_idx = srgn->srgn_idx;
map_req->rb.mctx = srgn->mctx;
+   hpb->num_inflight_map_req++;
 
return map_req;
 }
@@ -764,6 +774,7 @@ static void ufshpb_put_map_req(struct ufshpb_lu *hpb,
 {
bio_put(map_req->bio);
ufshpb_put_req(hpb, map_req);
+   hpb->num_inflight_map_req--;
 }
 
 static int ufshpb_clear_dirty_bitmap(struct ufshpb_lu *hpb,
diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
index 448062a94760..cfa0abac21db 100644
--- a/drivers/scsi/ufs/ufshpb.h
+++ b/drivers/scsi/ufs/ufshpb.h
@@ -217,6 +217,7 @@ struct ufshpb_lu {
struct ufshpb_req *pre_req;
int num_inflight_pre_req;
int throttle_pre_req;
+   int num_inflight_map_req;
struct list_head lh_pre_req_free;
int cur_read_id;
int pre_req_min_tr_len;
-- 
2.25.1



[PATCH v8 08/11] scsi: ufshpb: Add "Cold" regions timer

2021-04-11 Thread Avri Altman
In order not to hang on to “cold” regions, we shall inactivate a
region that has no READ access for a predefined amount of time -
READ_TO_MS. For that purpose we shall monitor the active regions list,
polling it on every POLLING_INTERVAL_MS. On timeout expiry we shall add
the region to the "to-be-inactivated" list, unless it is clean and did
not exhaust its READ_TO_EXPIRIES - another parameter.

All this does not apply to pinned regions.

Signed-off-by: Avri Altman 
Reviewed-by: Daejun Park 
---
 drivers/scsi/ufs/ufshpb.c | 74 +--
 drivers/scsi/ufs/ufshpb.h |  8 +
 2 files changed, 79 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 0352d269c1e9..8d067cf72ae2 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -18,6 +18,9 @@
 
 #define ACTIVATION_THRESHOLD 8 /* 8 IOs */
 #define EVICTION_THRESHOLD (ACTIVATION_THRESHOLD << 5) /* 256 IOs */
+#define READ_TO_MS 1000
+#define READ_TO_EXPIRIES 100
+#define POLLING_INTERVAL_MS 200
 
 /* memory management */
 static struct kmem_cache *ufshpb_mctx_cache;
@@ -1032,12 +1035,63 @@ static int ufshpb_check_srgns_issue_state(struct 
ufshpb_lu *hpb,
return 0;
 }
 
+static void ufshpb_read_to_handler(struct work_struct *work)
+{
+   struct ufshpb_lu *hpb = container_of(work, struct ufshpb_lu,
+ufshpb_read_to_work.work);
+   struct victim_select_info *lru_info = >lru_info;
+   struct ufshpb_region *rgn, *next_rgn;
+   unsigned long flags;
+   LIST_HEAD(expired_list);
+
+   if (test_and_set_bit(TIMEOUT_WORK_RUNNING, >work_data_bits))
+   return;
+
+   spin_lock_irqsave(>rgn_state_lock, flags);
+
+   list_for_each_entry_safe(rgn, next_rgn, _info->lh_lru_rgn,
+list_lru_rgn) {
+   bool timedout = ktime_after(ktime_get(), rgn->read_timeout);
+
+   if (timedout) {
+   rgn->read_timeout_expiries--;
+   if (is_rgn_dirty(rgn) ||
+   rgn->read_timeout_expiries == 0)
+   list_add(>list_expired_rgn, _list);
+   else
+   rgn->read_timeout = ktime_add_ms(ktime_get(),
+READ_TO_MS);
+   }
+   }
+
+   spin_unlock_irqrestore(>rgn_state_lock, flags);
+
+   list_for_each_entry_safe(rgn, next_rgn, _list,
+list_expired_rgn) {
+   list_del_init(>list_expired_rgn);
+   spin_lock_irqsave(>rsp_list_lock, flags);
+   ufshpb_update_inactive_info(hpb, rgn->rgn_idx);
+   spin_unlock_irqrestore(>rsp_list_lock, flags);
+   }
+
+   ufshpb_kick_map_work(hpb);
+
+   clear_bit(TIMEOUT_WORK_RUNNING, >work_data_bits);
+
+   schedule_delayed_work(>ufshpb_read_to_work,
+ msecs_to_jiffies(POLLING_INTERVAL_MS));
+}
+
 static void ufshpb_add_lru_info(struct victim_select_info *lru_info,
struct ufshpb_region *rgn)
 {
rgn->rgn_state = HPB_RGN_ACTIVE;
list_add_tail(>list_lru_rgn, _info->lh_lru_rgn);
atomic_inc(_info->active_cnt);
+   if (rgn->hpb->is_hcm) {
+   rgn->read_timeout = ktime_add_ms(ktime_get(), READ_TO_MS);
+   rgn->read_timeout_expiries = READ_TO_EXPIRIES;
+   }
 }
 
 static void ufshpb_hit_lru_info(struct victim_select_info *lru_info,
@@ -1825,6 +1879,7 @@ static int ufshpb_alloc_region_tbl(struct ufs_hba *hba, 
struct ufshpb_lu *hpb)
 
INIT_LIST_HEAD(>list_inact_rgn);
INIT_LIST_HEAD(>list_lru_rgn);
+   INIT_LIST_HEAD(>list_expired_rgn);
 
if (rgn_idx == hpb->rgns_per_lu - 1) {
srgn_cnt = ((hpb->srgns_per_lu - 1) %
@@ -1846,6 +1901,7 @@ static int ufshpb_alloc_region_tbl(struct ufs_hba *hba, 
struct ufshpb_lu *hpb)
}
 
rgn->rgn_flags = 0;
+   rgn->hpb = hpb;
}
 
return 0;
@@ -2069,9 +2125,12 @@ static int ufshpb_lu_hpb_init(struct ufs_hba *hba, 
struct ufshpb_lu *hpb)
INIT_LIST_HEAD(>list_hpb_lu);
 
INIT_WORK(>map_work, ufshpb_map_work_handler);
-   if (hpb->is_hcm)
+   if (hpb->is_hcm) {
INIT_WORK(>ufshpb_normalization_work,
  ufshpb_normalization_work_handler);
+   INIT_DELAYED_WORK(>ufshpb_read_to_work,
+ ufshpb_read_to_handler);
+   }
 
hpb->map_req_cache = kmem_cache_create("ufshpb_req_cache",
  sizeof(struct ufshpb_req), 0, 0, NULL);
@@ -2105,6 +2164,10 @@ static int ufshpb_lu_hp

[PATCH v8 11/11] scsi: ufshpb: Make host mode parameters configurable

2021-04-11 Thread Avri Altman
We can make use of this commit, to elaborate some more of the host
control mode logic, explaining what role play each and every variable.

While at it, allow those parameters to be configurable.

Signed-off-by: Avri Altman 
Reviewed-by: Daejun Park 
---
 Documentation/ABI/testing/sysfs-driver-ufs |  84 +-
 drivers/scsi/ufs/ufshpb.c  | 288 +++--
 drivers/scsi/ufs/ufshpb.h  |  20 ++
 3 files changed, 365 insertions(+), 27 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-driver-ufs 
b/Documentation/ABI/testing/sysfs-driver-ufs
index 419adf450b89..133af2114165 100644
--- a/Documentation/ABI/testing/sysfs-driver-ufs
+++ b/Documentation/ABI/testing/sysfs-driver-ufs
@@ -1323,14 +1323,76 @@ Description:This entry shows the maximum HPB data 
size for using single HPB
 
The file is read only.
 
-What:  /sys/bus/platform/drivers/ufshcd/*/flags/wb_enable
-Date:  March 2021
-Contact:   Daejun Park 
-Description:   This entry shows the status of HPB.
-
-   == 
-   0  HPB is not enabled.
-   1  HPB is enabled
-   == 
-
-   The file is read only.
+What:  /sys/class/scsi_device/*/device/hpb_param_sysfs/activation_thld
+Date:  February 2021
+Contact:   Avri Altman 
+Description:   In host control mode, reads are the major source of activation
+   trials.  once this threshold hs met, the region is added to the
+   "to-be-activated" list.  Since we reset the read counter upon
+   write, this include sending a rb command updating the region
+   ppn as well.
+
+What:  
/sys/class/scsi_device/*/device/hpb_param_sysfs/normalization_factor
+Date:  February 2021
+Contact:   Avri Altman 
+Description:   In host control mode, We think of the regions as "buckets".
+   Those buckets are being filled with reads, and emptied on write.
+   We use entries_per_srgn - the amount of blocks in a subregion as
+   our bucket size.  This applies because HPB1.0 only concern a
+   single-block reads.  Once the bucket size is crossed, we trigger
+   a normalization work - not only to avoid overflow, but mainly
+   because we want to keep those counters normalized, as we are
+   using those reads as a comparative score, to make various 
decisions.
+   The normalization is dividing (shift right) the read counter by
+   the normalization_factor. If during consecutive normalizations
+   an active region has exhaust its reads - inactivate it.
+
+What:  
/sys/class/scsi_device/*/device/hpb_param_sysfs/eviction_thld_enter
+Date:  February 2021
+Contact:   Avri Altman 
+Description:   Region deactivation is often due to the fact that eviction took
+   place: a region become active on the expense of another. This is
+   happening when the max-active-regions limit has crossed.
+   In host mode, eviction is considered an extreme measure. We
+   want to verify that the entering region has enough reads, and
+   the exiting region has much less reads.  eviction_thld_enter is
+   the min reads that a region must have in order to be considered
+   as a candidate to evict other region.
+
+What:  
/sys/class/scsi_device/*/device/hpb_param_sysfs/eviction_thld_exit
+Date:  February 2021
+Contact:   Avri Altman 
+Description:   same as above for the exiting region. A region is consider to
+   be a candidate to be evicted, only if it has less reads than
+   eviction_thld_exit.
+
+What:  /sys/class/scsi_device/*/device/hpb_param_sysfs/read_timeout_ms
+Date:  February 2021
+Contact:   Avri Altman 
+Description:   In order not to hang on to “cold” regions, we shall inactivate
+   a region that has no READ access for a predefined amount of
+   time - read_timeout_ms. If read_timeout_ms has expired, and the
+   region is dirty - it is less likely that we can make any use of
+   HPB-READing it.  So we inactivate it.  Still, deactivation has
+   its overhead, and we may still benefit from HPB-READing this
+   region if it is clean - see read_timeout_expiries.
+
+What:  
/sys/class/scsi_device/*/device/hpb_param_sysfs/read_timeout_expiries
+Date:  February 2021
+Contact:   Avri Altman 
+Description:   if the region read timeout has expired, but the region is clean,
+   just re-wind its timer for another spin.  Do that as long as it
+   is clean and did not exhaust its read_timeout_expiries 
threshold.
+
+What:  
/sys/class/scsi_device/*/de

[PATCH v8 04/11] scsi: ufshpb: Add reads counter

2021-04-11 Thread Avri Altman
In host control mode, reads are the major source of activation trials.
Keep track of those reads counters, for both active as well inactive
regions.

We reset the read counter upon write - we are only interested in "clean"
reads.

Keep those counters normalized, as we are using those reads as a
comparative score, to make various decisions.
If during consecutive normalizations an active region has exhaust its
reads - inactivate it.

while at it, protect the {active,inactive}_count stats by adding them
into the applicable handler.

Signed-off-by: Avri Altman 
Reviewed-by: Daejun Park 
---
 drivers/scsi/ufs/ufshpb.c | 94 ---
 drivers/scsi/ufs/ufshpb.h |  9 
 2 files changed, 97 insertions(+), 6 deletions(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 252fcfb48862..3ab66421dc00 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -16,6 +16,8 @@
 #include "ufshpb.h"
 #include "../sd.h"
 
+#define ACTIVATION_THRESHOLD 8 /* 8 IOs */
+
 /* memory management */
 static struct kmem_cache *ufshpb_mctx_cache;
 static mempool_t *ufshpb_mctx_pool;
@@ -26,6 +28,9 @@ static int tot_active_srgn_pages;
 
 static struct workqueue_struct *ufshpb_wq;
 
+static void ufshpb_update_active_info(struct ufshpb_lu *hpb, int rgn_idx,
+ int srgn_idx);
+
 bool ufshpb_is_allowed(struct ufs_hba *hba)
 {
return !(hba->ufshpb_dev.hpb_disabled);
@@ -148,7 +153,7 @@ static void ufshpb_iterate_rgn(struct ufshpb_lu *hpb, int 
rgn_idx, int srgn_idx,
   int srgn_offset, int cnt, bool set_dirty)
 {
struct ufshpb_region *rgn;
-   struct ufshpb_subregion *srgn;
+   struct ufshpb_subregion *srgn, *prev_srgn = NULL;
int set_bit_len;
int bitmap_len;
unsigned long flags;
@@ -167,15 +172,39 @@ static void ufshpb_iterate_rgn(struct ufshpb_lu *hpb, int 
rgn_idx, int srgn_idx,
else
set_bit_len = cnt;
 
-   if (set_dirty)
-   set_bit(RGN_FLAG_DIRTY, >rgn_flags);
-
spin_lock_irqsave(>rgn_state_lock, flags);
if (set_dirty && rgn->rgn_state != HPB_RGN_INACTIVE &&
srgn->srgn_state == HPB_SRGN_VALID)
bitmap_set(srgn->mctx->ppn_dirty, srgn_offset, set_bit_len);
spin_unlock_irqrestore(>rgn_state_lock, flags);
 
+   if (hpb->is_hcm && prev_srgn != srgn) {
+   bool activate = false;
+
+   spin_lock(>rgn_lock);
+   if (set_dirty) {
+   rgn->reads -= srgn->reads;
+   srgn->reads = 0;
+   set_bit(RGN_FLAG_DIRTY, >rgn_flags);
+   } else {
+   srgn->reads++;
+   rgn->reads++;
+   if (srgn->reads == ACTIVATION_THRESHOLD)
+   activate = true;
+   }
+   spin_unlock(>rgn_lock);
+
+   if (activate) {
+   spin_lock_irqsave(>rsp_list_lock, flags);
+   ufshpb_update_active_info(hpb, rgn_idx, srgn_idx);
+   spin_unlock_irqrestore(>rsp_list_lock, flags);
+   dev_dbg(>sdev_ufs_lu->sdev_dev,
+   "activate region %d-%d\n", rgn_idx, srgn_idx);
+   }
+
+   prev_srgn = srgn;
+   }
+
srgn_offset = 0;
if (++srgn_idx == hpb->srgns_per_rgn) {
srgn_idx = 0;
@@ -604,6 +633,19 @@ int ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb 
*lrbp)
if (!ufshpb_is_support_chunk(hpb, transfer_len))
return 0;
 
+   if (hpb->is_hcm) {
+   /*
+* in host control mode, reads are the main source for
+* activation trials.
+*/
+   ufshpb_iterate_rgn(hpb, rgn_idx, srgn_idx, srgn_offset,
+  transfer_len, false);
+
+   /* keep those counters normalized */
+   if (rgn->reads > hpb->entries_per_srgn)
+   schedule_work(>ufshpb_normalization_work);
+   }
+
spin_lock_irqsave(>rgn_state_lock, flags);
if (ufshpb_test_ppn_dirty(hpb, rgn_idx, srgn_idx, srgn_offset,
   transfer_len)) {
@@ -755,6 +797,8 @@ static void ufshpb_update_active_info(struct ufshpb_lu 
*hpb, int rgn_idx,
 
if (list_empty(>list_act_srgn))
list_add_tail(>list_act_srgn, >lh_act_srgn);
+
+   hpb->stats.rb_active_cnt++;
 }
 
 static void ufshpb_update_inactive_info(struct ufshpb_lu *hpb, int rgn_idx)
@@ -770,6 +814,8 @@ static void ufshpb_update_inactive_info(struct ufshpb_lu 
*hpb, int rgn_idx)
 
if (list_empty(>list_inact_rgn))
  

[PATCH v8 10/11] scsi: ufshpb: Add support for host control mode

2021-04-11 Thread Avri Altman
Support devices that report they are using host control mode.

Signed-off-by: Avri Altman 
Reviewed-by: Daejun Park 
---
 drivers/scsi/ufs/ufshpb.c | 6 --
 1 file changed, 6 deletions(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 0f72ea3f5d71..3c4001486cf1 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -2587,12 +2587,6 @@ void ufshpb_get_dev_info(struct ufs_hba *hba, u8 
*desc_buf)
u32 max_hpb_single_cmd = HPB_MULTI_CHUNK_LOW;
 
hpb_dev_info->control_mode = desc_buf[DEVICE_DESC_PARAM_HPB_CONTROL];
-   if (hpb_dev_info->control_mode == HPB_HOST_CONTROL) {
-   dev_err(hba->dev, "%s: host control mode is not supported.\n",
-   __func__);
-   hpb_dev_info->hpb_disabled = true;
-   return;
-   }
 
version = get_unaligned_be16(desc_buf + DEVICE_DESC_PARAM_HPB_VER);
if ((version != HPB_SUPPORT_VERSION) &&
-- 
2.25.1



[PATCH v8 07/11] scsi: ufshpb: Add hpb dev reset response

2021-04-11 Thread Avri Altman
The spec does not define what is the host's recommended response when
the device send hpb dev reset response (oper 0x2).

We will update all active hpb regions: mark them and do that on the next
read.

Signed-off-by: Avri Altman 
Reviewed-by: Daejun Park 
---
 drivers/scsi/ufs/ufshpb.c | 32 +++-
 drivers/scsi/ufs/ufshpb.h |  1 +
 2 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 28aef5acffa2..0352d269c1e9 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -195,7 +195,8 @@ static void ufshpb_iterate_rgn(struct ufshpb_lu *hpb, int 
rgn_idx, int srgn_idx,
}
spin_unlock(>rgn_lock);
 
-   if (activate) {
+   if (activate ||
+   test_and_clear_bit(RGN_FLAG_UPDATE, >rgn_flags)) {
spin_lock_irqsave(>rsp_list_lock, flags);
ufshpb_update_active_info(hpb, rgn_idx, srgn_idx);
spin_unlock_irqrestore(>rsp_list_lock, flags);
@@ -1417,6 +1418,20 @@ static void ufshpb_rsp_req_region_update(struct 
ufshpb_lu *hpb,
queue_work(ufshpb_wq, >map_work);
 }
 
+static void ufshpb_dev_reset_handler(struct ufshpb_lu *hpb)
+{
+   struct victim_select_info *lru_info = >lru_info;
+   struct ufshpb_region *rgn;
+   unsigned long flags;
+
+   spin_lock_irqsave(>rgn_state_lock, flags);
+
+   list_for_each_entry(rgn, _info->lh_lru_rgn, list_lru_rgn)
+   set_bit(RGN_FLAG_UPDATE, >rgn_flags);
+
+   spin_unlock_irqrestore(>rgn_state_lock, flags);
+}
+
 /*
  * This function will parse recommended active subregion information in sense
  * data field of response UPIU with SAM_STAT_GOOD state.
@@ -1491,6 +1506,18 @@ void ufshpb_rsp_upiu(struct ufs_hba *hba, struct 
ufshcd_lrb *lrbp)
case HPB_RSP_DEV_RESET:
dev_warn(>sdev_ufs_lu->sdev_dev,
 "UFS device lost HPB information during PM.\n");
+
+   if (hpb->is_hcm) {
+   struct scsi_device *sdev;
+
+   __shost_for_each_device(sdev, hba->host) {
+   struct ufshpb_lu *h = sdev->hostdata;
+
+   if (h)
+   ufshpb_dev_reset_handler(h);
+   }
+   }
+
break;
default:
dev_notice(>sdev_ufs_lu->sdev_dev,
@@ -1817,6 +1844,8 @@ static int ufshpb_alloc_region_tbl(struct ufs_hba *hba, 
struct ufshpb_lu *hpb)
} else {
rgn->rgn_state = HPB_RGN_INACTIVE;
}
+
+   rgn->rgn_flags = 0;
}
 
return 0;
@@ -2144,6 +2173,7 @@ static void ufshpb_cancel_jobs(struct ufshpb_lu *hpb)
 {
if (hpb->is_hcm)
cancel_work_sync(>ufshpb_normalization_work);
+
cancel_work_sync(>map_work);
 }
 
diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
index 1ea58c17a4de..b863540e28d6 100644
--- a/drivers/scsi/ufs/ufshpb.h
+++ b/drivers/scsi/ufs/ufshpb.h
@@ -127,6 +127,7 @@ struct ufshpb_region {
struct list_head list_lru_rgn;
unsigned long rgn_flags;
 #define RGN_FLAG_DIRTY 0
+#define RGN_FLAG_UPDATE 1
 
/* region reads - for host mode */
spinlock_t rgn_lock;
-- 
2.25.1



[PATCH v8 02/11] scsi: ufshpb: Add host control mode support to rsp_upiu

2021-04-11 Thread Avri Altman
In device control mode, the device may recommend the host to either
activate or inactivate a region, and the host should follow. Meaning
those are not actually recommendations, but more of instructions.

On the contrary, in host control mode, the recommendation protocol is
slightly changed:
a) The device may only recommend the host to update a subregion of an
   already-active region. And,
b) The device may *not* recommend to inactivate a region.

Furthermore, in host control mode, the host may choose not to follow any
of the device's recommendations. However, in case of a recommendation to
update an active and clean subregion, it is better to follow those
recommendation because otherwise the host has no other way to know that
some internal relocation took place.

Signed-off-by: Avri Altman 
Reviewed-by: Daejun Park 
---
 drivers/scsi/ufs/ufshpb.c | 34 +-
 drivers/scsi/ufs/ufshpb.h |  2 ++
 2 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 5285a50b05dd..6111019ca31a 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -166,6 +166,8 @@ static void ufshpb_set_ppn_dirty(struct ufshpb_lu *hpb, int 
rgn_idx,
else
set_bit_len = cnt;
 
+   set_bit(RGN_FLAG_DIRTY, >rgn_flags);
+
if (rgn->rgn_state != HPB_RGN_INACTIVE &&
srgn->srgn_state == HPB_SRGN_VALID)
bitmap_set(srgn->mctx->ppn_dirty, srgn_offset, set_bit_len);
@@ -235,6 +237,11 @@ static bool ufshpb_test_ppn_dirty(struct ufshpb_lu *hpb, 
int rgn_idx,
return false;
 }
 
+static inline bool is_rgn_dirty(struct ufshpb_region *rgn)
+{
+   return test_bit(RGN_FLAG_DIRTY, >rgn_flags);
+}
+
 static int ufshpb_fill_ppn_from_page(struct ufshpb_lu *hpb,
 struct ufshpb_map_ctx *mctx, int pos,
 int len, u64 *ppn_buf)
@@ -712,6 +719,7 @@ static void ufshpb_put_map_req(struct ufshpb_lu *hpb,
 static int ufshpb_clear_dirty_bitmap(struct ufshpb_lu *hpb,
 struct ufshpb_subregion *srgn)
 {
+   struct ufshpb_region *rgn;
u32 num_entries = hpb->entries_per_srgn;
 
if (!srgn->mctx) {
@@ -725,6 +733,10 @@ static int ufshpb_clear_dirty_bitmap(struct ufshpb_lu *hpb,
num_entries = hpb->last_srgn_entries;
 
bitmap_zero(srgn->mctx->ppn_dirty, num_entries);
+
+   rgn = hpb->rgn_tbl + srgn->rgn_idx;
+   clear_bit(RGN_FLAG_DIRTY, >rgn_flags);
+
return 0;
 }
 
@@ -1244,6 +1256,18 @@ static void ufshpb_rsp_req_region_update(struct 
ufshpb_lu *hpb,
srgn_i =
be16_to_cpu(rsp_field->hpb_active_field[i].active_srgn);
 
+   rgn = hpb->rgn_tbl + rgn_i;
+   if (hpb->is_hcm &&
+   (rgn->rgn_state != HPB_RGN_ACTIVE || is_rgn_dirty(rgn))) {
+   /*
+* in host control mode, subregion activation
+* recommendations are only allowed to active regions.
+* Also, ignore recommendations for dirty regions - the
+* host will make decisions concerning those by himself
+*/
+   continue;
+   }
+
dev_dbg(>sdev_ufs_lu->sdev_dev,
"activate(%d) region %d - %d\n", i, rgn_i, srgn_i);
 
@@ -1251,7 +1275,6 @@ static void ufshpb_rsp_req_region_update(struct ufshpb_lu 
*hpb,
ufshpb_update_active_info(hpb, rgn_i, srgn_i);
spin_unlock(>rsp_list_lock);
 
-   rgn = hpb->rgn_tbl + rgn_i;
srgn = rgn->srgn_tbl + srgn_i;
 
/* blocking HPB_READ */
@@ -1262,6 +1285,14 @@ static void ufshpb_rsp_req_region_update(struct 
ufshpb_lu *hpb,
hpb->stats.rb_active_cnt++;
}
 
+   if (hpb->is_hcm) {
+   /*
+* in host control mode the device is not allowed to inactivate
+* regions
+*/
+   goto out;
+   }
+
for (i = 0; i < rsp_field->inactive_rgn_cnt; i++) {
rgn_i = be16_to_cpu(rsp_field->hpb_inactive_field[i]);
dev_dbg(>sdev_ufs_lu->sdev_dev,
@@ -1286,6 +1317,7 @@ static void ufshpb_rsp_req_region_update(struct ufshpb_lu 
*hpb,
hpb->stats.rb_inactive_cnt++;
}
 
+out:
dev_dbg(>sdev_ufs_lu->sdev_dev, "Noti: #ACT %u #INACT %u\n",
rsp_field->active_rgn_cnt, rsp_field->inactive_rgn_cnt);
 
diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
index 7df30340386a..032672114881 100644
--- a/drivers/scsi/ufs/ufshpb.h
+++ b/drivers/scsi/ufs/ufshpb.h
@@ -121,6 +121,8 @@ struct ufsh

[PATCH v8 06/11] scsi: ufshpb: Region inactivation in host mode

2021-04-11 Thread Avri Altman
In host mode, the host is expected to send HPB-WRITE-BUFFER with
buffer-id = 0x1 when it inactivates a region.

Use the map-requests pool as there is no point in assigning a
designated cache for umap-requests.

Signed-off-by: Avri Altman 
Reviewed-by: Daejun Park 
---
 drivers/scsi/ufs/ufshpb.c | 46 +--
 drivers/scsi/ufs/ufshpb.h |  1 +
 2 files changed, 40 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index aefb6dc160ee..28aef5acffa2 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -691,7 +691,8 @@ int ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb 
*lrbp)
 }
 
 static struct ufshpb_req *ufshpb_get_req(struct ufshpb_lu *hpb,
-int rgn_idx, enum req_opf dir)
+int rgn_idx, enum req_opf dir,
+bool atomic)
 {
struct ufshpb_req *rq;
struct request *req;
@@ -705,7 +706,7 @@ static struct ufshpb_req *ufshpb_get_req(struct ufshpb_lu 
*hpb,
req = blk_get_request(hpb->sdev_ufs_lu->request_queue, dir,
  BLK_MQ_REQ_NOWAIT);
 
-   if ((PTR_ERR(req) == -EWOULDBLOCK) && (--retries > 0)) {
+   if (!atomic && (PTR_ERR(req) == -EWOULDBLOCK) && (--retries > 0)) {
usleep_range(3000, 3100);
goto retry;
}
@@ -736,7 +737,7 @@ static struct ufshpb_req *ufshpb_get_map_req(struct 
ufshpb_lu *hpb,
struct ufshpb_req *map_req;
struct bio *bio;
 
-   map_req = ufshpb_get_req(hpb, srgn->rgn_idx, REQ_OP_SCSI_IN);
+   map_req = ufshpb_get_req(hpb, srgn->rgn_idx, REQ_OP_SCSI_IN, false);
if (!map_req)
return NULL;
 
@@ -914,6 +915,7 @@ static int ufshpb_execute_umap_req(struct ufshpb_lu *hpb,
 
blk_execute_rq_nowait(NULL, req, 1, ufshpb_umap_req_compl_fn);
 
+   hpb->stats.umap_req_cnt++;
return 0;
 }
 
@@ -1091,12 +1093,13 @@ static void ufshpb_purge_active_subregion(struct 
ufshpb_lu *hpb,
 }
 
 static int ufshpb_issue_umap_req(struct ufshpb_lu *hpb,
-struct ufshpb_region *rgn)
+struct ufshpb_region *rgn,
+bool atomic)
 {
struct ufshpb_req *umap_req;
int rgn_idx = rgn ? rgn->rgn_idx : 0;
 
-   umap_req = ufshpb_get_req(hpb, rgn_idx, REQ_OP_SCSI_OUT);
+   umap_req = ufshpb_get_req(hpb, rgn_idx, REQ_OP_SCSI_OUT, atomic);
if (!umap_req)
return -ENOMEM;
 
@@ -1110,13 +1113,19 @@ static int ufshpb_issue_umap_req(struct ufshpb_lu *hpb,
return -EAGAIN;
 }
 
+static int ufshpb_issue_umap_single_req(struct ufshpb_lu *hpb,
+   struct ufshpb_region *rgn)
+{
+   return ufshpb_issue_umap_req(hpb, rgn, true);
+}
+
 static int ufshpb_issue_umap_all_req(struct ufshpb_lu *hpb)
 {
-   return ufshpb_issue_umap_req(hpb, NULL);
+   return ufshpb_issue_umap_req(hpb, NULL, false);
 }
 
 static void __ufshpb_evict_region(struct ufshpb_lu *hpb,
- struct ufshpb_region *rgn)
+struct ufshpb_region *rgn)
 {
struct victim_select_info *lru_info;
struct ufshpb_subregion *srgn;
@@ -1151,6 +1160,14 @@ static int ufshpb_evict_region(struct ufshpb_lu *hpb, 
struct ufshpb_region *rgn)
goto out;
}
 
+   if (hpb->is_hcm) {
+   spin_unlock_irqrestore(>rgn_state_lock, flags);
+   ret = ufshpb_issue_umap_single_req(hpb, rgn);
+   spin_lock_irqsave(>rgn_state_lock, flags);
+   if (ret)
+   goto out;
+   }
+
__ufshpb_evict_region(hpb, rgn);
}
 out:
@@ -1285,6 +1302,18 @@ static int ufshpb_add_region(struct ufshpb_lu *hpb, 
struct ufshpb_region *rgn)
"LRU full (%d), choose victim %d\n",
atomic_read(_info->active_cnt),
victim_rgn->rgn_idx);
+
+   if (hpb->is_hcm) {
+   spin_unlock_irqrestore(>rgn_state_lock,
+  flags);
+   ret = ufshpb_issue_umap_single_req(hpb,
+   victim_rgn);
+   spin_lock_irqsave(>rgn_state_lock,
+ flags);
+   if (ret)
+   goto out;
+   }
+
__ufshpb_evict_region(hpb, victim_rgn);
}
 
@@ -1856,6 +1885,7 @@ ufshpb_sysfs_attr_show_func(rb_no

[PATCH v8 03/11] scsi: ufshpb: Transform set_dirty to iterate_rgn

2021-04-11 Thread Avri Altman
Given a transfer length, set_dirty meticulously runs over all the
entries, across subregions and regions if needed. Currently its only use
is to mark dirty blocks, but soon HCM may profit from it as well, when
managing its read counters.

Signed-off-by: Avri Altman 
Reviewed-by: Daejun Park 
---
 drivers/scsi/ufs/ufshpb.c | 18 ++
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 6111019ca31a..252fcfb48862 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -144,13 +144,14 @@ static bool ufshpb_is_hpb_rsp_valid(struct ufs_hba *hba,
return true;
 }
 
-static void ufshpb_set_ppn_dirty(struct ufshpb_lu *hpb, int rgn_idx,
-int srgn_idx, int srgn_offset, int cnt)
+static void ufshpb_iterate_rgn(struct ufshpb_lu *hpb, int rgn_idx, int 
srgn_idx,
+  int srgn_offset, int cnt, bool set_dirty)
 {
struct ufshpb_region *rgn;
struct ufshpb_subregion *srgn;
int set_bit_len;
int bitmap_len;
+   unsigned long flags;
 
 next_srgn:
rgn = hpb->rgn_tbl + rgn_idx;
@@ -166,11 +167,14 @@ static void ufshpb_set_ppn_dirty(struct ufshpb_lu *hpb, 
int rgn_idx,
else
set_bit_len = cnt;
 
-   set_bit(RGN_FLAG_DIRTY, >rgn_flags);
+   if (set_dirty)
+   set_bit(RGN_FLAG_DIRTY, >rgn_flags);
 
-   if (rgn->rgn_state != HPB_RGN_INACTIVE &&
+   spin_lock_irqsave(>rgn_state_lock, flags);
+   if (set_dirty && rgn->rgn_state != HPB_RGN_INACTIVE &&
srgn->srgn_state == HPB_SRGN_VALID)
bitmap_set(srgn->mctx->ppn_dirty, srgn_offset, set_bit_len);
+   spin_unlock_irqrestore(>rgn_state_lock, flags);
 
srgn_offset = 0;
if (++srgn_idx == hpb->srgns_per_rgn) {
@@ -592,10 +596,8 @@ int ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb 
*lrbp)
 
/* If command type is WRITE or DISCARD, set bitmap as drity */
if (ufshpb_is_write_or_discard_cmd(cmd)) {
-   spin_lock_irqsave(>rgn_state_lock, flags);
-   ufshpb_set_ppn_dirty(hpb, rgn_idx, srgn_idx, srgn_offset,
-transfer_len);
-   spin_unlock_irqrestore(>rgn_state_lock, flags);
+   ufshpb_iterate_rgn(hpb, rgn_idx, srgn_idx, srgn_offset,
+  transfer_len, true);
return 0;
}
 
-- 
2.25.1



[PATCH v8 05/11] scsi: ufshpb: Make eviction depends on region's reads

2021-04-11 Thread Avri Altman
In host mode, eviction is considered an extreme measure.
verify that the entering region has enough reads, and the exiting
region has much less reads.

Signed-off-by: Avri Altman 
Reviewed-by: Daejun Park 
---
 drivers/scsi/ufs/ufshpb.c | 18 +-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 3ab66421dc00..aefb6dc160ee 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -17,6 +17,7 @@
 #include "../sd.h"
 
 #define ACTIVATION_THRESHOLD 8 /* 8 IOs */
+#define EVICTION_THRESHOLD (ACTIVATION_THRESHOLD << 5) /* 256 IOs */
 
 /* memory management */
 static struct kmem_cache *ufshpb_mctx_cache;
@@ -1057,6 +1058,13 @@ static struct ufshpb_region 
*ufshpb_victim_lru_info(struct ufshpb_lu *hpb)
if (ufshpb_check_srgns_issue_state(hpb, rgn))
continue;
 
+   /*
+* in host control mode, verify that the exiting region
+* has less reads
+*/
+   if (hpb->is_hcm && rgn->reads > (EVICTION_THRESHOLD >> 1))
+   continue;
+
victim_rgn = rgn;
break;
}
@@ -1229,7 +1237,7 @@ static int ufshpb_issue_map_req(struct ufshpb_lu *hpb,
 
 static int ufshpb_add_region(struct ufshpb_lu *hpb, struct ufshpb_region *rgn)
 {
-   struct ufshpb_region *victim_rgn;
+   struct ufshpb_region *victim_rgn = NULL;
struct victim_select_info *lru_info = >lru_info;
unsigned long flags;
int ret = 0;
@@ -1256,7 +1264,15 @@ static int ufshpb_add_region(struct ufshpb_lu *hpb, 
struct ufshpb_region *rgn)
 * It is okay to evict the least recently used region,
 * because the device could detect this region
 * by not issuing HPB_READ
+*
+* in host control mode, verify that the entering
+* region has enough reads
 */
+   if (hpb->is_hcm && rgn->reads < EVICTION_THRESHOLD) {
+   ret = -EACCES;
+   goto out;
+   }
+
victim_rgn = ufshpb_victim_lru_info(hpb);
if (!victim_rgn) {
dev_warn(>sdev_ufs_lu->sdev_dev,
-- 
2.25.1



[PATCH v8 00/11] Add Host control mode to HPB

2021-04-11 Thread Avri Altman
v7 -> v8:
 - restore Daejun atomic argument to ufshpb_get_req (v31)
 - Add Daejun's Reviewed-by tag

v6 -> v7:
 - attend CanG's comments
 - add one more patch to transform set_dirty to iterate_rgn
 - rebase on Daejun's v32

v5 -> v6:
 - attend CanG's comments
 - rebase on Daejun's v29

v4 -> v5:
 - attend Daejun's comments
 - Control the number of inflight map requests

v3 -> v4:
 - rebase on Daejun's v25

v2 -> v3:
 - Attend Greg's and Can's comments
 - rebase on Daejun's v21

v1 -> v2:
 - attend Greg's and Daejun's comments
 - add patch 9 making host mode parameters configurable
 - rebase on Daejun's v19


The HPB spec defines 2 control modes - device control mode and host
control mode. In oppose to device control mode, in which the host obey
to whatever recommendation received from the device - In host control
mode, the host uses its own algorithms to decide which regions should
be activated or inactivated.

We kept the host managed heuristic simple and concise.

Aside from adding a by-spec functionality, host control mode entails
some further potential benefits: makes the hpb logic transparent and
readable, while allow tuning / scaling its various parameters, and
utilize system-wide info to optimize HPB potential.

This series is based on Samsung's V32 device-control HPB2.0 driver

This version was tested on Galaxy S20, and Xiaomi Mi10 pro.
Your meticulous review and testing is mostly welcome and appreciated.

Thanks,
Avri

Avri Altman (11):
  scsi: ufshpb: Cache HPB Control mode on init
  scsi: ufshpb: Add host control mode support to rsp_upiu
  scsi: ufshpb: Transform set_dirty to iterate_rgn
  scsi: ufshpb: Add reads counter
  scsi: ufshpb: Make eviction depends on region's reads
  scsi: ufshpb: Region inactivation in host mode
  scsi: ufshpb: Add hpb dev reset response
  scsi: ufshpb: Add "Cold" regions timer
  scsi: ufshpb: Limit the number of inflight map requests
  scsi: ufshpb: Add support for host control mode
  scsi: ufshpb: Make host mode parameters configurable

 Documentation/ABI/testing/sysfs-driver-ufs |  84 ++-
 drivers/scsi/ufs/ufshcd.h  |   2 +
 drivers/scsi/ufs/ufshpb.c  | 566 -
 drivers/scsi/ufs/ufshpb.h  |  44 ++
 4 files changed, 661 insertions(+), 35 deletions(-)

-- 
2.25.1



[PATCH v8 01/11] scsi: ufshpb: Cache HPB Control mode on init

2021-04-11 Thread Avri Altman
We will use it later, when we'll need to differentiate between device
and host control modes.

Signed-off-by: Avri Altman 
Reviewed-by: Daejun Park 
---
 drivers/scsi/ufs/ufshcd.h | 2 ++
 drivers/scsi/ufs/ufshpb.c | 8 +---
 drivers/scsi/ufs/ufshpb.h | 2 ++
 3 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 4dbe9bc60e85..c01f75963750 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -656,6 +656,7 @@ struct ufs_hba_variant_params {
  * @hpb_disabled: flag to check if HPB is disabled
  * @max_hpb_single_cmd: maximum size of single HPB command
  * @is_legacy: flag to check HPB 1.0
+ * @control_mode: either host or device
  */
 struct ufshpb_dev_info {
int num_lu;
@@ -665,6 +666,7 @@ struct ufshpb_dev_info {
bool hpb_disabled;
int max_hpb_single_cmd;
bool is_legacy;
+   u8 control_mode;
 };
 #endif
 
diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 86805af9abe7..5285a50b05dd 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -1615,6 +1615,9 @@ static void ufshpb_lu_parameter_init(struct ufs_hba *hba,
 % (hpb->srgn_mem_size / HPB_ENTRY_SIZE);
 
hpb->pages_per_srgn = DIV_ROUND_UP(hpb->srgn_mem_size, PAGE_SIZE);
+
+   if (hpb_dev_info->control_mode == HPB_HOST_CONTROL)
+   hpb->is_hcm = true;
 }
 
 static int ufshpb_alloc_region_tbl(struct ufs_hba *hba, struct ufshpb_lu *hpb)
@@ -2308,11 +2311,10 @@ void ufshpb_get_dev_info(struct ufs_hba *hba, u8 
*desc_buf)
 {
struct ufshpb_dev_info *hpb_dev_info = >ufshpb_dev;
int version, ret;
-   u8 hpb_mode;
u32 max_hpb_single_cmd = HPB_MULTI_CHUNK_LOW;
 
-   hpb_mode = desc_buf[DEVICE_DESC_PARAM_HPB_CONTROL];
-   if (hpb_mode == HPB_HOST_CONTROL) {
+   hpb_dev_info->control_mode = desc_buf[DEVICE_DESC_PARAM_HPB_CONTROL];
+   if (hpb_dev_info->control_mode == HPB_HOST_CONTROL) {
dev_err(hba->dev, "%s: host control mode is not supported.\n",
__func__);
hpb_dev_info->hpb_disabled = true;
diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
index b1128b0ce486..7df30340386a 100644
--- a/drivers/scsi/ufs/ufshpb.h
+++ b/drivers/scsi/ufs/ufshpb.h
@@ -228,6 +228,8 @@ struct ufshpb_lu {
u32 entries_per_srgn_shift;
u32 pages_per_srgn;
 
+   bool is_hcm;
+
struct ufshpb_stats stats;
struct ufshpb_params params;
 
-- 
2.25.1



RE: [PATCH v7 06/11] scsi: ufshpb: Region inactivation in host mode

2021-04-09 Thread Avri Altman


> > >> On 2021-04-06 13:20, Avri Altman wrote:
> > >> >> > -static void __ufshpb_evict_region(struct ufshpb_lu *hpb,
> > >> >> > -   struct ufshpb_region *rgn)
> > >> >> > +static int __ufshpb_evict_region(struct ufshpb_lu *hpb,
> > >> >> > +  struct ufshpb_region *rgn)
> > >> >> >  {
> > >> >> >   struct victim_select_info *lru_info;
> > >> >> >   struct ufshpb_subregion *srgn;
> > >> >> >   int srgn_idx;
> > >> >> >
> > >> >> > + lockdep_assert_held(>rgn_state_lock);
> > >> >> > +
> > >> >> > + if (hpb->is_hcm) {
> > >> >> > + unsigned long flags;
> > >> >> > + int ret;
> > >> >> > +
> > >> >> > + spin_unlock_irqrestore(>rgn_state_lock, flags);
> > >> >>
> > >> >> Never seen a usage like this... Here flags is used without being
> > >> >> intialized.
> > >> >> The flag is needed when spin_unlock_irqrestore ->
> > >> >> local_irq_restore(flags) to
> > >> >> restore the DAIF register (in terms of ARM).
> > >> > OK.
> > >>
> > >> Hi Avri,
> > >>
> > >> Checked on my setup, this lead to compilation error. Will you fix it
> > >> in
> > >> next version?
> > >>
> > >> warning: variable 'flags' is uninitialized when used here
> > >> [-Wuninitialized]
> > > Yeah - I will pass it to __ufshpb_evict_region and drop the
> > > lockdep_assert call.
Please ignore it.  This of course won't do.
Will fix it in v8.

Thanks,
Avri


RE: [PATCH v7 06/11] scsi: ufshpb: Region inactivation in host mode

2021-04-06 Thread Avri Altman
> >>
> >> On 2021-04-06 13:20, Avri Altman wrote:
> >> >> > -static void __ufshpb_evict_region(struct ufshpb_lu *hpb,
> >> >> > -   struct ufshpb_region *rgn)
> >> >> > +static int __ufshpb_evict_region(struct ufshpb_lu *hpb,
> >> >> > +  struct ufshpb_region *rgn)
> >> >> >  {
> >> >> >   struct victim_select_info *lru_info;
> >> >> >   struct ufshpb_subregion *srgn;
> >> >> >   int srgn_idx;
> >> >> >
> >> >> > + lockdep_assert_held(>rgn_state_lock);
> >> >> > +
> >> >> > + if (hpb->is_hcm) {
> >> >> > + unsigned long flags;
> >> >> > + int ret;
> >> >> > +
> >> >> > + spin_unlock_irqrestore(>rgn_state_lock, flags);
> >> >>
> >> >> Never seen a usage like this... Here flags is used without being
> >> >> intialized.
> >> >> The flag is needed when spin_unlock_irqrestore ->
> >> >> local_irq_restore(flags) to
> >> >> restore the DAIF register (in terms of ARM).
> >> > OK.
> >>
> >> Hi Avri,
> >>
> >> Checked on my setup, this lead to compilation error. Will you fix it
> >> in
> >> next version?
> >>
> >> warning: variable 'flags' is uninitialized when used here
> >> [-Wuninitialized]
> > Yeah - I will pass it to __ufshpb_evict_region and drop the
> > lockdep_assert call.
> >
> 
> Please paste the sample code/change here so that I can move forward
> quickly.
Thanks a lot.  Also attaching the patch if its more convenient.

Thanks,
Avri

$ git show 5d33d36e8704
commit 5d33d36e87047d27a546ad3529cb7837186b47b2
Author: Avri Altman 
Date:   Tue Jun 30 15:14:31 2020 +0300

scsi: ufshpb: Region inactivation in host mode

In host mode, the host is expected to send HPB-WRITE-BUFFER with
buffer-id = 0x1 when it inactivates a region.

Use the map-requests pool as there is no point in assigning a
designated cache for umap-requests.

Signed-off-by: Avri Altman 
Reviewed-by: Daejun Park 
Change-Id: I1a6696b38d4abfb4d9fbe44e84016a6238825125

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index aefb6dc160ee..54a3ea9f5732 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -914,6 +914,7 @@ static int ufshpb_execute_umap_req(struct ufshpb_lu *hpb,
 
blk_execute_rq_nowait(NULL, req, 1, ufshpb_umap_req_compl_fn);
 
+   hpb->stats.umap_req_cnt++;
return 0;
 }
 
@@ -1110,18 +,35 @@ static int ufshpb_issue_umap_req(struct ufshpb_lu *hpb,
return -EAGAIN;
 }
 
+static int ufshpb_issue_umap_single_req(struct ufshpb_lu *hpb,
+   struct ufshpb_region *rgn)
+{
+   return ufshpb_issue_umap_req(hpb, rgn);
+}
+
 static int ufshpb_issue_umap_all_req(struct ufshpb_lu *hpb)
 {
return ufshpb_issue_umap_req(hpb, NULL);
 }
 
-static void __ufshpb_evict_region(struct ufshpb_lu *hpb,
- struct ufshpb_region *rgn)
+static int __ufshpb_evict_region(struct ufshpb_lu *hpb,
+struct ufshpb_region *rgn,
+unsigned long flags)
 {
struct victim_select_info *lru_info;
struct ufshpb_subregion *srgn;
int srgn_idx;
 
+   if (hpb->is_hcm) {
+   int ret;
+
+   spin_unlock_irqrestore(>rgn_state_lock, flags);
+   ret = ufshpb_issue_umap_single_req(hpb, rgn);
+   spin_lock_irqsave(>rgn_state_lock, flags);
+   if (ret)
+   return ret;
+   }
+
lru_info = >lru_info;
 
dev_dbg(>sdev_ufs_lu->sdev_dev, "evict region %d\n", rgn->rgn_idx);
@@ -1130,6 +1148,8 @@ static void __ufshpb_evict_region(struct ufshpb_lu *hpb,
 
for_each_sub_region(rgn, srgn_idx, srgn)
ufshpb_purge_active_subregion(hpb, srgn);
+
+   return 0;
 }
 
 static int ufshpb_evict_region(struct ufshpb_lu *hpb, struct ufshpb_region 
*rgn)
@@ -1151,7 +1171,7 @@ static int ufshpb_evict_region(struct ufshpb_lu *hpb, 
struct ufshpb_region *rgn)
goto out;
}
 
-   __ufshpb_evict_region(hpb, rgn);
+   ret = __ufshpb_evict_region(hpb, rgn, flags);
}
 out:
spin_unlock_irqrestore(>rgn_state_lock, flags);
@@ -1285,7 +1305,9 @@ static int ufshpb_add_region(struct ufshpb_lu *hpb, 
struct ufshpb_region *rgn)
"LRU full (%d), choose v

RE: [PATCH v7 06/11] scsi: ufshpb: Region inactivation in host mode

2021-04-06 Thread Avri Altman
 
> 
> On 2021-04-06 13:20, Avri Altman wrote:
> >> > -static void __ufshpb_evict_region(struct ufshpb_lu *hpb,
> >> > -   struct ufshpb_region *rgn)
> >> > +static int __ufshpb_evict_region(struct ufshpb_lu *hpb,
> >> > +  struct ufshpb_region *rgn)
> >> >  {
> >> >   struct victim_select_info *lru_info;
> >> >   struct ufshpb_subregion *srgn;
> >> >   int srgn_idx;
> >> >
> >> > + lockdep_assert_held(>rgn_state_lock);
> >> > +
> >> > + if (hpb->is_hcm) {
> >> > + unsigned long flags;
> >> > + int ret;
> >> > +
> >> > + spin_unlock_irqrestore(>rgn_state_lock, flags);
> >>
> >> Never seen a usage like this... Here flags is used without being
> >> intialized.
> >> The flag is needed when spin_unlock_irqrestore ->
> >> local_irq_restore(flags) to
> >> restore the DAIF register (in terms of ARM).
> > OK.
> 
> Hi Avri,
> 
> Checked on my setup, this lead to compilation error. Will you fix it in
> next version?
> 
> warning: variable 'flags' is uninitialized when used here
> [-Wuninitialized]
Yeah - I will pass it to __ufshpb_evict_region and drop the lockdep_assert call.

I don't want to block your testing - are there any other things you want me to 
change?

Thanks,
Avri

> 
> Thanks,
> Can Guo.
> 
> >
> > Thanks,
> > Avri
> >
> >>
> >> Thanks,
> >>
> >> Can Guo.
> >>
> >> > + ret = ufshpb_issue_umap_single_req(hpb, rgn);
> >> > + spin_lock_irqsave(>rgn_state_lock, flags);
> >> > + if (ret)
> >> > + return ret;
> >> > + }
> >> > +
> >> >   lru_info = >lru_info;
> >> >
> >> >   dev_dbg(>sdev_ufs_lu->sdev_dev, "evict region %d\n",
> >> > rgn->rgn_idx);
> >> > @@ -1130,6 +1150,8 @@ static void __ufshpb_evict_region(struct
> >> > ufshpb_lu *hpb,
> >> >
> >> >   for_each_sub_region(rgn, srgn_idx, srgn)
> >> >   ufshpb_purge_active_subregion(hpb, srgn);
> >> > +
> >> > + return 0;
> >> >  }
> >> >
> >> >  static int ufshpb_evict_region(struct ufshpb_lu *hpb, struct
> >> > ufshpb_region *rgn)
> >> > @@ -1151,7 +1173,7 @@ static int ufshpb_evict_region(struct ufshpb_lu
> >> > *hpb, struct ufshpb_region *rgn)
> >> >   goto out;
> >> >   }
> >> >
> >> > - __ufshpb_evict_region(hpb, rgn);
> >> > + ret = __ufshpb_evict_region(hpb, rgn);
> >> >   }
> >> >  out:
> >> >   spin_unlock_irqrestore(>rgn_state_lock, flags);
> >> > @@ -1285,7 +1307,9 @@ static int ufshpb_add_region(struct ufshpb_lu
> >> > *hpb, struct ufshpb_region *rgn)
> >> >   "LRU full (%d), choose victim %d\n",
> >> >   atomic_read(_info->active_cnt),
> >> >   victim_rgn->rgn_idx);
> >> > - __ufshpb_evict_region(hpb, victim_rgn);
> >> > + ret = __ufshpb_evict_region(hpb, victim_rgn);
> >> > + if (ret)
> >> > + goto out;
> >> >   }
> >> >
> >> >   /*
> >> > @@ -1856,6 +1880,7 @@ ufshpb_sysfs_attr_show_func(rb_noti_cnt);
> >> >  ufshpb_sysfs_attr_show_func(rb_active_cnt);
> >> >  ufshpb_sysfs_attr_show_func(rb_inactive_cnt);
> >> >  ufshpb_sysfs_attr_show_func(map_req_cnt);
> >> > +ufshpb_sysfs_attr_show_func(umap_req_cnt);
> >> >
> >> >  static struct attribute *hpb_dev_stat_attrs[] = {
> >> >   _attr_hit_cnt.attr,
> >> > @@ -1864,6 +1889,7 @@ static struct attribute *hpb_dev_stat_attrs[] = {
> >> >   _attr_rb_active_cnt.attr,
> >> >   _attr_rb_inactive_cnt.attr,
> >> >   _attr_map_req_cnt.attr,
> >> > + _attr_umap_req_cnt.attr,
> >> >   NULL,
> >> >  };
> >> >
> >> > @@ -1988,6 +2014,7 @@ static void ufshpb_stat_init(struct ufshpb_lu
> >> > *hpb)
> >> >   hpb->stats.rb_active_cnt = 0;
> >> >   hpb->stats.rb_inactive_cnt = 0;
> >> >   hpb->stats.map_req_cnt = 0;
> >> > + hpb->stats.umap_req_cnt = 0;
> >> >  }
> >> >
> >> >  static void ufshpb_param_init(struct ufshpb_lu *hpb)
> >> > diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
> >> > index 87495e59fcf1..1ea58c17a4de 100644
> >> > --- a/drivers/scsi/ufs/ufshpb.h
> >> > +++ b/drivers/scsi/ufs/ufshpb.h
> >> > @@ -191,6 +191,7 @@ struct ufshpb_stats {
> >> >   u64 rb_inactive_cnt;
> >> >   u64 map_req_cnt;
> >> >   u64 pre_req_cnt;
> >> > + u64 umap_req_cnt;
> >> >  };
> >> >
> >> >  struct ufshpb_lu {


RE: [PATCH v7 06/11] scsi: ufshpb: Region inactivation in host mode

2021-04-05 Thread Avri Altman
> > -static void __ufshpb_evict_region(struct ufshpb_lu *hpb,
> > -   struct ufshpb_region *rgn)
> > +static int __ufshpb_evict_region(struct ufshpb_lu *hpb,
> > +  struct ufshpb_region *rgn)
> >  {
> >   struct victim_select_info *lru_info;
> >   struct ufshpb_subregion *srgn;
> >   int srgn_idx;
> >
> > + lockdep_assert_held(>rgn_state_lock);
> > +
> > + if (hpb->is_hcm) {
> > + unsigned long flags;
> > + int ret;
> > +
> > + spin_unlock_irqrestore(>rgn_state_lock, flags);
> 
> Never seen a usage like this... Here flags is used without being
> intialized.
> The flag is needed when spin_unlock_irqrestore ->
> local_irq_restore(flags) to
> restore the DAIF register (in terms of ARM).
OK.

Thanks,
Avri

> 
> Thanks,
> 
> Can Guo.
> 
> > + ret = ufshpb_issue_umap_single_req(hpb, rgn);
> > + spin_lock_irqsave(>rgn_state_lock, flags);
> > + if (ret)
> > + return ret;
> > + }
> > +
> >   lru_info = >lru_info;
> >
> >   dev_dbg(>sdev_ufs_lu->sdev_dev, "evict region %d\n",
> > rgn->rgn_idx);
> > @@ -1130,6 +1150,8 @@ static void __ufshpb_evict_region(struct
> > ufshpb_lu *hpb,
> >
> >   for_each_sub_region(rgn, srgn_idx, srgn)
> >   ufshpb_purge_active_subregion(hpb, srgn);
> > +
> > + return 0;
> >  }
> >
> >  static int ufshpb_evict_region(struct ufshpb_lu *hpb, struct
> > ufshpb_region *rgn)
> > @@ -1151,7 +1173,7 @@ static int ufshpb_evict_region(struct ufshpb_lu
> > *hpb, struct ufshpb_region *rgn)
> >   goto out;
> >   }
> >
> > - __ufshpb_evict_region(hpb, rgn);
> > + ret = __ufshpb_evict_region(hpb, rgn);
> >   }
> >  out:
> >   spin_unlock_irqrestore(>rgn_state_lock, flags);
> > @@ -1285,7 +1307,9 @@ static int ufshpb_add_region(struct ufshpb_lu
> > *hpb, struct ufshpb_region *rgn)
> >   "LRU full (%d), choose victim %d\n",
> >   atomic_read(_info->active_cnt),
> >   victim_rgn->rgn_idx);
> > - __ufshpb_evict_region(hpb, victim_rgn);
> > + ret = __ufshpb_evict_region(hpb, victim_rgn);
> > + if (ret)
> > + goto out;
> >   }
> >
> >   /*
> > @@ -1856,6 +1880,7 @@ ufshpb_sysfs_attr_show_func(rb_noti_cnt);
> >  ufshpb_sysfs_attr_show_func(rb_active_cnt);
> >  ufshpb_sysfs_attr_show_func(rb_inactive_cnt);
> >  ufshpb_sysfs_attr_show_func(map_req_cnt);
> > +ufshpb_sysfs_attr_show_func(umap_req_cnt);
> >
> >  static struct attribute *hpb_dev_stat_attrs[] = {
> >   _attr_hit_cnt.attr,
> > @@ -1864,6 +1889,7 @@ static struct attribute *hpb_dev_stat_attrs[] = {
> >   _attr_rb_active_cnt.attr,
> >   _attr_rb_inactive_cnt.attr,
> >   _attr_map_req_cnt.attr,
> > + _attr_umap_req_cnt.attr,
> >   NULL,
> >  };
> >
> > @@ -1988,6 +2014,7 @@ static void ufshpb_stat_init(struct ufshpb_lu
> > *hpb)
> >   hpb->stats.rb_active_cnt = 0;
> >   hpb->stats.rb_inactive_cnt = 0;
> >   hpb->stats.map_req_cnt = 0;
> > + hpb->stats.umap_req_cnt = 0;
> >  }
> >
> >  static void ufshpb_param_init(struct ufshpb_lu *hpb)
> > diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
> > index 87495e59fcf1..1ea58c17a4de 100644
> > --- a/drivers/scsi/ufs/ufshpb.h
> > +++ b/drivers/scsi/ufs/ufshpb.h
> > @@ -191,6 +191,7 @@ struct ufshpb_stats {
> >   u64 rb_inactive_cnt;
> >   u64 map_req_cnt;
> >   u64 pre_req_cnt;
> > + u64 umap_req_cnt;
> >  };
> >
> >  struct ufshpb_lu {


RE: [PATCH][next] scsi: ufs: Fix out-of-bounds warnings in ufshcd_exec_raw_upiu_cmd

2021-04-03 Thread Avri Altman
> 
> Fix the following out-of-bounds warnings by enclosing
> some structure members into new structure objects upiu_req
> and upiu_rsp:
> 
> include/linux/fortify-string.h:20:29: warning: '__builtin_memcpy' offset [29,
> 48] from the object at 'treq' is out of the bounds of referenced subobject
> 'req_header' with type 'struct utp_upiu_header' at offset 16 [-Warray-bounds]
> include/linux/fortify-string.h:20:29: warning: '__builtin_memcpy' offset [61,
> 80] from the object at 'treq' is out of the bounds of referenced subobject
> 'rsp_header' with type 'struct utp_upiu_header' at offset 48 [-Warray-bounds]
> arch/m68k/include/asm/string.h:72:25: warning: '__builtin_memcpy' offset
> [29, 48] from the object at 'treq' is out of the bounds of referenced 
> subobject
> 'req_header' with type 'struct utp_upiu_header' at offset 16 [-Warray-bounds]
> arch/m68k/include/asm/string.h:72:25: warning: '__builtin_memcpy' offset
> [61, 80] from the object at 'treq' is out of the bounds of referenced 
> subobject
> 'rsp_header' with type 'struct utp_upiu_header' at offset 48 [-Warray-bounds]
> 
> Refactor the code by making it more structured.
> 
> The problem is that the original code is trying to copy data into a
> bunch of struct members adjacent to each other in a single call to
> memcpy(). Now that a new struct _upiu_req_ enclosing all those adjacent
> members is introduced, memcpy() doesn't overrun the length of
> _header, because the address of the new struct object
> _upiu_req_ is used as the destination, instead. The same problem
> is present when memcpy() overruns the length of the source
> _header; in this case the address of the new struct
> object _upiu_rsp_ is used, instead.
> 
> Also, this helps with the ongoing efforts to enable -Warray-bounds
> and avoid confusing the compiler.
> 
> Link: https://github.com/KSPP/linux/issues/109
> Reported-by: kernel test robot 
> Build-tested-by: kernel test robot 
> Link:
> https://lore.kernel.org/lkml/60640558.lsaxik6otpwto9rv%25...@intel.com/
> Signed-off-by: Gustavo A. R. Silva 
Reviewed-by: Avri Altman 

Thanks,
Avri

> ---
>  drivers/scsi/ufs/ufshcd.c | 28 
>  drivers/scsi/ufs/ufshci.h | 22 +-
>  2 files changed, 29 insertions(+), 21 deletions(-)
> 
> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
> index 7539a4ee9494..324eb641e66f 100644
> --- a/drivers/scsi/ufs/ufshcd.c
> +++ b/drivers/scsi/ufs/ufshcd.c
> @@ -336,11 +336,15 @@ static void ufshcd_add_tm_upiu_trace(struct
> ufs_hba *hba, unsigned int tag,
> return;
> 
> if (str_t == UFS_TM_SEND)
> -   trace_ufshcd_upiu(dev_name(hba->dev), str_t, 
> >req_header,
> - >input_param1, UFS_TSF_TM_INPUT);
> +   trace_ufshcd_upiu(dev_name(hba->dev), str_t,
> + >upiu_req.req_header,
> + >upiu_req.input_param1,
> + UFS_TSF_TM_INPUT);
> else
> -   trace_ufshcd_upiu(dev_name(hba->dev), str_t, 
> >rsp_header,
> - >output_param1, UFS_TSF_TM_OUTPUT);
> +   trace_ufshcd_upiu(dev_name(hba->dev), str_t,
> + >upiu_rsp.rsp_header,
> + >upiu_rsp.output_param1,
> + UFS_TSF_TM_OUTPUT);
>  }
> 
>  static void ufshcd_add_uic_command_trace(struct ufs_hba *hba,
> @@ -6420,7 +6424,7 @@ static int __ufshcd_issue_tm_cmd(struct ufs_hba
> *hba,
> spin_lock_irqsave(host->host_lock, flags);
> task_tag = hba->nutrs + free_slot;
> 
> -   treq->req_header.dword_0 |= cpu_to_be32(task_tag);
> +   treq->upiu_req.req_header.dword_0 |= cpu_to_be32(task_tag);
> 
> memcpy(hba->utmrdl_base_addr + free_slot, treq, sizeof(*treq));
> ufshcd_vops_setup_task_mgmt(hba, free_slot, tm_function);
> @@ -6493,16 +6497,16 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba
> *hba, int lun_id, int task_id,
> treq.header.dword_2 = cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
> 
> /* Configure task request UPIU */
> -   treq.req_header.dword_0 = cpu_to_be32(lun_id << 8) |
> +   treq.upiu_req.req_header.dword_0 = cpu_to_be32(lun_id << 8) |
>   cpu_to_be32(UPIU_TRANSACTION_TASK_REQ << 
> 24);
> -   treq.req_header.dword_1 = cpu_to_be32(tm_function << 16);
> +   treq.upiu_req.req_header.dword_1 = cpu_to_be32(tm_function << 16);
> 
> /*
>  * The host shall provide the same value for LUN field in 

Re: [PATCH 2/2] mmc: block: Update ext_csd.cache_ctrl if it was written

2021-04-02 Thread Avri Altman
>> @@ -571,6 +571,14 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, 
>> struct mmc_blk_data *md,
>>  main_md->part_curr = value & EXT_CSD_PART_CONFIG_ACC_MASK;
>>  }
>>  
>> +/* Make sure to update CACHE_CTRL in case it was changed */

> It might be worth noting that the cache will get turned back
> on if the card is re-initialized, for example suspend/resume or
> hw reset in recovery.
Done.

Thanks,
Avri


Re: [PATCH 1/2] mmc: block: Issue flush only if allowed

2021-04-02 Thread Avri Altman
>> @@ -1473,6 +1473,9 @@ static int mmc_blk_cqe_issue_flush(struct mmc_queue 
>> *mq, struct request *req)
>>  struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req);
>>  struct mmc_request *mrq = mmc_blk_cqe_prep_dcmd(mqrq, req);
>>  
>> +if (mmc_card_mmc(mq->card) && !mmc_flush_allowed(mq->card))
>> +return 0;

> Returning 0 means the flush was issued successfully. i.e. it is in
> flight.
>
> Instead, a call to blk_mq_end_request(req, BLK_STS_OK) is needed,
> and mmc_blk_mq_issue_rq() must be amended so that it will return
> MMC_REQ_DONE for this case.
Done.

Thanks,
Avri


RE: [PATCH v4 1/2] scsi: ufs: Fix task management request completion timeout

2021-03-31 Thread Avri Altman
> ufshcd_tmc_handler() calls blk_mq_tagset_busy_iter(fn =
> ufshcd_compl_tm()),
> but since blk_mq_tagset_busy_iter() only iterates over all reserved tags
> and requests which are not in IDLE state, ufshcd_compl_tm() never gets a
> chance to run. Thus, TMR always ends up with completion timeout. Fix it by
> calling blk_mq_start_request() in  __ufshcd_issue_tm_cmd().
> 
> Fixes: 69a6c269c097 ("scsi: ufs: Use blk_{get,put}_request() to allocate and
> free TMFs")
> 
> Signed-off-by: Can Guo 
> ---
>  drivers/scsi/ufs/ufshcd.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
> index b49555fa..d4f8cb2 100644
> --- a/drivers/scsi/ufs/ufshcd.c
> +++ b/drivers/scsi/ufs/ufshcd.c
> @@ -6464,6 +6464,7 @@ static int __ufshcd_issue_tm_cmd(struct ufs_hba
> *hba,
> 
> spin_lock_irqsave(host->host_lock, flags);
> task_tag = hba->nutrs + free_slot;
> +   blk_mq_start_request(req);
Maybe just set req->state to MQ_RQ_IN_FLIGHT
Without all other irrelevant initializations such as add timeout etc.

Thanks,
Avri
> 
> treq->req_header.dword_0 |= cpu_to_be32(task_tag);
> 
> --
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a
> Linux Foundation Collaborative Project.



[PATCH v7 11/11] scsi: ufshpb: Make host mode parameters configurable

2021-03-31 Thread Avri Altman
We can make use of this commit, to elaborate some more of the host
control mode logic, explaining what role play each and every variable.

While at it, allow those parameters to be configurable.

Signed-off-by: Avri Altman 
---
 Documentation/ABI/testing/sysfs-driver-ufs |  84 +-
 drivers/scsi/ufs/ufshpb.c  | 288 +++--
 drivers/scsi/ufs/ufshpb.h  |  20 ++
 3 files changed, 365 insertions(+), 27 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-driver-ufs 
b/Documentation/ABI/testing/sysfs-driver-ufs
index 419adf450b89..133af2114165 100644
--- a/Documentation/ABI/testing/sysfs-driver-ufs
+++ b/Documentation/ABI/testing/sysfs-driver-ufs
@@ -1323,14 +1323,76 @@ Description:This entry shows the maximum HPB data 
size for using single HPB
 
The file is read only.
 
-What:  /sys/bus/platform/drivers/ufshcd/*/flags/wb_enable
-Date:  March 2021
-Contact:   Daejun Park 
-Description:   This entry shows the status of HPB.
-
-   == 
-   0  HPB is not enabled.
-   1  HPB is enabled
-   == 
-
-   The file is read only.
+What:  /sys/class/scsi_device/*/device/hpb_param_sysfs/activation_thld
+Date:  February 2021
+Contact:   Avri Altman 
+Description:   In host control mode, reads are the major source of activation
+   trials.  once this threshold hs met, the region is added to the
+   "to-be-activated" list.  Since we reset the read counter upon
+   write, this include sending a rb command updating the region
+   ppn as well.
+
+What:  
/sys/class/scsi_device/*/device/hpb_param_sysfs/normalization_factor
+Date:  February 2021
+Contact:   Avri Altman 
+Description:   In host control mode, We think of the regions as "buckets".
+   Those buckets are being filled with reads, and emptied on write.
+   We use entries_per_srgn - the amount of blocks in a subregion as
+   our bucket size.  This applies because HPB1.0 only concern a
+   single-block reads.  Once the bucket size is crossed, we trigger
+   a normalization work - not only to avoid overflow, but mainly
+   because we want to keep those counters normalized, as we are
+   using those reads as a comparative score, to make various 
decisions.
+   The normalization is dividing (shift right) the read counter by
+   the normalization_factor. If during consecutive normalizations
+   an active region has exhaust its reads - inactivate it.
+
+What:  
/sys/class/scsi_device/*/device/hpb_param_sysfs/eviction_thld_enter
+Date:  February 2021
+Contact:   Avri Altman 
+Description:   Region deactivation is often due to the fact that eviction took
+   place: a region become active on the expense of another. This is
+   happening when the max-active-regions limit has crossed.
+   In host mode, eviction is considered an extreme measure. We
+   want to verify that the entering region has enough reads, and
+   the exiting region has much less reads.  eviction_thld_enter is
+   the min reads that a region must have in order to be considered
+   as a candidate to evict other region.
+
+What:  
/sys/class/scsi_device/*/device/hpb_param_sysfs/eviction_thld_exit
+Date:  February 2021
+Contact:   Avri Altman 
+Description:   same as above for the exiting region. A region is consider to
+   be a candidate to be evicted, only if it has less reads than
+   eviction_thld_exit.
+
+What:  /sys/class/scsi_device/*/device/hpb_param_sysfs/read_timeout_ms
+Date:  February 2021
+Contact:   Avri Altman 
+Description:   In order not to hang on to “cold” regions, we shall inactivate
+   a region that has no READ access for a predefined amount of
+   time - read_timeout_ms. If read_timeout_ms has expired, and the
+   region is dirty - it is less likely that we can make any use of
+   HPB-READing it.  So we inactivate it.  Still, deactivation has
+   its overhead, and we may still benefit from HPB-READing this
+   region if it is clean - see read_timeout_expiries.
+
+What:  
/sys/class/scsi_device/*/device/hpb_param_sysfs/read_timeout_expiries
+Date:  February 2021
+Contact:   Avri Altman 
+Description:   if the region read timeout has expired, but the region is clean,
+   just re-wind its timer for another spin.  Do that as long as it
+   is clean and did not exhaust its read_timeout_expiries 
threshold.
+
+What:  
/sys/class/scsi_device/*/device/hpb_param_sysfs/timeout_polli

[PATCH v7 10/11] scsi: ufshpb: Add support for host control mode

2021-03-31 Thread Avri Altman
Support devices that report they are using host control mode.

Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshpb.c | 6 --
 1 file changed, 6 deletions(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index c07da481ff4e..08066bb6da65 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -2582,12 +2582,6 @@ void ufshpb_get_dev_info(struct ufs_hba *hba, u8 
*desc_buf)
u32 max_hpb_single_cmd = HPB_MULTI_CHUNK_LOW;
 
hpb_dev_info->control_mode = desc_buf[DEVICE_DESC_PARAM_HPB_CONTROL];
-   if (hpb_dev_info->control_mode == HPB_HOST_CONTROL) {
-   dev_err(hba->dev, "%s: host control mode is not supported.\n",
-   __func__);
-   hpb_dev_info->hpb_disabled = true;
-   return;
-   }
 
version = get_unaligned_be16(desc_buf + DEVICE_DESC_PARAM_HPB_VER);
if ((version != HPB_SUPPORT_VERSION) &&
-- 
2.25.1



[PATCH v7 09/11] scsi: ufshpb: Limit the number of inflight map requests

2021-03-31 Thread Avri Altman
In host control mode the host is the originator of map requests. To not
flood the device with map requests, use a simple throttling mechanism
that limits the number of inflight map requests.

Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshpb.c | 11 +++
 drivers/scsi/ufs/ufshpb.h |  1 +
 2 files changed, 12 insertions(+)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 8dbeaf948afa..c07da481ff4e 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -21,6 +21,7 @@
 #define READ_TO_MS 1000
 #define READ_TO_EXPIRIES 100
 #define POLLING_INTERVAL_MS 200
+#define THROTTLE_MAP_REQ_DEFAULT 1
 
 /* memory management */
 static struct kmem_cache *ufshpb_mctx_cache;
@@ -740,6 +741,14 @@ static struct ufshpb_req *ufshpb_get_map_req(struct 
ufshpb_lu *hpb,
struct ufshpb_req *map_req;
struct bio *bio;
 
+   if (hpb->is_hcm &&
+   hpb->num_inflight_map_req >= THROTTLE_MAP_REQ_DEFAULT) {
+   dev_info(>sdev_ufs_lu->sdev_dev,
+"map_req throttle. inflight %d throttle %d",
+hpb->num_inflight_map_req, THROTTLE_MAP_REQ_DEFAULT);
+   return NULL;
+   }
+
map_req = ufshpb_get_req(hpb, srgn->rgn_idx, REQ_OP_SCSI_IN);
if (!map_req)
return NULL;
@@ -754,6 +763,7 @@ static struct ufshpb_req *ufshpb_get_map_req(struct 
ufshpb_lu *hpb,
 
map_req->rb.srgn_idx = srgn->srgn_idx;
map_req->rb.mctx = srgn->mctx;
+   hpb->num_inflight_map_req++;
 
return map_req;
 }
@@ -763,6 +773,7 @@ static void ufshpb_put_map_req(struct ufshpb_lu *hpb,
 {
bio_put(map_req->bio);
ufshpb_put_req(hpb, map_req);
+   hpb->num_inflight_map_req--;
 }
 
 static int ufshpb_clear_dirty_bitmap(struct ufshpb_lu *hpb,
diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
index 448062a94760..cfa0abac21db 100644
--- a/drivers/scsi/ufs/ufshpb.h
+++ b/drivers/scsi/ufs/ufshpb.h
@@ -217,6 +217,7 @@ struct ufshpb_lu {
struct ufshpb_req *pre_req;
int num_inflight_pre_req;
int throttle_pre_req;
+   int num_inflight_map_req;
struct list_head lh_pre_req_free;
int cur_read_id;
int pre_req_min_tr_len;
-- 
2.25.1



[PATCH v7 05/11] scsi: ufshpb: Make eviction depends on region's reads

2021-03-31 Thread Avri Altman
In host mode, eviction is considered an extreme measure.
verify that the entering region has enough reads, and the exiting
region has much less reads.

Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshpb.c | 18 +-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 3ab66421dc00..aefb6dc160ee 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -17,6 +17,7 @@
 #include "../sd.h"
 
 #define ACTIVATION_THRESHOLD 8 /* 8 IOs */
+#define EVICTION_THRESHOLD (ACTIVATION_THRESHOLD << 5) /* 256 IOs */
 
 /* memory management */
 static struct kmem_cache *ufshpb_mctx_cache;
@@ -1057,6 +1058,13 @@ static struct ufshpb_region 
*ufshpb_victim_lru_info(struct ufshpb_lu *hpb)
if (ufshpb_check_srgns_issue_state(hpb, rgn))
continue;
 
+   /*
+* in host control mode, verify that the exiting region
+* has less reads
+*/
+   if (hpb->is_hcm && rgn->reads > (EVICTION_THRESHOLD >> 1))
+   continue;
+
victim_rgn = rgn;
break;
}
@@ -1229,7 +1237,7 @@ static int ufshpb_issue_map_req(struct ufshpb_lu *hpb,
 
 static int ufshpb_add_region(struct ufshpb_lu *hpb, struct ufshpb_region *rgn)
 {
-   struct ufshpb_region *victim_rgn;
+   struct ufshpb_region *victim_rgn = NULL;
struct victim_select_info *lru_info = >lru_info;
unsigned long flags;
int ret = 0;
@@ -1256,7 +1264,15 @@ static int ufshpb_add_region(struct ufshpb_lu *hpb, 
struct ufshpb_region *rgn)
 * It is okay to evict the least recently used region,
 * because the device could detect this region
 * by not issuing HPB_READ
+*
+* in host control mode, verify that the entering
+* region has enough reads
 */
+   if (hpb->is_hcm && rgn->reads < EVICTION_THRESHOLD) {
+   ret = -EACCES;
+   goto out;
+   }
+
victim_rgn = ufshpb_victim_lru_info(hpb);
if (!victim_rgn) {
dev_warn(>sdev_ufs_lu->sdev_dev,
-- 
2.25.1



[PATCH v7 08/11] scsi: ufshpb: Add "Cold" regions timer

2021-03-31 Thread Avri Altman
In order not to hang on to “cold” regions, we shall inactivate a
region that has no READ access for a predefined amount of time -
READ_TO_MS. For that purpose we shall monitor the active regions list,
polling it on every POLLING_INTERVAL_MS. On timeout expiry we shall add
the region to the "to-be-inactivated" list, unless it is clean and did
not exhaust its READ_TO_EXPIRIES - another parameter.

All this does not apply to pinned regions.

Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshpb.c | 74 +--
 drivers/scsi/ufs/ufshpb.h |  8 +
 2 files changed, 79 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 1d99099ebd41..8dbeaf948afa 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -18,6 +18,9 @@
 
 #define ACTIVATION_THRESHOLD 8 /* 8 IOs */
 #define EVICTION_THRESHOLD (ACTIVATION_THRESHOLD << 5) /* 256 IOs */
+#define READ_TO_MS 1000
+#define READ_TO_EXPIRIES 100
+#define POLLING_INTERVAL_MS 200
 
 /* memory management */
 static struct kmem_cache *ufshpb_mctx_cache;
@@ -1031,12 +1034,63 @@ static int ufshpb_check_srgns_issue_state(struct 
ufshpb_lu *hpb,
return 0;
 }
 
+static void ufshpb_read_to_handler(struct work_struct *work)
+{
+   struct ufshpb_lu *hpb = container_of(work, struct ufshpb_lu,
+ufshpb_read_to_work.work);
+   struct victim_select_info *lru_info = >lru_info;
+   struct ufshpb_region *rgn, *next_rgn;
+   unsigned long flags;
+   LIST_HEAD(expired_list);
+
+   if (test_and_set_bit(TIMEOUT_WORK_RUNNING, >work_data_bits))
+   return;
+
+   spin_lock_irqsave(>rgn_state_lock, flags);
+
+   list_for_each_entry_safe(rgn, next_rgn, _info->lh_lru_rgn,
+list_lru_rgn) {
+   bool timedout = ktime_after(ktime_get(), rgn->read_timeout);
+
+   if (timedout) {
+   rgn->read_timeout_expiries--;
+   if (is_rgn_dirty(rgn) ||
+   rgn->read_timeout_expiries == 0)
+   list_add(>list_expired_rgn, _list);
+   else
+   rgn->read_timeout = ktime_add_ms(ktime_get(),
+READ_TO_MS);
+   }
+   }
+
+   spin_unlock_irqrestore(>rgn_state_lock, flags);
+
+   list_for_each_entry_safe(rgn, next_rgn, _list,
+list_expired_rgn) {
+   list_del_init(>list_expired_rgn);
+   spin_lock_irqsave(>rsp_list_lock, flags);
+   ufshpb_update_inactive_info(hpb, rgn->rgn_idx);
+   spin_unlock_irqrestore(>rsp_list_lock, flags);
+   }
+
+   ufshpb_kick_map_work(hpb);
+
+   clear_bit(TIMEOUT_WORK_RUNNING, >work_data_bits);
+
+   schedule_delayed_work(>ufshpb_read_to_work,
+ msecs_to_jiffies(POLLING_INTERVAL_MS));
+}
+
 static void ufshpb_add_lru_info(struct victim_select_info *lru_info,
struct ufshpb_region *rgn)
 {
rgn->rgn_state = HPB_RGN_ACTIVE;
list_add_tail(>list_lru_rgn, _info->lh_lru_rgn);
atomic_inc(_info->active_cnt);
+   if (rgn->hpb->is_hcm) {
+   rgn->read_timeout = ktime_add_ms(ktime_get(), READ_TO_MS);
+   rgn->read_timeout_expiries = READ_TO_EXPIRIES;
+   }
 }
 
 static void ufshpb_hit_lru_info(struct victim_select_info *lru_info,
@@ -1820,6 +1874,7 @@ static int ufshpb_alloc_region_tbl(struct ufs_hba *hba, 
struct ufshpb_lu *hpb)
 
INIT_LIST_HEAD(>list_inact_rgn);
INIT_LIST_HEAD(>list_lru_rgn);
+   INIT_LIST_HEAD(>list_expired_rgn);
 
if (rgn_idx == hpb->rgns_per_lu - 1) {
srgn_cnt = ((hpb->srgns_per_lu - 1) %
@@ -1841,6 +1896,7 @@ static int ufshpb_alloc_region_tbl(struct ufs_hba *hba, 
struct ufshpb_lu *hpb)
}
 
rgn->rgn_flags = 0;
+   rgn->hpb = hpb;
}
 
return 0;
@@ -2064,9 +2120,12 @@ static int ufshpb_lu_hpb_init(struct ufs_hba *hba, 
struct ufshpb_lu *hpb)
INIT_LIST_HEAD(>list_hpb_lu);
 
INIT_WORK(>map_work, ufshpb_map_work_handler);
-   if (hpb->is_hcm)
+   if (hpb->is_hcm) {
INIT_WORK(>ufshpb_normalization_work,
  ufshpb_normalization_work_handler);
+   INIT_DELAYED_WORK(>ufshpb_read_to_work,
+ ufshpb_read_to_handler);
+   }
 
hpb->map_req_cache = kmem_cache_create("ufshpb_req_cache",
  sizeof(struct ufshpb_req), 0, 0, NULL);
@@ -2100,6 +2159,10 @@ static int ufshpb_lu_hp

[PATCH v7 07/11] scsi: ufshpb: Add hpb dev reset response

2021-03-31 Thread Avri Altman
The spec does not define what is the host's recommended response when
the device send hpb dev reset response (oper 0x2).

We will update all active hpb regions: mark them and do that on the next
read.

Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshpb.c | 32 +++-
 drivers/scsi/ufs/ufshpb.h |  1 +
 2 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index fcc954f51bcf..1d99099ebd41 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -195,7 +195,8 @@ static void ufshpb_iterate_rgn(struct ufshpb_lu *hpb, int 
rgn_idx, int srgn_idx,
}
spin_unlock(>rgn_lock);
 
-   if (activate) {
+   if (activate ||
+   test_and_clear_bit(RGN_FLAG_UPDATE, >rgn_flags)) {
spin_lock_irqsave(>rsp_list_lock, flags);
ufshpb_update_active_info(hpb, rgn_idx, srgn_idx);
spin_unlock_irqrestore(>rsp_list_lock, flags);
@@ -1412,6 +1413,20 @@ static void ufshpb_rsp_req_region_update(struct 
ufshpb_lu *hpb,
queue_work(ufshpb_wq, >map_work);
 }
 
+static void ufshpb_dev_reset_handler(struct ufshpb_lu *hpb)
+{
+   struct victim_select_info *lru_info = >lru_info;
+   struct ufshpb_region *rgn;
+   unsigned long flags;
+
+   spin_lock_irqsave(>rgn_state_lock, flags);
+
+   list_for_each_entry(rgn, _info->lh_lru_rgn, list_lru_rgn)
+   set_bit(RGN_FLAG_UPDATE, >rgn_flags);
+
+   spin_unlock_irqrestore(>rgn_state_lock, flags);
+}
+
 /*
  * This function will parse recommended active subregion information in sense
  * data field of response UPIU with SAM_STAT_GOOD state.
@@ -1486,6 +1501,18 @@ void ufshpb_rsp_upiu(struct ufs_hba *hba, struct 
ufshcd_lrb *lrbp)
case HPB_RSP_DEV_RESET:
dev_warn(>sdev_ufs_lu->sdev_dev,
 "UFS device lost HPB information during PM.\n");
+
+   if (hpb->is_hcm) {
+   struct scsi_device *sdev;
+
+   __shost_for_each_device(sdev, hba->host) {
+   struct ufshpb_lu *h = sdev->hostdata;
+
+   if (h)
+   ufshpb_dev_reset_handler(h);
+   }
+   }
+
break;
default:
dev_notice(>sdev_ufs_lu->sdev_dev,
@@ -1812,6 +1839,8 @@ static int ufshpb_alloc_region_tbl(struct ufs_hba *hba, 
struct ufshpb_lu *hpb)
} else {
rgn->rgn_state = HPB_RGN_INACTIVE;
}
+
+   rgn->rgn_flags = 0;
}
 
return 0;
@@ -2139,6 +2168,7 @@ static void ufshpb_cancel_jobs(struct ufshpb_lu *hpb)
 {
if (hpb->is_hcm)
cancel_work_sync(>ufshpb_normalization_work);
+
cancel_work_sync(>map_work);
 }
 
diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
index 1ea58c17a4de..b863540e28d6 100644
--- a/drivers/scsi/ufs/ufshpb.h
+++ b/drivers/scsi/ufs/ufshpb.h
@@ -127,6 +127,7 @@ struct ufshpb_region {
struct list_head list_lru_rgn;
unsigned long rgn_flags;
 #define RGN_FLAG_DIRTY 0
+#define RGN_FLAG_UPDATE 1
 
/* region reads - for host mode */
spinlock_t rgn_lock;
-- 
2.25.1



[PATCH v7 06/11] scsi: ufshpb: Region inactivation in host mode

2021-03-31 Thread Avri Altman
In host mode, the host is expected to send HPB-WRITE-BUFFER with
buffer-id = 0x1 when it inactivates a region.

Use the map-requests pool as there is no point in assigning a
designated cache for umap-requests.

Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshpb.c | 35 +++
 drivers/scsi/ufs/ufshpb.h |  1 +
 2 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index aefb6dc160ee..fcc954f51bcf 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -914,6 +914,7 @@ static int ufshpb_execute_umap_req(struct ufshpb_lu *hpb,
 
blk_execute_rq_nowait(NULL, req, 1, ufshpb_umap_req_compl_fn);
 
+   hpb->stats.umap_req_cnt++;
return 0;
 }
 
@@ -1110,18 +,37 @@ static int ufshpb_issue_umap_req(struct ufshpb_lu *hpb,
return -EAGAIN;
 }
 
+static int ufshpb_issue_umap_single_req(struct ufshpb_lu *hpb,
+   struct ufshpb_region *rgn)
+{
+   return ufshpb_issue_umap_req(hpb, rgn);
+}
+
 static int ufshpb_issue_umap_all_req(struct ufshpb_lu *hpb)
 {
return ufshpb_issue_umap_req(hpb, NULL);
 }
 
-static void __ufshpb_evict_region(struct ufshpb_lu *hpb,
- struct ufshpb_region *rgn)
+static int __ufshpb_evict_region(struct ufshpb_lu *hpb,
+struct ufshpb_region *rgn)
 {
struct victim_select_info *lru_info;
struct ufshpb_subregion *srgn;
int srgn_idx;
 
+   lockdep_assert_held(>rgn_state_lock);
+
+   if (hpb->is_hcm) {
+   unsigned long flags;
+   int ret;
+
+   spin_unlock_irqrestore(>rgn_state_lock, flags);
+   ret = ufshpb_issue_umap_single_req(hpb, rgn);
+   spin_lock_irqsave(>rgn_state_lock, flags);
+   if (ret)
+   return ret;
+   }
+
lru_info = >lru_info;
 
dev_dbg(>sdev_ufs_lu->sdev_dev, "evict region %d\n", rgn->rgn_idx);
@@ -1130,6 +1150,8 @@ static void __ufshpb_evict_region(struct ufshpb_lu *hpb,
 
for_each_sub_region(rgn, srgn_idx, srgn)
ufshpb_purge_active_subregion(hpb, srgn);
+
+   return 0;
 }
 
 static int ufshpb_evict_region(struct ufshpb_lu *hpb, struct ufshpb_region 
*rgn)
@@ -1151,7 +1173,7 @@ static int ufshpb_evict_region(struct ufshpb_lu *hpb, 
struct ufshpb_region *rgn)
goto out;
}
 
-   __ufshpb_evict_region(hpb, rgn);
+   ret = __ufshpb_evict_region(hpb, rgn);
}
 out:
spin_unlock_irqrestore(>rgn_state_lock, flags);
@@ -1285,7 +1307,9 @@ static int ufshpb_add_region(struct ufshpb_lu *hpb, 
struct ufshpb_region *rgn)
"LRU full (%d), choose victim %d\n",
atomic_read(_info->active_cnt),
victim_rgn->rgn_idx);
-   __ufshpb_evict_region(hpb, victim_rgn);
+   ret = __ufshpb_evict_region(hpb, victim_rgn);
+   if (ret)
+   goto out;
}
 
/*
@@ -1856,6 +1880,7 @@ ufshpb_sysfs_attr_show_func(rb_noti_cnt);
 ufshpb_sysfs_attr_show_func(rb_active_cnt);
 ufshpb_sysfs_attr_show_func(rb_inactive_cnt);
 ufshpb_sysfs_attr_show_func(map_req_cnt);
+ufshpb_sysfs_attr_show_func(umap_req_cnt);
 
 static struct attribute *hpb_dev_stat_attrs[] = {
_attr_hit_cnt.attr,
@@ -1864,6 +1889,7 @@ static struct attribute *hpb_dev_stat_attrs[] = {
_attr_rb_active_cnt.attr,
_attr_rb_inactive_cnt.attr,
_attr_map_req_cnt.attr,
+   _attr_umap_req_cnt.attr,
NULL,
 };
 
@@ -1988,6 +2014,7 @@ static void ufshpb_stat_init(struct ufshpb_lu *hpb)
hpb->stats.rb_active_cnt = 0;
hpb->stats.rb_inactive_cnt = 0;
hpb->stats.map_req_cnt = 0;
+   hpb->stats.umap_req_cnt = 0;
 }
 
 static void ufshpb_param_init(struct ufshpb_lu *hpb)
diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
index 87495e59fcf1..1ea58c17a4de 100644
--- a/drivers/scsi/ufs/ufshpb.h
+++ b/drivers/scsi/ufs/ufshpb.h
@@ -191,6 +191,7 @@ struct ufshpb_stats {
u64 rb_inactive_cnt;
u64 map_req_cnt;
u64 pre_req_cnt;
+   u64 umap_req_cnt;
 };
 
 struct ufshpb_lu {
-- 
2.25.1



[PATCH v7 03/11] scsi: ufshpb: Transform set_dirty to iterate_rgn

2021-03-31 Thread Avri Altman
Given a transfer length, set_dirty meticulously runs over all the
entries, across subregions and regions if needed. Currently its only use
is to mark dirty blocks, but soon HCM may profit from it as well, when
managing its read counters.

Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshpb.c | 18 ++
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 6111019ca31a..252fcfb48862 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -144,13 +144,14 @@ static bool ufshpb_is_hpb_rsp_valid(struct ufs_hba *hba,
return true;
 }
 
-static void ufshpb_set_ppn_dirty(struct ufshpb_lu *hpb, int rgn_idx,
-int srgn_idx, int srgn_offset, int cnt)
+static void ufshpb_iterate_rgn(struct ufshpb_lu *hpb, int rgn_idx, int 
srgn_idx,
+  int srgn_offset, int cnt, bool set_dirty)
 {
struct ufshpb_region *rgn;
struct ufshpb_subregion *srgn;
int set_bit_len;
int bitmap_len;
+   unsigned long flags;
 
 next_srgn:
rgn = hpb->rgn_tbl + rgn_idx;
@@ -166,11 +167,14 @@ static void ufshpb_set_ppn_dirty(struct ufshpb_lu *hpb, 
int rgn_idx,
else
set_bit_len = cnt;
 
-   set_bit(RGN_FLAG_DIRTY, >rgn_flags);
+   if (set_dirty)
+   set_bit(RGN_FLAG_DIRTY, >rgn_flags);
 
-   if (rgn->rgn_state != HPB_RGN_INACTIVE &&
+   spin_lock_irqsave(>rgn_state_lock, flags);
+   if (set_dirty && rgn->rgn_state != HPB_RGN_INACTIVE &&
srgn->srgn_state == HPB_SRGN_VALID)
bitmap_set(srgn->mctx->ppn_dirty, srgn_offset, set_bit_len);
+   spin_unlock_irqrestore(>rgn_state_lock, flags);
 
srgn_offset = 0;
if (++srgn_idx == hpb->srgns_per_rgn) {
@@ -592,10 +596,8 @@ int ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb 
*lrbp)
 
/* If command type is WRITE or DISCARD, set bitmap as drity */
if (ufshpb_is_write_or_discard_cmd(cmd)) {
-   spin_lock_irqsave(>rgn_state_lock, flags);
-   ufshpb_set_ppn_dirty(hpb, rgn_idx, srgn_idx, srgn_offset,
-transfer_len);
-   spin_unlock_irqrestore(>rgn_state_lock, flags);
+   ufshpb_iterate_rgn(hpb, rgn_idx, srgn_idx, srgn_offset,
+  transfer_len, true);
return 0;
}
 
-- 
2.25.1



[PATCH v7 04/11] scsi: ufshpb: Add reads counter

2021-03-31 Thread Avri Altman
In host control mode, reads are the major source of activation trials.
Keep track of those reads counters, for both active as well inactive
regions.

We reset the read counter upon write - we are only interested in "clean"
reads.

Keep those counters normalized, as we are using those reads as a
comparative score, to make various decisions.
If during consecutive normalizations an active region has exhaust its
reads - inactivate it.

while at it, protect the {active,inactive}_count stats by adding them
into the applicable handler.

Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshpb.c | 94 ---
 drivers/scsi/ufs/ufshpb.h |  9 
 2 files changed, 97 insertions(+), 6 deletions(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 252fcfb48862..3ab66421dc00 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -16,6 +16,8 @@
 #include "ufshpb.h"
 #include "../sd.h"
 
+#define ACTIVATION_THRESHOLD 8 /* 8 IOs */
+
 /* memory management */
 static struct kmem_cache *ufshpb_mctx_cache;
 static mempool_t *ufshpb_mctx_pool;
@@ -26,6 +28,9 @@ static int tot_active_srgn_pages;
 
 static struct workqueue_struct *ufshpb_wq;
 
+static void ufshpb_update_active_info(struct ufshpb_lu *hpb, int rgn_idx,
+ int srgn_idx);
+
 bool ufshpb_is_allowed(struct ufs_hba *hba)
 {
return !(hba->ufshpb_dev.hpb_disabled);
@@ -148,7 +153,7 @@ static void ufshpb_iterate_rgn(struct ufshpb_lu *hpb, int 
rgn_idx, int srgn_idx,
   int srgn_offset, int cnt, bool set_dirty)
 {
struct ufshpb_region *rgn;
-   struct ufshpb_subregion *srgn;
+   struct ufshpb_subregion *srgn, *prev_srgn = NULL;
int set_bit_len;
int bitmap_len;
unsigned long flags;
@@ -167,15 +172,39 @@ static void ufshpb_iterate_rgn(struct ufshpb_lu *hpb, int 
rgn_idx, int srgn_idx,
else
set_bit_len = cnt;
 
-   if (set_dirty)
-   set_bit(RGN_FLAG_DIRTY, >rgn_flags);
-
spin_lock_irqsave(>rgn_state_lock, flags);
if (set_dirty && rgn->rgn_state != HPB_RGN_INACTIVE &&
srgn->srgn_state == HPB_SRGN_VALID)
bitmap_set(srgn->mctx->ppn_dirty, srgn_offset, set_bit_len);
spin_unlock_irqrestore(>rgn_state_lock, flags);
 
+   if (hpb->is_hcm && prev_srgn != srgn) {
+   bool activate = false;
+
+   spin_lock(>rgn_lock);
+   if (set_dirty) {
+   rgn->reads -= srgn->reads;
+   srgn->reads = 0;
+   set_bit(RGN_FLAG_DIRTY, >rgn_flags);
+   } else {
+   srgn->reads++;
+   rgn->reads++;
+   if (srgn->reads == ACTIVATION_THRESHOLD)
+   activate = true;
+   }
+   spin_unlock(>rgn_lock);
+
+   if (activate) {
+   spin_lock_irqsave(>rsp_list_lock, flags);
+   ufshpb_update_active_info(hpb, rgn_idx, srgn_idx);
+   spin_unlock_irqrestore(>rsp_list_lock, flags);
+   dev_dbg(>sdev_ufs_lu->sdev_dev,
+   "activate region %d-%d\n", rgn_idx, srgn_idx);
+   }
+
+   prev_srgn = srgn;
+   }
+
srgn_offset = 0;
if (++srgn_idx == hpb->srgns_per_rgn) {
srgn_idx = 0;
@@ -604,6 +633,19 @@ int ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb 
*lrbp)
if (!ufshpb_is_support_chunk(hpb, transfer_len))
return 0;
 
+   if (hpb->is_hcm) {
+   /*
+* in host control mode, reads are the main source for
+* activation trials.
+*/
+   ufshpb_iterate_rgn(hpb, rgn_idx, srgn_idx, srgn_offset,
+  transfer_len, false);
+
+   /* keep those counters normalized */
+   if (rgn->reads > hpb->entries_per_srgn)
+   schedule_work(>ufshpb_normalization_work);
+   }
+
spin_lock_irqsave(>rgn_state_lock, flags);
if (ufshpb_test_ppn_dirty(hpb, rgn_idx, srgn_idx, srgn_offset,
   transfer_len)) {
@@ -755,6 +797,8 @@ static void ufshpb_update_active_info(struct ufshpb_lu 
*hpb, int rgn_idx,
 
if (list_empty(>list_act_srgn))
list_add_tail(>list_act_srgn, >lh_act_srgn);
+
+   hpb->stats.rb_active_cnt++;
 }
 
 static void ufshpb_update_inactive_info(struct ufshpb_lu *hpb, int rgn_idx)
@@ -770,6 +814,8 @@ static void ufshpb_update_inactive_info(struct ufshpb_lu 
*hpb, int rgn_idx)
 
if (list_empty(>list_inact_rgn))
list_add_tail(>li

[PATCH v7 02/11] scsi: ufshpb: Add host control mode support to rsp_upiu

2021-03-31 Thread Avri Altman
In device control mode, the device may recommend the host to either
activate or inactivate a region, and the host should follow. Meaning
those are not actually recommendations, but more of instructions.

On the contrary, in host control mode, the recommendation protocol is
slightly changed:
a) The device may only recommend the host to update a subregion of an
   already-active region. And,
b) The device may *not* recommend to inactivate a region.

Furthermore, in host control mode, the host may choose not to follow any
of the device's recommendations. However, in case of a recommendation to
update an active and clean subregion, it is better to follow those
recommendation because otherwise the host has no other way to know that
some internal relocation took place.

Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshpb.c | 34 +-
 drivers/scsi/ufs/ufshpb.h |  2 ++
 2 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 5285a50b05dd..6111019ca31a 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -166,6 +166,8 @@ static void ufshpb_set_ppn_dirty(struct ufshpb_lu *hpb, int 
rgn_idx,
else
set_bit_len = cnt;
 
+   set_bit(RGN_FLAG_DIRTY, >rgn_flags);
+
if (rgn->rgn_state != HPB_RGN_INACTIVE &&
srgn->srgn_state == HPB_SRGN_VALID)
bitmap_set(srgn->mctx->ppn_dirty, srgn_offset, set_bit_len);
@@ -235,6 +237,11 @@ static bool ufshpb_test_ppn_dirty(struct ufshpb_lu *hpb, 
int rgn_idx,
return false;
 }
 
+static inline bool is_rgn_dirty(struct ufshpb_region *rgn)
+{
+   return test_bit(RGN_FLAG_DIRTY, >rgn_flags);
+}
+
 static int ufshpb_fill_ppn_from_page(struct ufshpb_lu *hpb,
 struct ufshpb_map_ctx *mctx, int pos,
 int len, u64 *ppn_buf)
@@ -712,6 +719,7 @@ static void ufshpb_put_map_req(struct ufshpb_lu *hpb,
 static int ufshpb_clear_dirty_bitmap(struct ufshpb_lu *hpb,
 struct ufshpb_subregion *srgn)
 {
+   struct ufshpb_region *rgn;
u32 num_entries = hpb->entries_per_srgn;
 
if (!srgn->mctx) {
@@ -725,6 +733,10 @@ static int ufshpb_clear_dirty_bitmap(struct ufshpb_lu *hpb,
num_entries = hpb->last_srgn_entries;
 
bitmap_zero(srgn->mctx->ppn_dirty, num_entries);
+
+   rgn = hpb->rgn_tbl + srgn->rgn_idx;
+   clear_bit(RGN_FLAG_DIRTY, >rgn_flags);
+
return 0;
 }
 
@@ -1244,6 +1256,18 @@ static void ufshpb_rsp_req_region_update(struct 
ufshpb_lu *hpb,
srgn_i =
be16_to_cpu(rsp_field->hpb_active_field[i].active_srgn);
 
+   rgn = hpb->rgn_tbl + rgn_i;
+   if (hpb->is_hcm &&
+   (rgn->rgn_state != HPB_RGN_ACTIVE || is_rgn_dirty(rgn))) {
+   /*
+* in host control mode, subregion activation
+* recommendations are only allowed to active regions.
+* Also, ignore recommendations for dirty regions - the
+* host will make decisions concerning those by himself
+*/
+   continue;
+   }
+
dev_dbg(>sdev_ufs_lu->sdev_dev,
"activate(%d) region %d - %d\n", i, rgn_i, srgn_i);
 
@@ -1251,7 +1275,6 @@ static void ufshpb_rsp_req_region_update(struct ufshpb_lu 
*hpb,
ufshpb_update_active_info(hpb, rgn_i, srgn_i);
spin_unlock(>rsp_list_lock);
 
-   rgn = hpb->rgn_tbl + rgn_i;
srgn = rgn->srgn_tbl + srgn_i;
 
/* blocking HPB_READ */
@@ -1262,6 +1285,14 @@ static void ufshpb_rsp_req_region_update(struct 
ufshpb_lu *hpb,
hpb->stats.rb_active_cnt++;
}
 
+   if (hpb->is_hcm) {
+   /*
+* in host control mode the device is not allowed to inactivate
+* regions
+*/
+   goto out;
+   }
+
for (i = 0; i < rsp_field->inactive_rgn_cnt; i++) {
rgn_i = be16_to_cpu(rsp_field->hpb_inactive_field[i]);
dev_dbg(>sdev_ufs_lu->sdev_dev,
@@ -1286,6 +1317,7 @@ static void ufshpb_rsp_req_region_update(struct ufshpb_lu 
*hpb,
hpb->stats.rb_inactive_cnt++;
}
 
+out:
dev_dbg(>sdev_ufs_lu->sdev_dev, "Noti: #ACT %u #INACT %u\n",
rsp_field->active_rgn_cnt, rsp_field->inactive_rgn_cnt);
 
diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
index 7df30340386a..032672114881 100644
--- a/drivers/scsi/ufs/ufshpb.h
+++ b/drivers/scsi/ufs/ufshpb.h
@@ -121,6 +121,8 @@ struct ufshpb_region {
 
/* below i

[PATCH v7 01/11] scsi: ufshpb: Cache HPB Control mode on init

2021-03-31 Thread Avri Altman
We will use it later, when we'll need to differentiate between device
and host control modes.

Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshcd.h | 2 ++
 drivers/scsi/ufs/ufshpb.c | 8 +---
 drivers/scsi/ufs/ufshpb.h | 2 ++
 3 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 4dbe9bc60e85..c01f75963750 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -656,6 +656,7 @@ struct ufs_hba_variant_params {
  * @hpb_disabled: flag to check if HPB is disabled
  * @max_hpb_single_cmd: maximum size of single HPB command
  * @is_legacy: flag to check HPB 1.0
+ * @control_mode: either host or device
  */
 struct ufshpb_dev_info {
int num_lu;
@@ -665,6 +666,7 @@ struct ufshpb_dev_info {
bool hpb_disabled;
int max_hpb_single_cmd;
bool is_legacy;
+   u8 control_mode;
 };
 #endif
 
diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 86805af9abe7..5285a50b05dd 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -1615,6 +1615,9 @@ static void ufshpb_lu_parameter_init(struct ufs_hba *hba,
 % (hpb->srgn_mem_size / HPB_ENTRY_SIZE);
 
hpb->pages_per_srgn = DIV_ROUND_UP(hpb->srgn_mem_size, PAGE_SIZE);
+
+   if (hpb_dev_info->control_mode == HPB_HOST_CONTROL)
+   hpb->is_hcm = true;
 }
 
 static int ufshpb_alloc_region_tbl(struct ufs_hba *hba, struct ufshpb_lu *hpb)
@@ -2308,11 +2311,10 @@ void ufshpb_get_dev_info(struct ufs_hba *hba, u8 
*desc_buf)
 {
struct ufshpb_dev_info *hpb_dev_info = >ufshpb_dev;
int version, ret;
-   u8 hpb_mode;
u32 max_hpb_single_cmd = HPB_MULTI_CHUNK_LOW;
 
-   hpb_mode = desc_buf[DEVICE_DESC_PARAM_HPB_CONTROL];
-   if (hpb_mode == HPB_HOST_CONTROL) {
+   hpb_dev_info->control_mode = desc_buf[DEVICE_DESC_PARAM_HPB_CONTROL];
+   if (hpb_dev_info->control_mode == HPB_HOST_CONTROL) {
dev_err(hba->dev, "%s: host control mode is not supported.\n",
__func__);
hpb_dev_info->hpb_disabled = true;
diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
index b1128b0ce486..7df30340386a 100644
--- a/drivers/scsi/ufs/ufshpb.h
+++ b/drivers/scsi/ufs/ufshpb.h
@@ -228,6 +228,8 @@ struct ufshpb_lu {
u32 entries_per_srgn_shift;
u32 pages_per_srgn;
 
+   bool is_hcm;
+
struct ufshpb_stats stats;
struct ufshpb_params params;
 
-- 
2.25.1



[PATCH v7 00/11] Add Host control mode to HPB

2021-03-31 Thread Avri Altman
v6 -> v7:
 - attend CanG's comments
 - add one more patch to transform set_dirty to iterate_rgn
 - rebase on Daejun's v32

v5 -> v6:
 - attend CanG's comments
 - rebase on Daejun's v29

v4 -> v5:
 - attend Daejun's comments
 - Control the number of inflight map requests

v3 -> v4:
 - rebase on Daejun's v25

v2 -> v3:
 - Attend Greg's and Can's comments
 - rebase on Daejun's v21

v1 -> v2:
 - attend Greg's and Daejun's comments
 - add patch 9 making host mode parameters configurable
 - rebase on Daejun's v19


The HPB spec defines 2 control modes - device control mode and host
control mode. In oppose to device control mode, in which the host obey
to whatever recommendation received from the device - In host control
mode, the host uses its own algorithms to decide which regions should
be activated or inactivated.

We kept the host managed heuristic simple and concise.

Aside from adding a by-spec functionality, host control mode entails
some further potential benefits: makes the hpb logic transparent and
readable, while allow tuning / scaling its various parameters, and
utilize system-wide info to optimize HPB potential.

This series is based on Samsung's V32 device-control HPB2.0 driver

This version was tested on Galaxy S20, and Xiaomi Mi10 pro.
Your meticulous review and testing is mostly welcome and appreciated.

Thanks,
Avri


Avri Altman (11):
  scsi: ufshpb: Cache HPB Control mode on init
  scsi: ufshpb: Add host control mode support to rsp_upiu
  scsi: ufshpb: Transform set_dirty to iterate_rgn
  scsi: ufshpb: Add reads counter
  scsi: ufshpb: Make eviction depends on region's reads
  scsi: ufshpb: Region inactivation in host mode
  scsi: ufshpb: Add hpb dev reset response
  scsi: ufshpb: Add "Cold" regions timer
  scsi: ufshpb: Limit the number of inflight map requests
  scsi: ufshpb: Add support for host control mode
  scsi: ufshpb: Make host mode parameters configurable

 Documentation/ABI/testing/sysfs-driver-ufs |  84 ++-
 drivers/scsi/ufs/ufshcd.h  |   2 +
 drivers/scsi/ufs/ufshpb.c  | 568 -
 drivers/scsi/ufs/ufshpb.h  |  44 ++
 4 files changed, 663 insertions(+), 35 deletions(-)

-- 
2.25.1



RE: [PATCH 1/1] scsi: ufs: Remove duplicated header file inclusion

2021-03-28 Thread Avri Altman
> 
> The header file  is already included above and can be
> removed here.
Already removed, see
https://lore.kernel.org/lkml/20210306114706.217873-1-zhang.yun...@zte.com.cn/

Thanks,
Avri

> 
> Signed-off-by: Zhen Lei 
> ---
>  drivers/scsi/ufs/ufshcd.c | 1 -
>  1 file changed, 1 deletion(-)
> 
> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
> index c86760788c72c9a..e8aa7de17d0accd 100644
> --- a/drivers/scsi/ufs/ufshcd.c
> +++ b/drivers/scsi/ufs/ufshcd.c
> @@ -24,7 +24,6 @@
>  #include "ufs_bsg.h"
>  #include "ufshcd-crypto.h"
>  #include 
> -#include 
> 
>  #define CREATE_TRACE_POINTS
>  #include 
> --
> 1.8.3
> 



RE: [PATCH v6 03/10] scsi: ufshpb: Add region's reads counter

2021-03-27 Thread Avri Altman
> > > @@ -596,12 +615,43 @@ int ufshpb_prep(struct ufs_hba *hba, struct
> > > ufshcd_lrb *lrbp)
> > >   ufshpb_set_ppn_dirty(hpb, rgn_idx, srgn_idx, srgn_offset,
> > >transfer_len);
> > >   spin_unlock_irqrestore(>rgn_state_lock, flags);
> > > +
> > > + if (hpb->is_hcm) {
> > > + spin_lock(>rgn_lock);
> > > + rgn->reads = 0;
> > > + spin_unlock(>rgn_lock);
> Here also.
> 
> > > + }
> > > +
> > >   return 0;
> > >   }
> > >
> > >   if (!ufshpb_is_support_chunk(hpb, transfer_len))
> > >   return 0;
> > >
> > > + if (hpb->is_hcm) {
> > > + bool activate = false;
> > > + /*
> > > +  * in host control mode, reads are the main source for
> > > +  * activation trials.
> > > +  */
> > > + spin_lock(>rgn_lock);
> > > + rgn->reads++;
> > > + if (rgn->reads == ACTIVATION_THRESHOLD)
> > > + activate = true;
> > > + spin_unlock(>rgn_lock);
> > > + if (activate) {
> > > + spin_lock_irqsave(>rsp_list_lock, flags);
> > > + ufshpb_update_active_info(hpb, rgn_idx, srgn_idx);
> >
> > If a transfer_len (possible with HPB2.0) sits accross two
> > regions/sub-regions,
> > here it only updates active info of the first region/sub-region.
> Yes.  Will fix.
Giving it another look, I noticed that the current design only support a single 
subregion per region.
As activation is done per subregion, we need to count reads per subregion and 
make those activation decisions accordingly. 
Still, the read counter per region needs to stay, as well as its spinlock for 
the inactivation decision.

Will fix it in my next version.  Waiting for v32.

Thanks,
Avri

> 
> Thanks,
> Avri
> 
> >
> > Thanks,
> > Can Guo.
> >


RE: [PATCH v6 04/10] scsi: ufshpb: Make eviction depends on region's reads

2021-03-24 Thread Avri Altman
> 
> On 2021-03-22 16:10, Avri Altman wrote:
> > In host mode, eviction is considered an extreme measure.
> > verify that the entering region has enough reads, and the exiting
> > region has much less reads.
> >
> > Signed-off-by: Avri Altman 
> > ---
> >  drivers/scsi/ufs/ufshpb.c | 18 +-
> >  1 file changed, 17 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
> > index a1519cbb4ce0..5e757220d66a 100644
> > --- a/drivers/scsi/ufs/ufshpb.c
> > +++ b/drivers/scsi/ufs/ufshpb.c
> > @@ -17,6 +17,7 @@
> >  #include "../sd.h"
> >
> >  #define ACTIVATION_THRESHOLD 8 /* 8 IOs */
> > +#define EVICTION_THRESHOLD (ACTIVATION_THRESHOLD << 5) /* 256 IOs
> */
> >
> >  /* memory management */
> >  static struct kmem_cache *ufshpb_mctx_cache;
> > @@ -1047,6 +1048,13 @@ static struct ufshpb_region
> > *ufshpb_victim_lru_info(struct ufshpb_lu *hpb)
> >   if (ufshpb_check_srgns_issue_state(hpb, rgn))
> >   continue;
> >
> > + /*
> > +  * in host control mode, verify that the exiting region
> > +  * has less reads
> > +  */
> > + if (hpb->is_hcm && rgn->reads > (EVICTION_THRESHOLD >> 1))
> > + continue;
> > +
> >   victim_rgn = rgn;
> >   break;
> >   }
> > @@ -1219,7 +1227,7 @@ static int ufshpb_issue_map_req(struct ufshpb_lu
> > *hpb,
> >
> >  static int ufshpb_add_region(struct ufshpb_lu *hpb, struct
> > ufshpb_region *rgn)
> >  {
> > - struct ufshpb_region *victim_rgn;
> > + struct ufshpb_region *victim_rgn = NULL;
> >   struct victim_select_info *lru_info = >lru_info;
> >   unsigned long flags;
> >   int ret = 0;
> > @@ -1246,7 +1254,15 @@ static int ufshpb_add_region(struct ufshpb_lu
> > *hpb, struct ufshpb_region *rgn)
> >* It is okay to evict the least recently used region,
> >* because the device could detect this region
> >* by not issuing HPB_READ
> > +  *
> > +  * in host control mode, verify that the entering
> > +  * region has enough reads
> >*/
> > + if (hpb->is_hcm && rgn->reads < EVICTION_THRESHOLD) {
> > + ret = -EACCES;
> > + goto out;
> > + }
> > +
> 
> I cannot understand the logic behind this. A rgn which host chooses to
> activate,
> is in INACTIVE state now, if its rgn->reads < 256, then don't activate
> it.
> Could you please elaborate?
I am re-citing the commit log:
"In host mode, eviction is considered an extreme measure.
verify that the entering region has enough reads, and the exiting
region has much less reads."

Here comes to play the reads counter as a comparative index.
Max-active-regions has crossed, and to activate a region, you need to evict 
another region.
But the activation threshold is relatively low, how do you know that you will 
benefit more,
>From the new region, than from the one you choose to evict?

Not to arbitrarily evict the "first" (LRU) region, like in device mode, we are 
looking for a solid
Reason for the new region to enter, and for the existing region to leave.
Otherwise, you will find yourself entering and existing the same region over 
and over,
Just threshing the active-list creating an unnecessary overhead by keep sending 
map requests.
For example, say the entering region has 4 reads, but the LRU region has 200, 
and its reads keeps coming.
Is it the "correct" decision to evict a 200-reads region for a 4-reads region?
If you indeed evict this 200-reads region, you will evict another to put it 
right back,
Over and over.

On the other hand, we are not hanging-on to "cold" regions, and inactivate them 
if there are no recent
Reads to that region - see the patch with the "Cold" timeout.

I agree that this can be elaborate to a more sophisticated policies - which we 
tried.
For now, let's go with the simplest one - use thresholds for both the entering 
and exiting regions.

Thanks,
Avri
> 
> Thanks,
> Can Guo.
> 
> >   victim_rgn = ufshpb_victim_lru_info(hpb);
> >   if (!victim_rgn) {
> >   dev_warn(>sdev_ufs_lu->sdev_dev,


RE: [PATCH v6 02/10] scsi: ufshpb: Add host control mode support to rsp_upiu

2021-03-24 Thread Avri Altman
> >> @@ -1245,6 +1257,18 @@ static void
> ufshpb_rsp_req_region_update(struct
> >> ufshpb_lu *hpb,
> >>  srgn_i =
> >>  
> >> be16_to_cpu(rsp_field->hpb_active_field[i].active_srgn);
> >>
> >> +rgn = hpb->rgn_tbl + rgn_i;
> >> +if (hpb->is_hcm &&
> >> +(rgn->rgn_state != HPB_RGN_ACTIVE || is_rgn_dirty(rgn))) {
> >> +/*
> >> + * in host control mode, subregion activation
> >> + * recommendations are only allowed to active regions.
> >> + * Also, ignore recommendations for dirty regions - 
> >> the
> >> + * host will make decisions concerning those by 
> >> himself
> >> + */
> >> +continue;
> >> +}
> >> +
> >
> > Hi Avri, host control mode also need the recommendations from device,
> > because the bkops would make the ppn invalid, is that right?
> 
> Right, but ONLY recommandations to ACTIVE regions are of host's interest
> in host control mode. For those inactive regions, host makes its own
> decisions.
Correct.
Generally speaking, in host control mode, the host may ignore the device 
recommendation altogether.
In host mode the device is not allowed to send recommendation to Inactive 
regions.
Furthermore, as the comment above indicates, we *chose* to ignore 
recommendation for DIRTY regions.
We do so, because the host is aware of dirty regions and can make the decision 
per its own internal logic.
ONLY if the region is ACTIVE and CLEAN - the host is unaware that bkops took 
place,
So we *chose* to act per the device recommendation.  

Thanks,
Avri
> 
> Can Guo.
> 
> >
> >>  dev_dbg(>sdev_ufs_lu->sdev_dev,
> >>  "activate(%d) region %d - %d\n", i, rgn_i, srgn_i);
> >>
> >> @@ -1252,7 +1276,6 @@ static void ufshpb_rsp_req_region_update(struct
> >> ufshpb_lu *hpb,
> >>  ufshpb_update_active_info(hpb, rgn_i, srgn_i);
> >>  spin_unlock(>rsp_list_lock);
> >>
> >> -rgn = hpb->rgn_tbl + rgn_i;
> >>  srgn = rgn->srgn_tbl + srgn_i;
> >>
> >>  /* blocking HPB_READ */
> >> @@ -1263,6 +1286,14 @@ static void
> ufshpb_rsp_req_region_update(struct
> >> ufshpb_lu *hpb,
> >>  hpb->stats.rb_active_cnt++;
> >>  }
> >>
> >> +if (hpb->is_hcm) {
> >> +/*
> >> + * in host control mode the device is not allowed to 
> >> inactivate
> >> + * regions
> >> + */
> >> +goto out;
> >> +}
> >> +
> >>  for (i = 0; i < rsp_field->inactive_rgn_cnt; i++) {
> >>  rgn_i = be16_to_cpu(rsp_field->hpb_inactive_field[i]);
> >>  dev_dbg(>sdev_ufs_lu->sdev_dev,
> >> @@ -1287,6 +1318,7 @@ static void ufshpb_rsp_req_region_update(struct
> >> ufshpb_lu *hpb,
> >>  hpb->stats.rb_inactive_cnt++;
> >>  }
> >>
> >> +out:
> >>  dev_dbg(>sdev_ufs_lu->sdev_dev, "Noti: #ACT %u #INACT %u\n",
> >>  rsp_field->active_rgn_cnt, rsp_field->inactive_rgn_cnt);
> >>
> >> diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
> >> index 7df30340386a..032672114881 100644
> >> --- a/drivers/scsi/ufs/ufshpb.h
> >> +++ b/drivers/scsi/ufs/ufshpb.h
> >> @@ -121,6 +121,8 @@ struct ufshpb_region {
> >>
> >>  /* below information is used by lru */
> >>  struct list_head list_lru_rgn;
> >> +unsigned long rgn_flags;
> >> +#define RGN_FLAG_DIRTY 0
> >>  };
> >>
> >>  #define for_each_sub_region(rgn, i, srgn)   \
> >> --
> >> 2.25.1
> >>


RE: [PATCH v6 03/10] scsi: ufshpb: Add region's reads counter

2021-03-24 Thread Avri Altman
> > @@ -596,12 +615,43 @@ int ufshpb_prep(struct ufs_hba *hba, struct
> > ufshcd_lrb *lrbp)
> >   ufshpb_set_ppn_dirty(hpb, rgn_idx, srgn_idx, srgn_offset,
> >transfer_len);
> >   spin_unlock_irqrestore(>rgn_state_lock, flags);
> > +
> > + if (hpb->is_hcm) {
> > + spin_lock(>rgn_lock);
> > + rgn->reads = 0;
> > + spin_unlock(>rgn_lock);
Here also.

> > + }
> > +
> >   return 0;
> >   }
> >
> >   if (!ufshpb_is_support_chunk(hpb, transfer_len))
> >   return 0;
> >
> > + if (hpb->is_hcm) {
> > + bool activate = false;
> > + /*
> > +  * in host control mode, reads are the main source for
> > +  * activation trials.
> > +  */
> > + spin_lock(>rgn_lock);
> > + rgn->reads++;
> > + if (rgn->reads == ACTIVATION_THRESHOLD)
> > + activate = true;
> > + spin_unlock(>rgn_lock);
> > + if (activate) {
> > + spin_lock_irqsave(>rsp_list_lock, flags);
> > + ufshpb_update_active_info(hpb, rgn_idx, srgn_idx);
> 
> If a transfer_len (possible with HPB2.0) sits accross two
> regions/sub-regions,
> here it only updates active info of the first region/sub-region.
Yes.  Will fix.

Thanks,
Avri

> 
> Thanks,
> Can Guo.
> 


RE: [PATCH v31 2/4] scsi: ufs: L2P map management for HPB read

2021-03-23 Thread Avri Altman
> 
> On 2021-03-23 14:37, Daejun Park wrote:
> >> On 2021-03-23 14:19, Daejun Park wrote:
>  On 2021-03-23 13:37, Daejun Park wrote:
> >> On 2021-03-23 12:22, Can Guo wrote:
> >>> On 2021-03-22 17:11, Bean Huo wrote:
>  On Mon, 2021-03-22 at 15:54 +0900, Daejun Park wrote:
> > +   switch (rsp_field->hpb_op) {
> >
> > +   case HPB_RSP_REQ_REGION_UPDATE:
> >
> > +   if (data_seg_len != DEV_DATA_SEG_LEN)
> >
> > +   dev_warn(>sdev_ufs_lu->sdev_dev,
> >
> > +"%s: data seg length is not
> > same.\n",
> >
> > +__func__);
> >
> > +   ufshpb_rsp_req_region_update(hpb, rsp_field);
> >
> > +   break;
> >
> > +   case HPB_RSP_DEV_RESET:
> >
> > +   dev_warn(>sdev_ufs_lu->sdev_dev,
> >
> > +"UFS device lost HPB information
> > during
> > PM.\n");
> >
> > +   break;
> 
>  Hi Deajun,
>  This series looks good to me. Just here I have one question. You
>  didn't
>  handle HPB_RSP_DEV_RESET, just a warning.  Based on your SS UFS,
>  how
>  to
>  handle HPB_RSP_DEV_RESET from the host side? Do you think we
>  shoud
>  reset host side HPB entry as well or what else?
> 
> 
>  Bean
> >>>
> >>> Same question here - I am still collecting feedbacks from flash
> >>> vendors
> >>> about
> >>> what is recommanded host behavior on reception of HPB Op code
> >>> 0x2,
> >>> since it
> >>> is not cleared defined in HPB2.0 specs.
> >>>
> >>> Can Guo.
> >>
> >> I think the question should be asked in the HPB2.0 patch, since in
> >> HPB1.0 device
> >> control mode, a HPB reset in device side does not impact anything
> >> in
> >> host side -
> >> host is not writing back any HPB entries to device anyways and HPB
> >> Read
> >> cmd with
> >> invalid HPB entries shall be treated as normal Read(10) cmd
> >> without
> >> any
> >> problems.
> >
> > Yes, UFS device will process read command even the HPB entries are
> > valid or
> > not. So it is warning about read performance drop by dev reset.
> 
>  Yeah, but still I am 100% sure about what should host do in case of
>  HPB2.0
>  when it receives HPB Op code 0x2, I am waiting for feedbacks.
> >>>
> >>> I think the host has two choices when it receives 0x2.
> >>> One is nothing on host.
> >>> The other is discarding all HPB entries in the host.
> >>>
> >>> In the JEDEC HPB spec, it as follows:
> >>> When the device is powered off by the host, the device may restore
> >>> L2P
> >>> map
> >>> data upon power up or build from the host’s HPB READ command.
> >>>
> >>> If some UFS builds L2P map data from the host's HPB READ commands, we
> >>> don't
> >>> have to discard HPB entries in the host.
> >>>
> >>> So I thinks there is nothing to do when it receives 0x2.
> >>
> >> But in HPB2.0, if we do nothing to active regions in host side, host
> >> can
> >> write
> >> HPB entries (which host thinks valid, but actually invalid in device
> >> side since
> >> reset happened) back to device through HPB Write Buffer cmds (BUFFER
> >> ID
> >> = 0x2).
> >> My question is that are all UFSs OK with this?
> >
> > Yes, it must be OK.
> >
> > Please refer the following the HPB 2.0 spec:
> >
> > If the HPB Entries sent by HPB WRITE BUFFER are removed by the device,
> > for example, because they are not consumed for a long enough period of
> > time,
> > then the HPB READ command for the removed HPB entries shall be handled
> > as a
> > normal READ command.
> >
> 
> No, it is talking about the subsequent HPB READ cmd sent after a HPB
> WRITE BUFFER cmd,
> but not the HPB WRITE BUFFER cmd itself...
Looks like this discussion is going the same way as we had in host mode.
HPB-WRITE-BUFFER 0x2, if exist,  is always a companion to HPB-READ.
You shouldn't consider them separately.

The device is expected to handle invalid ppn by itself, and specifically for 
this case,
As Daejun explained, Handle each HPB-READ (and its companion HPB-WRITE-BUFFER) 
like READ10.

For device mode, doing nothing in case of dev reset, seems to me like the right 
thing to do.

Thanks,
Avri

> 
> Thanks,
> Can Guo.
> 
> > Thanks,
> > Daejun
> >
> >> Thanks,
> >> Can Guo.
> >>
> >>>
> >>> Thanks,
> >>> Daejun
> >>>
>  Thanks,
>  Can Guo.
> 
> >
> > Thanks,
> > Daejun
> >
> >> Please correct me if I am wrong.
> >
> >
> >
> >> Thanks,
> >> Can Guo.
> >>
> >>
> >>
> 
> 
> 
> >>
> >>
> >>


[PATCH 2/2] mmc: block: Update ext_csd.cache_ctrl if it was written

2021-03-22 Thread Avri Altman
The cache function can be turned ON and OFF by writing to the CACHE_CTRL
byte (EXT_CSD byte [33]).  However,  card->ext_csd.cache_ctrl is only
set on init if cache size > 0.

Fix that by explicitly setting ext_csd.cache_ctrl on ext-csd write.

Signed-off-by: Avri Altman 
---
 drivers/mmc/core/block.c | 8 
 1 file changed, 8 insertions(+)

diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index a93c66aa91db..4c816d001ac1 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -571,6 +571,14 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, 
struct mmc_blk_data *md,
main_md->part_curr = value & EXT_CSD_PART_CONFIG_ACC_MASK;
}
 
+   /* Make sure to update CACHE_CTRL in case it was changed */
+   if ((MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_CACHE_CTRL) &&
+   (cmd.opcode == MMC_SWITCH)) {
+   u8 value = MMC_EXTRACT_VALUE_FROM_ARG(cmd.arg) & 1;
+
+   card->ext_csd.cache_ctrl = value;
+   }
+
/*
 * According to the SD specs, some commands require a delay after
 * issuing the command.
-- 
2.25.1



[PATCH 1/2] mmc: block: Issue flush only if allowed

2021-03-22 Thread Avri Altman
The cache may be flushed to the nonvolatile storage by writing to
FLUSH_CACHE byte (EXT_CSD byte [32]). When in command queueing mode, the
cache may be flushed by issuing a CMDQ_TASK_ DEV_MGMT (CMD48) with a
FLUSH_CACHE op-code.  Either way, verify that The cache function is
turned ON before doing so.

fixes: 1e8e55b67030 (mmc: block: Add CQE support)

Reported-by: Brendan Peter 
Tested-by: Brendan Peter 
Signed-off-by: Avri Altman 
---
 drivers/mmc/core/block.c   | 3 +++
 drivers/mmc/core/mmc_ops.c | 4 +---
 drivers/mmc/core/mmc_ops.h | 5 +
 3 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index 42e27a298218..a93c66aa91db 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -1473,6 +1473,9 @@ static int mmc_blk_cqe_issue_flush(struct mmc_queue *mq, 
struct request *req)
struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req);
struct mmc_request *mrq = mmc_blk_cqe_prep_dcmd(mqrq, req);
 
+   if (mmc_card_mmc(mq->card) && !mmc_flush_allowed(mq->card))
+   return 0;
+
mrq->cmd->opcode = MMC_SWITCH;
mrq->cmd->arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
(EXT_CSD_FLUSH_CACHE << 16) |
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index baa6314f69b4..b8a0c9ac8a20 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -988,9 +988,7 @@ int mmc_flush_cache(struct mmc_card *card)
 {
int err = 0;
 
-   if (mmc_card_mmc(card) &&
-   (card->ext_csd.cache_size > 0) &&
-   (card->ext_csd.cache_ctrl & 1)) {
+   if (mmc_card_mmc(card) && mmc_flush_allowed(card)) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
 EXT_CSD_FLUSH_CACHE, 1,
 MMC_CACHE_FLUSH_TIMEOUT_MS);
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 632009260e51..bf2b315addd7 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -19,6 +19,11 @@ enum mmc_busy_cmd {
 struct mmc_host;
 struct mmc_card;
 
+static inline bool mmc_flush_allowed(struct mmc_card *card)
+{
+   return card->ext_csd.cache_size > 0 && (card->ext_csd.cache_ctrl & 1);
+}
+
 int mmc_select_card(struct mmc_card *card);
 int mmc_deselect_cards(struct mmc_host *host);
 int mmc_set_dsr(struct mmc_host *host);
-- 
2.25.1



[PATCH 0/2] Do not flush cache when it is disabled

2021-03-22 Thread Avri Altman
Cache is a temporary storage space in an eMMC device. Volatile by
nature, the cache should in typical case reduce the access time compared
to an access to the main nonvolatile storage.

The cache function can be turned ON and OFF. Once OFF, the host is not
expected to issue a flush-cache command to the device.


Avri Altman (2):
  mmc: block: Issue flush only if allowed
  mmc: block: Update ext_csd.cache_ctrl if it was written

 drivers/mmc/core/block.c   | 11 +++
 drivers/mmc/core/mmc_ops.c |  4 +---
 drivers/mmc/core/mmc_ops.h |  5 +
 3 files changed, 17 insertions(+), 3 deletions(-)

-- 
2.25.1



[PATCH v6 10/10] scsi: ufshpb: Make host mode parameters configurable

2021-03-22 Thread Avri Altman
We can make use of this commit, to elaborate some more of the host
control mode logic, explaining what role play each and every variable.

While at it, allow those parameters to be configurable.

Signed-off-by: Avri Altman 
---
 Documentation/ABI/testing/sysfs-driver-ufs |  84 +-
 drivers/scsi/ufs/ufshpb.c  | 289 +++--
 drivers/scsi/ufs/ufshpb.h  |  20 ++
 3 files changed, 366 insertions(+), 27 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-driver-ufs 
b/Documentation/ABI/testing/sysfs-driver-ufs
index 419adf450b89..133af2114165 100644
--- a/Documentation/ABI/testing/sysfs-driver-ufs
+++ b/Documentation/ABI/testing/sysfs-driver-ufs
@@ -1323,14 +1323,76 @@ Description:This entry shows the maximum HPB data 
size for using single HPB
 
The file is read only.
 
-What:  /sys/bus/platform/drivers/ufshcd/*/flags/wb_enable
-Date:  March 2021
-Contact:   Daejun Park 
-Description:   This entry shows the status of HPB.
-
-   == 
-   0  HPB is not enabled.
-   1  HPB is enabled
-   == 
-
-   The file is read only.
+What:  /sys/class/scsi_device/*/device/hpb_param_sysfs/activation_thld
+Date:  February 2021
+Contact:   Avri Altman 
+Description:   In host control mode, reads are the major source of activation
+   trials.  once this threshold hs met, the region is added to the
+   "to-be-activated" list.  Since we reset the read counter upon
+   write, this include sending a rb command updating the region
+   ppn as well.
+
+What:  
/sys/class/scsi_device/*/device/hpb_param_sysfs/normalization_factor
+Date:  February 2021
+Contact:   Avri Altman 
+Description:   In host control mode, We think of the regions as "buckets".
+   Those buckets are being filled with reads, and emptied on write.
+   We use entries_per_srgn - the amount of blocks in a subregion as
+   our bucket size.  This applies because HPB1.0 only concern a
+   single-block reads.  Once the bucket size is crossed, we trigger
+   a normalization work - not only to avoid overflow, but mainly
+   because we want to keep those counters normalized, as we are
+   using those reads as a comparative score, to make various 
decisions.
+   The normalization is dividing (shift right) the read counter by
+   the normalization_factor. If during consecutive normalizations
+   an active region has exhaust its reads - inactivate it.
+
+What:  
/sys/class/scsi_device/*/device/hpb_param_sysfs/eviction_thld_enter
+Date:  February 2021
+Contact:   Avri Altman 
+Description:   Region deactivation is often due to the fact that eviction took
+   place: a region become active on the expense of another. This is
+   happening when the max-active-regions limit has crossed.
+   In host mode, eviction is considered an extreme measure. We
+   want to verify that the entering region has enough reads, and
+   the exiting region has much less reads.  eviction_thld_enter is
+   the min reads that a region must have in order to be considered
+   as a candidate to evict other region.
+
+What:  
/sys/class/scsi_device/*/device/hpb_param_sysfs/eviction_thld_exit
+Date:  February 2021
+Contact:   Avri Altman 
+Description:   same as above for the exiting region. A region is consider to
+   be a candidate to be evicted, only if it has less reads than
+   eviction_thld_exit.
+
+What:  /sys/class/scsi_device/*/device/hpb_param_sysfs/read_timeout_ms
+Date:  February 2021
+Contact:   Avri Altman 
+Description:   In order not to hang on to “cold” regions, we shall inactivate
+   a region that has no READ access for a predefined amount of
+   time - read_timeout_ms. If read_timeout_ms has expired, and the
+   region is dirty - it is less likely that we can make any use of
+   HPB-READing it.  So we inactivate it.  Still, deactivation has
+   its overhead, and we may still benefit from HPB-READing this
+   region if it is clean - see read_timeout_expiries.
+
+What:  
/sys/class/scsi_device/*/device/hpb_param_sysfs/read_timeout_expiries
+Date:  February 2021
+Contact:   Avri Altman 
+Description:   if the region read timeout has expired, but the region is clean,
+   just re-wind its timer for another spin.  Do that as long as it
+   is clean and did not exhaust its read_timeout_expiries 
threshold.
+
+What:  
/sys/class/scsi_device/*/device/hpb_param_sysfs/timeout_polli

[PATCH v6 08/10] scsi: ufshpb: Limit the number of inflight map requests

2021-03-22 Thread Avri Altman
In host control mode the host is the originator of map requests. To not
flood the device with map requests, use a simple throttling mechanism
that limits the number of inflight map requests.

Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshpb.c | 11 +++
 drivers/scsi/ufs/ufshpb.h |  1 +
 2 files changed, 12 insertions(+)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 4639d3b5ddc0..f755f5a7775c 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -21,6 +21,7 @@
 #define READ_TO_MS 1000
 #define READ_TO_EXPIRIES 100
 #define POLLING_INTERVAL_MS 200
+#define THROTTLE_MAP_REQ_DEFAULT 1
 
 /* memory management */
 static struct kmem_cache *ufshpb_mctx_cache;
@@ -747,6 +748,14 @@ static struct ufshpb_req *ufshpb_get_map_req(struct 
ufshpb_lu *hpb,
struct ufshpb_req *map_req;
struct bio *bio;
 
+   if (hpb->is_hcm &&
+   hpb->num_inflight_map_req >= THROTTLE_MAP_REQ_DEFAULT) {
+   dev_info(>sdev_ufs_lu->sdev_dev,
+"map_req throttle. inflight %d throttle %d",
+hpb->num_inflight_map_req, THROTTLE_MAP_REQ_DEFAULT);
+   return NULL;
+   }
+
map_req = ufshpb_get_req(hpb, srgn->rgn_idx, REQ_OP_SCSI_IN, false);
if (!map_req)
return NULL;
@@ -761,6 +770,7 @@ static struct ufshpb_req *ufshpb_get_map_req(struct 
ufshpb_lu *hpb,
 
map_req->rb.srgn_idx = srgn->srgn_idx;
map_req->rb.mctx = srgn->mctx;
+   hpb->num_inflight_map_req++;
 
return map_req;
 }
@@ -770,6 +780,7 @@ static void ufshpb_put_map_req(struct ufshpb_lu *hpb,
 {
bio_put(map_req->bio);
ufshpb_put_req(hpb, map_req);
+   hpb->num_inflight_map_req--;
 }
 
 static int ufshpb_clear_dirty_bitmap(struct ufshpb_lu *hpb,
diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
index c2821504a2d8..c9b8ee540704 100644
--- a/drivers/scsi/ufs/ufshpb.h
+++ b/drivers/scsi/ufs/ufshpb.h
@@ -213,6 +213,7 @@ struct ufshpb_lu {
struct ufshpb_req *pre_req;
int num_inflight_pre_req;
int throttle_pre_req;
+   int num_inflight_map_req;
struct list_head lh_pre_req_free;
int cur_read_id;
int pre_req_min_tr_len;
-- 
2.25.1



[PATCH v6 09/10] scsi: ufshpb: Add support for host control mode

2021-03-22 Thread Avri Altman
Support devices that report they are using host control mode.

Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshpb.c | 6 --
 1 file changed, 6 deletions(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index f755f5a7775c..51c527c6f8c2 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -2565,12 +2565,6 @@ void ufshpb_get_dev_info(struct ufs_hba *hba, u8 
*desc_buf)
u32 max_hpb_single_cmd = HPB_MULTI_CHUNK_LOW;
 
hpb_dev_info->control_mode = desc_buf[DEVICE_DESC_PARAM_HPB_CONTROL];
-   if (hpb_dev_info->control_mode == HPB_HOST_CONTROL) {
-   dev_err(hba->dev, "%s: host control mode is not supported.\n",
-   __func__);
-   hpb_dev_info->hpb_disabled = true;
-   return;
-   }
 
version = get_unaligned_be16(desc_buf + DEVICE_DESC_PARAM_HPB_VER);
if ((version != HPB_SUPPORT_VERSION) &&
-- 
2.25.1



[PATCH v6 06/10] scsi: ufshpb: Add hpb dev reset response

2021-03-22 Thread Avri Altman
The spec does not define what is the host's recommended response when
the device send hpb dev reset response (oper 0x2).

We will update all active hpb regions: mark them and do that on the next
read.

Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshpb.c | 32 +++-
 drivers/scsi/ufs/ufshpb.h |  1 +
 2 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 1f0344eaa546..6e580111293f 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -640,7 +640,8 @@ int ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb 
*lrbp)
if (rgn->reads == ACTIVATION_THRESHOLD)
activate = true;
spin_unlock(>rgn_lock);
-   if (activate) {
+   if (activate ||
+   test_and_clear_bit(RGN_FLAG_UPDATE, >rgn_flags)) {
spin_lock_irqsave(>rsp_list_lock, flags);
ufshpb_update_active_info(hpb, rgn_idx, srgn_idx);
spin_unlock_irqrestore(>rsp_list_lock, flags);
@@ -1402,6 +1403,20 @@ static void ufshpb_rsp_req_region_update(struct 
ufshpb_lu *hpb,
queue_work(ufshpb_wq, >map_work);
 }
 
+static void ufshpb_dev_reset_handler(struct ufshpb_lu *hpb)
+{
+   struct victim_select_info *lru_info = >lru_info;
+   struct ufshpb_region *rgn;
+   unsigned long flags;
+
+   spin_lock_irqsave(>rgn_state_lock, flags);
+
+   list_for_each_entry(rgn, _info->lh_lru_rgn, list_lru_rgn)
+   set_bit(RGN_FLAG_UPDATE, >rgn_flags);
+
+   spin_unlock_irqrestore(>rgn_state_lock, flags);
+}
+
 /*
  * This function will parse recommended active subregion information in sense
  * data field of response UPIU with SAM_STAT_GOOD state.
@@ -1476,6 +1491,18 @@ void ufshpb_rsp_upiu(struct ufs_hba *hba, struct 
ufshcd_lrb *lrbp)
case HPB_RSP_DEV_RESET:
dev_warn(>sdev_ufs_lu->sdev_dev,
 "UFS device lost HPB information during PM.\n");
+
+   if (hpb->is_hcm) {
+   struct scsi_device *sdev;
+
+   __shost_for_each_device(sdev, hba->host) {
+   struct ufshpb_lu *h = sdev->hostdata;
+
+   if (h)
+   ufshpb_dev_reset_handler(h);
+   }
+   }
+
break;
default:
dev_notice(>sdev_ufs_lu->sdev_dev,
@@ -1795,6 +1822,8 @@ static int ufshpb_alloc_region_tbl(struct ufs_hba *hba, 
struct ufshpb_lu *hpb)
} else {
rgn->rgn_state = HPB_RGN_INACTIVE;
}
+
+   rgn->rgn_flags = 0;
}
 
return 0;
@@ -2122,6 +2151,7 @@ static void ufshpb_cancel_jobs(struct ufshpb_lu *hpb)
 {
if (hpb->is_hcm)
cancel_work_sync(>ufshpb_normalization_work);
+
cancel_work_sync(>map_work);
 }
 
diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
index 7afc98e61c0a..24aa116c42c6 100644
--- a/drivers/scsi/ufs/ufshpb.h
+++ b/drivers/scsi/ufs/ufshpb.h
@@ -123,6 +123,7 @@ struct ufshpb_region {
struct list_head list_lru_rgn;
unsigned long rgn_flags;
 #define RGN_FLAG_DIRTY 0
+#define RGN_FLAG_UPDATE 1
 
/* region reads - for host mode */
spinlock_t rgn_lock;
-- 
2.25.1



[PATCH v6 07/10] scsi: ufshpb: Add "Cold" regions timer

2021-03-22 Thread Avri Altman
In order not to hang on to “cold” regions, we shall inactivate a
region that has no READ access for a predefined amount of time -
READ_TO_MS. For that purpose we shall monitor the active regions list,
polling it on every POLLING_INTERVAL_MS. On timeout expiry we shall add
the region to the "to-be-inactivated" list, unless it is clean and did
not exhaust its READ_TO_EXPIRIES - another parameter.

All this does not apply to pinned regions.

Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshpb.c | 74 +--
 drivers/scsi/ufs/ufshpb.h |  8 +
 2 files changed, 79 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 6e580111293f..4639d3b5ddc0 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -18,6 +18,9 @@
 
 #define ACTIVATION_THRESHOLD 8 /* 8 IOs */
 #define EVICTION_THRESHOLD (ACTIVATION_THRESHOLD << 5) /* 256 IOs */
+#define READ_TO_MS 1000
+#define READ_TO_EXPIRIES 100
+#define POLLING_INTERVAL_MS 200
 
 /* memory management */
 static struct kmem_cache *ufshpb_mctx_cache;
@@ -1021,12 +1024,63 @@ static int ufshpb_check_srgns_issue_state(struct 
ufshpb_lu *hpb,
return 0;
 }
 
+static void ufshpb_read_to_handler(struct work_struct *work)
+{
+   struct ufshpb_lu *hpb = container_of(work, struct ufshpb_lu,
+ufshpb_read_to_work.work);
+   struct victim_select_info *lru_info = >lru_info;
+   struct ufshpb_region *rgn, *next_rgn;
+   unsigned long flags;
+   LIST_HEAD(expired_list);
+
+   if (test_and_set_bit(TIMEOUT_WORK_RUNNING, >work_data_bits))
+   return;
+
+   spin_lock_irqsave(>rgn_state_lock, flags);
+
+   list_for_each_entry_safe(rgn, next_rgn, _info->lh_lru_rgn,
+list_lru_rgn) {
+   bool timedout = ktime_after(ktime_get(), rgn->read_timeout);
+
+   if (timedout) {
+   rgn->read_timeout_expiries--;
+   if (is_rgn_dirty(rgn) ||
+   rgn->read_timeout_expiries == 0)
+   list_add(>list_expired_rgn, _list);
+   else
+   rgn->read_timeout = ktime_add_ms(ktime_get(),
+READ_TO_MS);
+   }
+   }
+
+   spin_unlock_irqrestore(>rgn_state_lock, flags);
+
+   list_for_each_entry_safe(rgn, next_rgn, _list,
+list_expired_rgn) {
+   list_del_init(>list_expired_rgn);
+   spin_lock_irqsave(>rsp_list_lock, flags);
+   ufshpb_update_inactive_info(hpb, rgn->rgn_idx);
+   spin_unlock_irqrestore(>rsp_list_lock, flags);
+   }
+
+   ufshpb_kick_map_work(hpb);
+
+   clear_bit(TIMEOUT_WORK_RUNNING, >work_data_bits);
+
+   schedule_delayed_work(>ufshpb_read_to_work,
+ msecs_to_jiffies(POLLING_INTERVAL_MS));
+}
+
 static void ufshpb_add_lru_info(struct victim_select_info *lru_info,
struct ufshpb_region *rgn)
 {
rgn->rgn_state = HPB_RGN_ACTIVE;
list_add_tail(>list_lru_rgn, _info->lh_lru_rgn);
atomic_inc(_info->active_cnt);
+   if (rgn->hpb->is_hcm) {
+   rgn->read_timeout = ktime_add_ms(ktime_get(), READ_TO_MS);
+   rgn->read_timeout_expiries = READ_TO_EXPIRIES;
+   }
 }
 
 static void ufshpb_hit_lru_info(struct victim_select_info *lru_info,
@@ -1803,6 +1857,7 @@ static int ufshpb_alloc_region_tbl(struct ufs_hba *hba, 
struct ufshpb_lu *hpb)
 
INIT_LIST_HEAD(>list_inact_rgn);
INIT_LIST_HEAD(>list_lru_rgn);
+   INIT_LIST_HEAD(>list_expired_rgn);
 
if (rgn_idx == hpb->rgns_per_lu - 1) {
srgn_cnt = ((hpb->srgns_per_lu - 1) %
@@ -1824,6 +1879,7 @@ static int ufshpb_alloc_region_tbl(struct ufs_hba *hba, 
struct ufshpb_lu *hpb)
}
 
rgn->rgn_flags = 0;
+   rgn->hpb = hpb;
}
 
return 0;
@@ -2047,9 +2103,12 @@ static int ufshpb_lu_hpb_init(struct ufs_hba *hba, 
struct ufshpb_lu *hpb)
INIT_LIST_HEAD(>list_hpb_lu);
 
INIT_WORK(>map_work, ufshpb_map_work_handler);
-   if (hpb->is_hcm)
+   if (hpb->is_hcm) {
INIT_WORK(>ufshpb_normalization_work,
  ufshpb_normalization_work_handler);
+   INIT_DELAYED_WORK(>ufshpb_read_to_work,
+ ufshpb_read_to_handler);
+   }
 
hpb->map_req_cache = kmem_cache_create("ufshpb_req_cache",
  sizeof(struct ufshpb_req), 0, 0, NULL);
@@ -2083,6 +2142,10 @@ static int ufshpb_lu_hp

[PATCH v6 04/10] scsi: ufshpb: Make eviction depends on region's reads

2021-03-22 Thread Avri Altman
In host mode, eviction is considered an extreme measure.
verify that the entering region has enough reads, and the exiting
region has much less reads.

Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshpb.c | 18 +-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index a1519cbb4ce0..5e757220d66a 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -17,6 +17,7 @@
 #include "../sd.h"
 
 #define ACTIVATION_THRESHOLD 8 /* 8 IOs */
+#define EVICTION_THRESHOLD (ACTIVATION_THRESHOLD << 5) /* 256 IOs */
 
 /* memory management */
 static struct kmem_cache *ufshpb_mctx_cache;
@@ -1047,6 +1048,13 @@ static struct ufshpb_region 
*ufshpb_victim_lru_info(struct ufshpb_lu *hpb)
if (ufshpb_check_srgns_issue_state(hpb, rgn))
continue;
 
+   /*
+* in host control mode, verify that the exiting region
+* has less reads
+*/
+   if (hpb->is_hcm && rgn->reads > (EVICTION_THRESHOLD >> 1))
+   continue;
+
victim_rgn = rgn;
break;
}
@@ -1219,7 +1227,7 @@ static int ufshpb_issue_map_req(struct ufshpb_lu *hpb,
 
 static int ufshpb_add_region(struct ufshpb_lu *hpb, struct ufshpb_region *rgn)
 {
-   struct ufshpb_region *victim_rgn;
+   struct ufshpb_region *victim_rgn = NULL;
struct victim_select_info *lru_info = >lru_info;
unsigned long flags;
int ret = 0;
@@ -1246,7 +1254,15 @@ static int ufshpb_add_region(struct ufshpb_lu *hpb, 
struct ufshpb_region *rgn)
 * It is okay to evict the least recently used region,
 * because the device could detect this region
 * by not issuing HPB_READ
+*
+* in host control mode, verify that the entering
+* region has enough reads
 */
+   if (hpb->is_hcm && rgn->reads < EVICTION_THRESHOLD) {
+   ret = -EACCES;
+   goto out;
+   }
+
victim_rgn = ufshpb_victim_lru_info(hpb);
if (!victim_rgn) {
dev_warn(>sdev_ufs_lu->sdev_dev,
-- 
2.25.1



[PATCH v6 05/10] scsi: ufshpb: Region inactivation in host mode

2021-03-22 Thread Avri Altman
In host mode, the host is expected to send HPB-WRITE-BUFFER with
buffer-id = 0x1 when it inactivates a region.

Use the map-requests pool as there is no point in assigning a
designated cache for umap-requests.

Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshpb.c | 35 +++
 drivers/scsi/ufs/ufshpb.h |  1 +
 2 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 5e757220d66a..1f0344eaa546 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -904,6 +904,7 @@ static int ufshpb_execute_umap_req(struct ufshpb_lu *hpb,
 
blk_execute_rq_nowait(NULL, req, 1, ufshpb_umap_req_compl_fn);
 
+   hpb->stats.umap_req_cnt++;
return 0;
 }
 
@@ -1100,18 +1101,37 @@ static int ufshpb_issue_umap_req(struct ufshpb_lu *hpb,
return -EAGAIN;
 }
 
+static int ufshpb_issue_umap_single_req(struct ufshpb_lu *hpb,
+   struct ufshpb_region *rgn)
+{
+   return ufshpb_issue_umap_req(hpb, rgn, false);
+}
+
 static int ufshpb_issue_umap_all_req(struct ufshpb_lu *hpb)
 {
return ufshpb_issue_umap_req(hpb, NULL, false);
 }
 
-static void __ufshpb_evict_region(struct ufshpb_lu *hpb,
- struct ufshpb_region *rgn)
+static int __ufshpb_evict_region(struct ufshpb_lu *hpb,
+struct ufshpb_region *rgn)
 {
struct victim_select_info *lru_info;
struct ufshpb_subregion *srgn;
int srgn_idx;
 
+   lockdep_assert_held(>rgn_state_lock);
+
+   if (hpb->is_hcm) {
+   unsigned long flags;
+   int ret;
+
+   spin_unlock_irqrestore(>rgn_state_lock, flags);
+   ret = ufshpb_issue_umap_single_req(hpb, rgn);
+   spin_lock_irqsave(>rgn_state_lock, flags);
+   if (ret)
+   return ret;
+   }
+
lru_info = >lru_info;
 
dev_dbg(>sdev_ufs_lu->sdev_dev, "evict region %d\n", rgn->rgn_idx);
@@ -1120,6 +1140,8 @@ static void __ufshpb_evict_region(struct ufshpb_lu *hpb,
 
for_each_sub_region(rgn, srgn_idx, srgn)
ufshpb_purge_active_subregion(hpb, srgn);
+
+   return 0;
 }
 
 static int ufshpb_evict_region(struct ufshpb_lu *hpb, struct ufshpb_region 
*rgn)
@@ -1141,7 +1163,7 @@ static int ufshpb_evict_region(struct ufshpb_lu *hpb, 
struct ufshpb_region *rgn)
goto out;
}
 
-   __ufshpb_evict_region(hpb, rgn);
+   ret = __ufshpb_evict_region(hpb, rgn);
}
 out:
spin_unlock_irqrestore(>rgn_state_lock, flags);
@@ -1275,7 +1297,9 @@ static int ufshpb_add_region(struct ufshpb_lu *hpb, 
struct ufshpb_region *rgn)
"LRU full (%d), choose victim %d\n",
atomic_read(_info->active_cnt),
victim_rgn->rgn_idx);
-   __ufshpb_evict_region(hpb, victim_rgn);
+   ret = __ufshpb_evict_region(hpb, victim_rgn);
+   if (ret)
+   goto out;
}
 
/*
@@ -1839,6 +1863,7 @@ ufshpb_sysfs_attr_show_func(rb_noti_cnt);
 ufshpb_sysfs_attr_show_func(rb_active_cnt);
 ufshpb_sysfs_attr_show_func(rb_inactive_cnt);
 ufshpb_sysfs_attr_show_func(map_req_cnt);
+ufshpb_sysfs_attr_show_func(umap_req_cnt);
 
 static struct attribute *hpb_dev_stat_attrs[] = {
_attr_hit_cnt.attr,
@@ -1847,6 +1872,7 @@ static struct attribute *hpb_dev_stat_attrs[] = {
_attr_rb_active_cnt.attr,
_attr_rb_inactive_cnt.attr,
_attr_map_req_cnt.attr,
+   _attr_umap_req_cnt.attr,
NULL,
 };
 
@@ -1971,6 +1997,7 @@ static void ufshpb_stat_init(struct ufshpb_lu *hpb)
hpb->stats.rb_active_cnt = 0;
hpb->stats.rb_inactive_cnt = 0;
hpb->stats.map_req_cnt = 0;
+   hpb->stats.umap_req_cnt = 0;
 }
 
 static void ufshpb_param_init(struct ufshpb_lu *hpb)
diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
index 32d72c46c57a..7afc98e61c0a 100644
--- a/drivers/scsi/ufs/ufshpb.h
+++ b/drivers/scsi/ufs/ufshpb.h
@@ -187,6 +187,7 @@ struct ufshpb_stats {
u64 rb_inactive_cnt;
u64 map_req_cnt;
u64 pre_req_cnt;
+   u64 umap_req_cnt;
 };
 
 struct ufshpb_lu {
-- 
2.25.1



[PATCH v6 03/10] scsi: ufshpb: Add region's reads counter

2021-03-22 Thread Avri Altman
In host control mode, reads are the major source of activation trials.
Keep track of those reads counters, for both active as well inactive
regions.

We reset the read counter upon write - we are only interested in "clean"
reads.

Keep those counters normalized, as we are using those reads as a
comparative score, to make various decisions.
If during consecutive normalizations an active region has exhaust its
reads - inactivate it.

while at it, protect the {active,inactive}_count stats by adding them
into the applicable handler.

Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshpb.c | 100 +++---
 drivers/scsi/ufs/ufshpb.h |   5 ++
 2 files changed, 88 insertions(+), 17 deletions(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index d4f0bb6d8fa1..a1519cbb4ce0 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -16,6 +16,8 @@
 #include "ufshpb.h"
 #include "../sd.h"
 
+#define ACTIVATION_THRESHOLD 8 /* 8 IOs */
+
 /* memory management */
 static struct kmem_cache *ufshpb_mctx_cache;
 static mempool_t *ufshpb_mctx_pool;
@@ -546,6 +548,23 @@ static int ufshpb_issue_pre_req(struct ufshpb_lu *hpb, 
struct scsi_cmnd *cmd,
return ret;
 }
 
+static void ufshpb_update_active_info(struct ufshpb_lu *hpb, int rgn_idx,
+ int srgn_idx)
+{
+   struct ufshpb_region *rgn;
+   struct ufshpb_subregion *srgn;
+
+   rgn = hpb->rgn_tbl + rgn_idx;
+   srgn = rgn->srgn_tbl + srgn_idx;
+
+   list_del_init(>list_inact_rgn);
+
+   if (list_empty(>list_act_srgn))
+   list_add_tail(>list_act_srgn, >lh_act_srgn);
+
+   hpb->stats.rb_active_cnt++;
+}
+
 /*
  * This function will set up HPB read command using host-side L2P map data.
  */
@@ -596,12 +615,43 @@ int ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb 
*lrbp)
ufshpb_set_ppn_dirty(hpb, rgn_idx, srgn_idx, srgn_offset,
 transfer_len);
spin_unlock_irqrestore(>rgn_state_lock, flags);
+
+   if (hpb->is_hcm) {
+   spin_lock(>rgn_lock);
+   rgn->reads = 0;
+   spin_unlock(>rgn_lock);
+   }
+
return 0;
}
 
if (!ufshpb_is_support_chunk(hpb, transfer_len))
return 0;
 
+   if (hpb->is_hcm) {
+   bool activate = false;
+   /*
+* in host control mode, reads are the main source for
+* activation trials.
+*/
+   spin_lock(>rgn_lock);
+   rgn->reads++;
+   if (rgn->reads == ACTIVATION_THRESHOLD)
+   activate = true;
+   spin_unlock(>rgn_lock);
+   if (activate) {
+   spin_lock_irqsave(>rsp_list_lock, flags);
+   ufshpb_update_active_info(hpb, rgn_idx, srgn_idx);
+   spin_unlock_irqrestore(>rsp_list_lock, flags);
+   dev_dbg(>sdev_ufs_lu->sdev_dev,
+   "activate region %d-%d\n", rgn_idx, srgn_idx);
+   }
+
+   /* keep those counters normalized */
+   if (rgn->reads > hpb->entries_per_srgn)
+   schedule_work(>ufshpb_normalization_work);
+   }
+
spin_lock_irqsave(>rgn_state_lock, flags);
if (ufshpb_test_ppn_dirty(hpb, rgn_idx, srgn_idx, srgn_offset,
   transfer_len)) {
@@ -741,21 +791,6 @@ static int ufshpb_clear_dirty_bitmap(struct ufshpb_lu *hpb,
return 0;
 }
 
-static void ufshpb_update_active_info(struct ufshpb_lu *hpb, int rgn_idx,
- int srgn_idx)
-{
-   struct ufshpb_region *rgn;
-   struct ufshpb_subregion *srgn;
-
-   rgn = hpb->rgn_tbl + rgn_idx;
-   srgn = rgn->srgn_tbl + srgn_idx;
-
-   list_del_init(>list_inact_rgn);
-
-   if (list_empty(>list_act_srgn))
-   list_add_tail(>list_act_srgn, >lh_act_srgn);
-}
-
 static void ufshpb_update_inactive_info(struct ufshpb_lu *hpb, int rgn_idx)
 {
struct ufshpb_region *rgn;
@@ -769,6 +804,8 @@ static void ufshpb_update_inactive_info(struct ufshpb_lu 
*hpb, int rgn_idx)
 
if (list_empty(>list_inact_rgn))
list_add_tail(>list_inact_rgn, >lh_inact_rgn);
+
+   hpb->stats.rb_inactive_cnt++;
 }
 
 static void ufshpb_activate_subregion(struct ufshpb_lu *hpb,
@@ -1089,6 +1126,7 @@ static int ufshpb_evict_region(struct ufshpb_lu *hpb, 
struct ufshpb_region *rgn)
 rgn->rgn_idx);
goto out;
}
+
if (!list_empty(>list_lru_rgn)) {
if (ufshpb_check_srgns_issue_state(hpb, rgn)) {
r

[PATCH v6 02/10] scsi: ufshpb: Add host control mode support to rsp_upiu

2021-03-22 Thread Avri Altman
In device control mode, the device may recommend the host to either
activate or inactivate a region, and the host should follow. Meaning
those are not actually recommendations, but more of instructions.

On the contrary, in host control mode, the recommendation protocol is
slightly changed:
a) The device may only recommend the host to update a subregion of an
   already-active region. And,
b) The device may *not* recommend to inactivate a region.

Furthermore, in host control mode, the host may choose not to follow any
of the device's recommendations. However, in case of a recommendation to
update an active and clean subregion, it is better to follow those
recommendation because otherwise the host has no other way to know that
some internal relocation took place.

Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshpb.c | 34 +-
 drivers/scsi/ufs/ufshpb.h |  2 ++
 2 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index fb10afcbb49f..d4f0bb6d8fa1 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -166,6 +166,8 @@ static void ufshpb_set_ppn_dirty(struct ufshpb_lu *hpb, int 
rgn_idx,
else
set_bit_len = cnt;
 
+   set_bit(RGN_FLAG_DIRTY, >rgn_flags);
+
if (rgn->rgn_state != HPB_RGN_INACTIVE &&
srgn->srgn_state == HPB_SRGN_VALID)
bitmap_set(srgn->mctx->ppn_dirty, srgn_offset, set_bit_len);
@@ -235,6 +237,11 @@ static bool ufshpb_test_ppn_dirty(struct ufshpb_lu *hpb, 
int rgn_idx,
return false;
 }
 
+static inline bool is_rgn_dirty(struct ufshpb_region *rgn)
+{
+   return test_bit(RGN_FLAG_DIRTY, >rgn_flags);
+}
+
 static int ufshpb_fill_ppn_from_page(struct ufshpb_lu *hpb,
 struct ufshpb_map_ctx *mctx, int pos,
 int len, u64 *ppn_buf)
@@ -713,6 +720,7 @@ static void ufshpb_put_map_req(struct ufshpb_lu *hpb,
 static int ufshpb_clear_dirty_bitmap(struct ufshpb_lu *hpb,
 struct ufshpb_subregion *srgn)
 {
+   struct ufshpb_region *rgn;
u32 num_entries = hpb->entries_per_srgn;
 
if (!srgn->mctx) {
@@ -726,6 +734,10 @@ static int ufshpb_clear_dirty_bitmap(struct ufshpb_lu *hpb,
num_entries = hpb->last_srgn_entries;
 
bitmap_zero(srgn->mctx->ppn_dirty, num_entries);
+
+   rgn = hpb->rgn_tbl + srgn->rgn_idx;
+   clear_bit(RGN_FLAG_DIRTY, >rgn_flags);
+
return 0;
 }
 
@@ -1245,6 +1257,18 @@ static void ufshpb_rsp_req_region_update(struct 
ufshpb_lu *hpb,
srgn_i =
be16_to_cpu(rsp_field->hpb_active_field[i].active_srgn);
 
+   rgn = hpb->rgn_tbl + rgn_i;
+   if (hpb->is_hcm &&
+   (rgn->rgn_state != HPB_RGN_ACTIVE || is_rgn_dirty(rgn))) {
+   /*
+* in host control mode, subregion activation
+* recommendations are only allowed to active regions.
+* Also, ignore recommendations for dirty regions - the
+* host will make decisions concerning those by himself
+*/
+   continue;
+   }
+
dev_dbg(>sdev_ufs_lu->sdev_dev,
"activate(%d) region %d - %d\n", i, rgn_i, srgn_i);
 
@@ -1252,7 +1276,6 @@ static void ufshpb_rsp_req_region_update(struct ufshpb_lu 
*hpb,
ufshpb_update_active_info(hpb, rgn_i, srgn_i);
spin_unlock(>rsp_list_lock);
 
-   rgn = hpb->rgn_tbl + rgn_i;
srgn = rgn->srgn_tbl + srgn_i;
 
/* blocking HPB_READ */
@@ -1263,6 +1286,14 @@ static void ufshpb_rsp_req_region_update(struct 
ufshpb_lu *hpb,
hpb->stats.rb_active_cnt++;
}
 
+   if (hpb->is_hcm) {
+   /*
+* in host control mode the device is not allowed to inactivate
+* regions
+*/
+   goto out;
+   }
+
for (i = 0; i < rsp_field->inactive_rgn_cnt; i++) {
rgn_i = be16_to_cpu(rsp_field->hpb_inactive_field[i]);
dev_dbg(>sdev_ufs_lu->sdev_dev,
@@ -1287,6 +1318,7 @@ static void ufshpb_rsp_req_region_update(struct ufshpb_lu 
*hpb,
hpb->stats.rb_inactive_cnt++;
}
 
+out:
dev_dbg(>sdev_ufs_lu->sdev_dev, "Noti: #ACT %u #INACT %u\n",
rsp_field->active_rgn_cnt, rsp_field->inactive_rgn_cnt);
 
diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
index 7df30340386a..032672114881 100644
--- a/drivers/scsi/ufs/ufshpb.h
+++ b/drivers/scsi/ufs/ufshpb.h
@@ -121,6 +121,8 @@ struct ufshpb_region {
 
/* below i

[PATCH v6 01/10] scsi: ufshpb: Cache HPB Control mode on init

2021-03-22 Thread Avri Altman
We will use it later, when we'll need to differentiate between device
and host control modes.

Signed-off-by: Avri Altman 
---
 drivers/scsi/ufs/ufshcd.h | 2 ++
 drivers/scsi/ufs/ufshpb.c | 8 +---
 drivers/scsi/ufs/ufshpb.h | 2 ++
 3 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 8aca8f327981..a578927d9c59 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -656,6 +656,7 @@ struct ufs_hba_variant_params {
  * @hpb_disabled: flag to check if HPB is disabled
  * @max_hpb_single_cmd: maximum size of single HPB command
  * @is_legacy: flag to check HPB 1.0
+ * @control_mode: either host or device
  */
 struct ufshpb_dev_info {
int num_lu;
@@ -665,6 +666,7 @@ struct ufshpb_dev_info {
bool hpb_disabled;
int max_hpb_single_cmd;
bool is_legacy;
+   u8 control_mode;
 };
 #endif
 
diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 3ac8b0a9e8d3..fb10afcbb49f 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -1616,6 +1616,9 @@ static void ufshpb_lu_parameter_init(struct ufs_hba *hba,
 % (hpb->srgn_mem_size / HPB_ENTRY_SIZE);
 
hpb->pages_per_srgn = DIV_ROUND_UP(hpb->srgn_mem_size, PAGE_SIZE);
+
+   if (hpb_dev_info->control_mode == HPB_HOST_CONTROL)
+   hpb->is_hcm = true;
 }
 
 static int ufshpb_alloc_region_tbl(struct ufs_hba *hba, struct ufshpb_lu *hpb)
@@ -2309,11 +2312,10 @@ void ufshpb_get_dev_info(struct ufs_hba *hba, u8 
*desc_buf)
 {
struct ufshpb_dev_info *hpb_dev_info = >ufshpb_dev;
int version, ret;
-   u8 hpb_mode;
u32 max_hpb_single_cmd = HPB_MULTI_CHUNK_LOW;
 
-   hpb_mode = desc_buf[DEVICE_DESC_PARAM_HPB_CONTROL];
-   if (hpb_mode == HPB_HOST_CONTROL) {
+   hpb_dev_info->control_mode = desc_buf[DEVICE_DESC_PARAM_HPB_CONTROL];
+   if (hpb_dev_info->control_mode == HPB_HOST_CONTROL) {
dev_err(hba->dev, "%s: host control mode is not supported.\n",
__func__);
hpb_dev_info->hpb_disabled = true;
diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
index b1128b0ce486..7df30340386a 100644
--- a/drivers/scsi/ufs/ufshpb.h
+++ b/drivers/scsi/ufs/ufshpb.h
@@ -228,6 +228,8 @@ struct ufshpb_lu {
u32 entries_per_srgn_shift;
u32 pages_per_srgn;
 
+   bool is_hcm;
+
struct ufshpb_stats stats;
struct ufshpb_params params;
 
-- 
2.25.1



[PATCH v6 00/10] Add Host control mode to HPB

2021-03-22 Thread Avri Altman
v5 -> v6:
 - attend CanG's comments
 - rebase on Daejun's v31

v4 -> v5:
 - attend Daejun's comments
 - Control the number of inflight map requests

v3 -> v4:
 - rebase on Daejun's v25

v2 -> v3:
 - Attend Greg's and Can's comments
 - rebase on Daejun's v21

v1 -> v2:
 - attend Greg's and Daejun's comments
 - add patch 9 making host mode parameters configurable
 - rebase on Daejun's v19


The HPB spec defines 2 control modes - device control mode and host
control mode. In oppose to device control mode, in which the host obey
to whatever recommendation received from the device - In host control
mode, the host uses its own algorithms to decide which regions should
be activated or inactivated.

We kept the host managed heuristic simple and concise.

Aside from adding a by-spec functionality, host control mode entails
some further potential benefits: makes the hpb logic transparent and
readable, while allow tuning / scaling its various parameters, and
utilize system-wide info to optimize HPB potential.

This series is based on Samsung's V31 device-control HPB2.0 driver, see
msg-id: CGME20210322064159epcms2p6a4c7deed5f81eaa4f2a8340aaedb446c@epcms2p6
in lore.kernel.org.

This version was tested on Galaxy S20, and Xiaomi Mi10 pro.
Your meticulous review and testing is mostly welcome and appreciated.

Thanks,
Avri

Avri Altman (10):
  scsi: ufshpb: Cache HPB Control mode on init
  scsi: ufshpb: Add host control mode support to rsp_upiu
  scsi: ufshpb: Add region's reads counter
  scsi: ufshpb: Make eviction depends on region's reads
  scsi: ufshpb: Region inactivation in host mode
  scsi: ufshpb: Add hpb dev reset response
  scsi: ufshpb: Add "Cold" regions timer
  scsi: ufshpb: Limit the number of inflight map requests
  scsi: ufshpb: Add support for host control mode
  scsi: ufshpb: Make host mode parameters configurable

 Documentation/ABI/testing/sysfs-driver-ufs |  84 +++-
 drivers/scsi/ufs/ufshcd.h  |   2 +
 drivers/scsi/ufs/ufshpb.c  | 547 -
 drivers/scsi/ufs/ufshpb.h  |  39 ++
 4 files changed, 635 insertions(+), 37 deletions(-)

-- 
2.25.1



RE: [PATCH v29 4/4] scsi: ufs: Add HPB 2.0 support

2021-03-21 Thread Avri Altman

> +static int ufshpb_execute_umap_req(struct ufshpb_lu *hpb,
> +  struct ufshpb_req *umap_req,
> +  struct ufshpb_region *rgn)
> +{
> +   struct request *req;
> +   struct scsi_request *rq;
> +
> +   req = umap_req->req;
> +   req->timeout = 0;
> +   req->end_io_data = (void *)umap_req;
> +   rq = scsi_req(req);
> +   ufshpb_set_unmap_cmd(rq->cmd, rgn);
> +   rq->cmd_len = HPB_WRITE_BUFFER_CMD_LENGTH;
> +
> +   blk_execute_rq_nowait(NULL, req, 1, ufshpb_umap_req_compl_fn);
Typo? Forgot the struct request_queue *q?

> +
> +   return 0;
> +}
> +
>  static int ufshpb_execute_map_req(struct ufshpb_lu *hpb,
>   struct ufshpb_req *map_req, bool last)
>  {
> @@ -533,12 +878,12 @@ static int ufshpb_execute_map_req(struct ufshpb_lu
> *hpb,
> 
> q = hpb->sdev_ufs_lu->request_queue;
> for (i = 0; i < hpb->pages_per_srgn; i++) {
> -   ret = bio_add_pc_page(q, map_req->bio, 
> map_req->mctx->m_page[i],
> +   ret = bio_add_pc_page(q, map_req->bio, map_req->rb.mctx-
> >m_page[i],
>   PAGE_SIZE, 0);
> if (ret != PAGE_SIZE) {
> dev_err(>sdev_ufs_lu->sdev_dev,
>"bio_add_pc_page fail %d - %d\n",
> -  map_req->rgn_idx, map_req->srgn_idx);
> +  map_req->rb.rgn_idx, map_req->rb.srgn_idx);
> return ret;
> }
> }
> @@ -554,8 +899,8 @@ static int ufshpb_execute_map_req(struct ufshpb_lu
> *hpb,
> if (unlikely(last))
> mem_size = hpb->last_srgn_entries * HPB_ENTRY_SIZE;
> 
> -   ufshpb_set_read_buf_cmd(rq->cmd, map_req->rgn_idx,
> -   map_req->srgn_idx, mem_size);
> +   ufshpb_set_read_buf_cmd(rq->cmd, map_req->rb.rgn_idx,
> +   map_req->rb.srgn_idx, mem_size);
> rq->cmd_len = HPB_READ_BUFFER_CMD_LENGTH;
> 
> blk_execute_rq_nowait(NULL, req, 1, ufshpb_map_req_compl_fn);
Ditto


Thanks,
Avri


RE: [PATCH v2] scsi: ufs: Tidy up WB configuration code

2021-03-18 Thread Avri Altman


> 
> -int ufshcd_wb_ctrl(struct ufs_hba *hba, bool enable)
> +static int __ufshcd_wb_toggle(struct ufs_hba *hba, bool set, enum flag_idn
> idn)
>  {
> -   int ret;
> u8 index;
> enum query_opcode opcode;
What I meant is:
enum query_opcode opcode = set ? UPIU_QUERY_OPCODE_SET_FLAG : 
   UPIU_QUERY_OPCODE_CLEAR_FLAG;

Either way, you can add my Reviewed-by tag.

Thanks,
Avri

> 
> +   if (set)
> +   opcode = UPIU_QUERY_OPCODE_SET_FLAG;
> +   else
> +   opcode = UPIU_QUERY_OPCODE_CLEAR_FLAG;
> +
> +   index = ufshcd_wb_get_query_index(hba);
> +   return ufshcd_query_flag_retry(hba, opcode, idn, index, NULL);
> +}
> +
> +int ufshcd_wb_toggle(struct ufs_hba *hba, bool enable)
> +{
> +   int ret;
> +
> if (!ufshcd_is_wb_allowed(hba))
> return 0;
> 
> if (!(enable ^ hba->dev_info.wb_enabled))
> return 0;
> -   if (enable)
> -   opcode = UPIU_QUERY_OPCODE_SET_FLAG;
> -   else
> -   opcode = UPIU_QUERY_OPCODE_CLEAR_FLAG;
> 
> -   index = ufshcd_wb_get_query_index(hba);
> -   ret = ufshcd_query_flag_retry(hba, opcode,
> - QUERY_FLAG_IDN_WB_EN, index, NULL);
> +   ret = __ufshcd_wb_toggle(hba, enable, QUERY_FLAG_IDN_WB_EN);
> if (ret) {
> -   dev_err(hba->dev, "%s write booster %s failed %d\n",
> +   dev_err(hba->dev, "%s Write Booster %s failed %d\n",
> __func__, enable ? "enable" : "disable", ret);
> return ret;
> }
> 
> hba->dev_info.wb_enabled = enable;
> -   dev_dbg(hba->dev, "%s write booster %s %d\n",
> -   __func__, enable ? "enable" : "disable", ret);
> +   dev_info(hba->dev, "%s Write Booster %s\n",
> +   __func__, enable ? "enabled" : "disabled");
> 
> return ret;
>  }
> 
> -static int ufshcd_wb_toggle_flush_during_h8(struct ufs_hba *hba, bool set)
> +static void ufshcd_wb_toggle_flush_during_h8(struct ufs_hba *hba, bool set)
>  {
> -   int val;
> -   u8 index;
> -
> -   if (set)
> -   val =  UPIU_QUERY_OPCODE_SET_FLAG;
> -   else
> -   val = UPIU_QUERY_OPCODE_CLEAR_FLAG;
> +   int ret;
> 
> -   index = ufshcd_wb_get_query_index(hba);
> -   return ufshcd_query_flag_retry(hba, val,
> -   QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8,
> -   index, NULL);
> +   ret = __ufshcd_wb_toggle(hba, set,
> +   QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8);
> +   if (ret) {
> +   dev_err(hba->dev, "%s: WB-Buf Flush during H8 %s failed: 
> %d\n",
> +   __func__, set ? "enable" : "disable", ret);
> +   return;
> +   }
> +   dev_dbg(hba->dev, "%s WB-Buf Flush during H8 %s\n",
> +   __func__, set ? "enabled" : "disabled");
>  }
> 
> -static inline int ufshcd_wb_toggle_flush(struct ufs_hba *hba, bool enable)
> +static inline void ufshcd_wb_toggle_flush(struct ufs_hba *hba, bool enable)
>  {
> int ret;
> -   u8 index;
> -   enum query_opcode opcode;
> 
> if (!ufshcd_is_wb_allowed(hba) ||
> hba->dev_info.wb_buf_flush_enabled == enable)
> -   return 0;
> -
> -   if (enable)
> -   opcode = UPIU_QUERY_OPCODE_SET_FLAG;
> -   else
> -   opcode = UPIU_QUERY_OPCODE_CLEAR_FLAG;
> +   return;
> 
> -   index = ufshcd_wb_get_query_index(hba);
> -   ret = ufshcd_query_flag_retry(hba, opcode,
> - QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN, index,
> - NULL);
> +   ret = __ufshcd_wb_toggle(hba, enable,
> QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN);
> if (ret) {
> dev_err(hba->dev, "%s WB-Buf Flush %s failed %d\n", __func__,
> enable ? "enable" : "disable", ret);
> -   goto out;
> +   return;
> }
> 
> hba->dev_info.wb_buf_flush_enabled = enable;
> 
> -   dev_dbg(hba->dev, "WB-Buf Flush %s\n", enable ? "enabled" : 
> "disabled");
> -out:
> -   return ret;
> -
> +   dev_dbg(hba->dev, "%s WB-Buf Flush %s\n",
> +   __func__, enable ? "enabled" : "disabled");
>  }
> 
>  static bool ufshcd_wb_presrv_usrspc_keep_vcc_on(struct ufs_hba *hba,
> diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
> index 18e56c1..eddc819 100644
> --- a/drivers/scsi/ufs/ufshcd.h
> +++ b/drivers/scsi/ufs/ufshcd.h
> @@ -1099,7 +1099,7 @@ int ufshcd_exec_raw_upiu_cmd(struct ufs_hba
> *hba,
>  u8 *desc_buff, int *buff_len,
>  enum query_opcode desc_op);
> 
> -int ufshcd_wb_ctrl(struct ufs_hba *hba, bool enable);
> +int ufshcd_wb_toggle(struct ufs_hba 

RE: [PATCH v5 06/10] scsi: ufshpb: Add hpb dev reset response

2021-03-18 Thread Avri Altman
> On 2021-03-17 23:46, Avri Altman wrote:
> >> >> >> >>
> >> >> >> >> Just curious, directly doing below things inside ufshpb_rsp_upiu()
> >> >> >> >> does
> >> >> >> >> not
> >> >> >> >> seem a problem to me, does this really deserve a separate work?
> >> >> >> > I don't know, I never even consider of doing this.
> >> >> >> > The active region list may contain up to few thousands of regions -
> >> >> >> > It is not rare to see configurations that covers the entire device.
> >> >> >> >
> >> >> >>
> >> >> >> Yes, true, it can be a huge list. But what does the ops
> >> >> >> "HPB_RSP_DEV_RESET"
> >> >> >> really mean? The specs says "Device reset HPB Regions information",
> >> >> >> but
> >> >> >> I
> >> >> >> don't know what is really happening. Could you please elaborate?
> >> >> > It means that the device informs the host that the L2P cache is no
> >> >> > longer valid.
> >> >> > The spec doesn't say what to do in that case.
> >> >>
> >> >> Then it means that all the clean (without DIRTY flag set) HPB entries
> >> >> (ppns)
> >> >> in active rgns in host memory side may not be valid to the device
> >> >> anymore.
> >> >> Please correct me if I am wrong.
> >> >>
> >> >> > We thought that in host mode, it make sense to update all the active
> >> >> > regions.
> >> >>
> >> >> But current logic does not set the state of the sub-regions (in active
> >> >> regions) to
> >> >> INVALID, it only marks all active regions as UPDATE.
> >> >>
> >> >> Although one of subsequent read cmds shall put the sub-region back to
> >> >> activate_list,
> >> >> ufshpb_test_ppn_dirty() can still return false, thus these read cmds
> >> >> still think the
> >> >> ppns are valid and they shall move forward to send HPB Write Buffer
> >> >> (buffer id = 0x2,
> >> >> in case of HPB2.0) and HPB Read cmds.
> >> >>
> >> >> HPB Read cmds with invalid ppns will be treated as normal Read cmds by
> >> >> device as the
> >> >> specs says, but what would happen to HPB Write Buffer cmds (buffer id
> >> >> =
> >> >> 0x2, in case
> >> >> of HPB2.0) with invalid ppns? Can this be a real problem?
> >> > No need to control the ppn dirty / invalid state for this case.
> >> > The device send device reset so it is aware that all the L2P cache is
> >> > invalid.
> >> > Any HPB_READ is treated like normal READ10.
> >> >
> >> > Only once HPB-READ-BUFFER is completed,
> >> > the device will relate back to the physical address.
> >>
> >> What about HPB-WRITE-BUFFER (buffer id = 0x2) cmds?
> > Same.
> > Oper 0x2 is a relative simple case.
> > The device is expected to manage some versioning framework not to be
> > "fooled" by erroneous ppn.
> > There are some more challenging races that the device should meet.
> >
> 
> But I don't find the handling w.r.t this scenario on HPB2.0 specs -
> how would the device re-act/respond to HPB-WRITE-BUFFER cmds with
> invalid HPB entries? Could you please point me to relevant
> section/paragraph?
The spec does not handle that.
HPB-WRITE-BUFFER 0x2 is not a stand-alone command, it always tagged to a 
HPB-READ command.
It is up to the device to handle invalid ppn and always return the correct data.
The expected performance in that case is like a regular READ10.

Thanks,
Avri 

> 
> Thanks,
> Can Guo.
> 
> > Thanks,
> > Avri
> >>
> >> Thanks,
> >> Can Guo.
> >>
> >> >
> >> >>
> >> >> >
> >> >> > I think I will go with your suggestion.
> >> >> > Effectively, in host mode, since it is deactivating "cold" regions,
> >> >> > the lru list is kept relatively small, and contains only "hot" 
> >> >> > regions.
> >> >>
> >> >> hmm... I don't really have a idea on this, please go with whatever you
> >> >> and Daejun think is fine here.
> >> &

RE: [PATCH v5 06/10] scsi: ufshpb: Add hpb dev reset response

2021-03-17 Thread Avri Altman
> >> >> >>
> >> >> >> Just curious, directly doing below things inside ufshpb_rsp_upiu()
> >> >> >> does
> >> >> >> not
> >> >> >> seem a problem to me, does this really deserve a separate work?
> >> >> > I don't know, I never even consider of doing this.
> >> >> > The active region list may contain up to few thousands of regions -
> >> >> > It is not rare to see configurations that covers the entire device.
> >> >> >
> >> >>
> >> >> Yes, true, it can be a huge list. But what does the ops
> >> >> "HPB_RSP_DEV_RESET"
> >> >> really mean? The specs says "Device reset HPB Regions information",
> >> >> but
> >> >> I
> >> >> don't know what is really happening. Could you please elaborate?
> >> > It means that the device informs the host that the L2P cache is no
> >> > longer valid.
> >> > The spec doesn't say what to do in that case.
> >>
> >> Then it means that all the clean (without DIRTY flag set) HPB entries
> >> (ppns)
> >> in active rgns in host memory side may not be valid to the device
> >> anymore.
> >> Please correct me if I am wrong.
> >>
> >> > We thought that in host mode, it make sense to update all the active
> >> > regions.
> >>
> >> But current logic does not set the state of the sub-regions (in active
> >> regions) to
> >> INVALID, it only marks all active regions as UPDATE.
> >>
> >> Although one of subsequent read cmds shall put the sub-region back to
> >> activate_list,
> >> ufshpb_test_ppn_dirty() can still return false, thus these read cmds
> >> still think the
> >> ppns are valid and they shall move forward to send HPB Write Buffer
> >> (buffer id = 0x2,
> >> in case of HPB2.0) and HPB Read cmds.
> >>
> >> HPB Read cmds with invalid ppns will be treated as normal Read cmds by
> >> device as the
> >> specs says, but what would happen to HPB Write Buffer cmds (buffer id
> >> =
> >> 0x2, in case
> >> of HPB2.0) with invalid ppns? Can this be a real problem?
> > No need to control the ppn dirty / invalid state for this case.
> > The device send device reset so it is aware that all the L2P cache is
> > invalid.
> > Any HPB_READ is treated like normal READ10.
> >
> > Only once HPB-READ-BUFFER is completed,
> > the device will relate back to the physical address.
> 
> What about HPB-WRITE-BUFFER (buffer id = 0x2) cmds?
Same.
Oper 0x2 is a relative simple case.
The device is expected to manage some versioning framework not to be "fooled" 
by erroneous ppn.
There are some more challenging races that the device should meet.

Thanks,
Avri
> 
> Thanks,
> Can Guo.
> 
> >
> >>
> >> >
> >> > I think I will go with your suggestion.
> >> > Effectively, in host mode, since it is deactivating "cold" regions,
> >> > the lru list is kept relatively small, and contains only "hot" regions.
> >>
> >> hmm... I don't really have a idea on this, please go with whatever you
> >> and Daejun think is fine here.
> > I will take your advice and remove the worker.
> >
> >
> > Thanks,
> > Avri
> >
> >>
> >> Thanks,
> >> Can Guo.
> >>
> >> >
> >> > Thanks,
> >> > Avri
> >> >
> >> >>
> >> >> Thanks,
> >> >> Can Guo.
> >> >>
> >> >> > But yes, I can do that.
> >> >> > Better to get ack from Daejun first.
> >> >> >
> >> >> > Thanks,
> >> >> > Avri
> >> >> >
> >> >> >>
> >> >> >> Thanks,
> >> >> >> Can Guo.
> >> >> >>
> >> >> >> > +{
> >> >> >> > + struct ufshpb_lu *hpb;
> >> >> >> > + struct victim_select_info *lru_info;
> >> >> >> > + struct ufshpb_region *rgn;
> >> >> >> > + unsigned long flags;
> >> >> >> > +
> >> >> >> > + hpb = container_of(work, struct ufshpb_lu,
> >> ufshpb_lun_reset_work);
> >> >> >> > +
> >> >> >> > + lru_info = >lru_info;
> >> >> >> > +
> >> >> >> > + spin_lock_irqsave(>rgn_state_lock, flags);
> >> >> >> > +
> >> >> >> > + list_for_each_entry(rgn, _info->lh_lru_rgn, list_lru_rgn)
> >> >> >> > + set_bit(RGN_FLAG_UPDATE, >rgn_flags);
> >> >> >> > +
> >> >> >> > + spin_unlock_irqrestore(>rgn_state_lock, flags);
> >> >> >> > +}
> >> >> >> > +
> >> >> >> >  static void ufshpb_normalization_work_handler(struct work_struct
> >> >> >> > *work)
> >> >> >> >  {
> >> >> >> >   struct ufshpb_lu *hpb;
> >> >> >> > @@ -1798,6 +1832,8 @@ static int ufshpb_alloc_region_tbl(struct
> >> >> >> > ufs_hba *hba, struct ufshpb_lu *hpb)
> >> >> >> >   } else {
> >> >> >> >   rgn->rgn_state = HPB_RGN_INACTIVE;
> >> >> >> >   }
> >> >> >> > +
> >> >> >> > + rgn->rgn_flags = 0;
> >> >> >> >   }
> >> >> >> >
> >> >> >> >   return 0;
> >> >> >> > @@ -2012,9 +2048,12 @@ static int ufshpb_lu_hpb_init(struct
> >> ufs_hba
> >> >> >> > *hba, struct ufshpb_lu *hpb)
> >> >> >> >   INIT_LIST_HEAD(>list_hpb_lu);
> >> >> >> >
> >> >> >> >   INIT_WORK(>map_work, ufshpb_map_work_handler);
> >> >> >> > - if (hpb->is_hcm)
> >> >> >> > + if (hpb->is_hcm) {
> >> >> >> >   INIT_WORK(>ufshpb_normalization_work,
> >> >> >> > ufshpb_normalization_work_handler);
> >> >> >> > +  

RE: [PATCH v5 06/10] scsi: ufshpb: Add hpb dev reset response

2021-03-17 Thread Avri Altman
 
> 
> On 2021-03-17 20:22, Avri Altman wrote:
> >>
> >> On 2021-03-17 19:23, Avri Altman wrote:
> >> >>
> >> >> On 2021-03-02 21:24, Avri Altman wrote:
> >> >> > The spec does not define what is the host's recommended response
> when
> >> >> > the device send hpb dev reset response (oper 0x2).
> >> >> >
> >> >> > We will update all active hpb regions: mark them and do that on the
> >> >> > next
> >> >> > read.
> >> >> >
> >> >> > Signed-off-by: Avri Altman 
> >> >> > ---
> >> >> >  drivers/scsi/ufs/ufshpb.c | 47
> >> >> ---
> >> >> >  drivers/scsi/ufs/ufshpb.h |  2 ++
> >> >> >  2 files changed, 46 insertions(+), 3 deletions(-)
> >> >> >
> >> >> > diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
> >> >> > index 0744feb4d484..0034fa03fdc6 100644
> >> >> > --- a/drivers/scsi/ufs/ufshpb.c
> >> >> > +++ b/drivers/scsi/ufs/ufshpb.c
> >> >> > @@ -642,7 +642,8 @@ int ufshpb_prep(struct ufs_hba *hba, struct
> >> >> > ufshcd_lrb *lrbp)
> >> >> >   if (rgn->reads == ACTIVATION_THRESHOLD)
> >> >> >   activate = true;
> >> >> >   spin_unlock_irqrestore(>rgn_lock, flags);
> >> >> > - if (activate) {
> >> >> > + if (activate ||
> >> >> > + test_and_clear_bit(RGN_FLAG_UPDATE, 
> >> >> > >rgn_flags)) {
> 
> Other than this place, do we also need to clear this bit in places like
> ufshpb_map_req_compl_fn() and/or ufshpb_cleanup_lru_info()? Otherwise,
> this flag may be left there even after the rgn is inactivated.
I don't think so - may cause a race if device reset arrives when map request 
just finished.
Better to be in one place.

> 
> >> >> >   spin_lock_irqsave(>rsp_list_lock, flags);
> >> >> >   ufshpb_update_active_info(hpb, rgn_idx, 
> >> >> > srgn_idx);
> >> >> >   hpb->stats.rb_active_cnt++;
> >> >> > @@ -1480,6 +1481,20 @@ void ufshpb_rsp_upiu(struct ufs_hba
> *hba,
> >> >> > struct ufshcd_lrb *lrbp)
> >> >> >   case HPB_RSP_DEV_RESET:
> >> >> >   dev_warn(>sdev_ufs_lu->sdev_dev,
> >> >> >"UFS device lost HPB information during 
> >> >> > PM.\n");
> >> >> > +
> >> >> > + if (hpb->is_hcm) {
> >> >> > + struct scsi_device *sdev;
> >> >> > +
> >> >> > + __shost_for_each_device(sdev, hba->host) {
> >> >> > + struct ufshpb_lu *h = sdev->hostdata;
> >> >> > +
> >> >> > + if (!h)
> >> >> > + continue;
> >> >> > +
> >> >> > + 
> >> >> > schedule_work(>ufshpb_lun_reset_work);
> >> >> > + }
> >> >> > + }
> >> >> > +
> >> >> >   break;
> >> >> >   default:
> >> >> >   dev_notice(>sdev_ufs_lu->sdev_dev,
> >> >> > @@ -1594,6 +1609,25 @@ static void
> >> >> > ufshpb_run_inactive_region_list(struct ufshpb_lu *hpb)
> >> >> >   spin_unlock_irqrestore(>rsp_list_lock, flags);
> >> >> >  }
> >> >> >
> >> >> > +static void ufshpb_reset_work_handler(struct work_struct *work)
> >> >>
> >> >> Just curious, directly doing below things inside ufshpb_rsp_upiu()
> >> >> does
> >> >> not
> >> >> seem a problem to me, does this really deserve a separate work?
> >> > I don't know, I never even consider of doing this.
> >> > The active region list may contain up to few thousands of regions -
> >> > It is not rare to see configurations that covers the entire device.
> >> >
> >>
> >> Yes, true, it can be a huge list. But what does 

RE: [PATCH] scsi: ufs: Tidy up WB configuration code

2021-03-17 Thread Avri Altman
> 
> From: Yue Hu 
> 
> There are similar code implemetentions for WB configurations in
> ufshcd_wb_{ctrl, toggle_flush_during_h8, toggle_flush}. We can
> extract the part to create a new helper with a flag parameter to
> reduce code duplication.
> 
> Meanwhile, change ufshcd_wb_ctrl() -> ufshcd_wb_toggle() for better
> readability.
> 
> And remove unnecessary log messages from ufshcd_wb_config() since
> relevant toggle function will spew log respectively. Also change
> ufshcd_wb_toggle_flush{__during_h8} to void type accordingly.
> 
> Signed-off-by: Yue Hu 
A small nit below, otherwise - looks good to me.
Reviewed-by: Avri Altman 

> ---
>  drivers/scsi/ufs/ufs-sysfs.c |  2 +-
>  drivers/scsi/ufs/ufshcd.c| 99 
> +++-
>  drivers/scsi/ufs/ufshcd.h|  2 +-
>  3 files changed, 44 insertions(+), 59 deletions(-)
> 
> diff --git a/drivers/scsi/ufs/ufs-sysfs.c b/drivers/scsi/ufs/ufs-sysfs.c
> index acc54f5..d7c3cff 100644
> --- a/drivers/scsi/ufs/ufs-sysfs.c
> +++ b/drivers/scsi/ufs/ufs-sysfs.c
> @@ -246,7 +246,7 @@ static ssize_t wb_on_store(struct device *dev, struct
> device_attribute *attr,
> }
> 
> pm_runtime_get_sync(hba->dev);
> -   res = ufshcd_wb_ctrl(hba, wb_enable);
> +   res = ufshcd_wb_toggle(hba, wb_enable);
> pm_runtime_put_sync(hba->dev);
>  out:
> up(>host_sem);
> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
> index 7716175..1368f9e 100644
> --- a/drivers/scsi/ufs/ufshcd.c
> +++ b/drivers/scsi/ufs/ufshcd.c
> @@ -247,8 +247,8 @@ static int ufshcd_change_power_mode(struct ufs_hba
> *hba,
>  static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba,
>  struct ufs_vreg *vreg);
>  static int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag);
> -static int ufshcd_wb_toggle_flush_during_h8(struct ufs_hba *hba, bool set);
> -static inline int ufshcd_wb_toggle_flush(struct ufs_hba *hba, bool enable);
> +static void ufshcd_wb_toggle_flush_during_h8(struct ufs_hba *hba, bool set);
> +static inline void ufshcd_wb_toggle_flush(struct ufs_hba *hba, bool enable);
>  static void ufshcd_hba_vreg_set_lpm(struct ufs_hba *hba);
>  static void ufshcd_hba_vreg_set_hpm(struct ufs_hba *hba);
> 
> @@ -275,20 +275,12 @@ static inline void ufshcd_disable_irq(struct ufs_hba
> *hba)
> 
>  static inline void ufshcd_wb_config(struct ufs_hba *hba)
>  {
> -   int ret;
> -
> if (!ufshcd_is_wb_allowed(hba))
> return;
> 
> -   ret = ufshcd_wb_ctrl(hba, true);
> -   if (ret)
> -   dev_err(hba->dev, "%s: Enable WB failed: %d\n", __func__, 
> ret);
> -   else
> -   dev_info(hba->dev, "%s: Write Booster Configured\n", 
> __func__);
> -   ret = ufshcd_wb_toggle_flush_during_h8(hba, true);
> -   if (ret)
> -   dev_err(hba->dev, "%s: En WB flush during H8: failed: %d\n",
> -   __func__, ret);
> +   ufshcd_wb_toggle(hba, true);
> +
> +   ufshcd_wb_toggle_flush_during_h8(hba, true);
> if (!(hba->quirks & UFSHCI_QUIRK_SKIP_MANUAL_WB_FLUSH_CTRL))
> ufshcd_wb_toggle_flush(hba, true);
>  }
> @@ -1268,7 +1260,7 @@ static int ufshcd_devfreq_scale(struct ufs_hba *hba,
> bool scale_up)
> /* Enable Write Booster if we have scaled up else disable it */
> downgrade_write(>clk_scaling_lock);
> is_writelock = false;
> -   ufshcd_wb_ctrl(hba, scale_up);
> +   ufshcd_wb_toggle(hba, scale_up);
> 
>  out_unprepare:
> ufshcd_clock_scaling_unprepare(hba, is_writelock);
> @@ -5432,85 +5424,78 @@ static void
> ufshcd_bkops_exception_event_handler(struct ufs_hba *hba)
> __func__, err);
>  }
> 
> -int ufshcd_wb_ctrl(struct ufs_hba *hba, bool enable)
> +static int __ufshcd_wb_toggle(struct ufs_hba *hba, bool set, enum flag_idn
> idn)
>  {
> -   int ret;
> +   int val;
Use turnery here?


> u8 index;
> -   enum query_opcode opcode;
> +
> +   if (set)
> +   val = UPIU_QUERY_OPCODE_SET_FLAG;
> +   else
> +   val = UPIU_QUERY_OPCODE_CLEAR_FLAG;
> +
> +   index = ufshcd_wb_get_query_index(hba);
> +   return ufshcd_query_flag_retry(hba, val, idn, index, NULL);
> +}
> +
> +int ufshcd_wb_toggle(struct ufs_hba *hba, bool enable)
> +{
> +   int ret;
> 
> if (!ufshcd_is_wb_allowed(hba))
> return 0;
> 
> if (!(enable ^ hba->dev_info.wb_enabled))
> return 0;
> -   if (enable)
&g

RE: [PATCH v5 06/10] scsi: ufshpb: Add hpb dev reset response

2021-03-17 Thread Avri Altman
> 
> On 2021-03-17 19:23, Avri Altman wrote:
> >>
> >> On 2021-03-02 21:24, Avri Altman wrote:
> >> > The spec does not define what is the host's recommended response when
> >> > the device send hpb dev reset response (oper 0x2).
> >> >
> >> > We will update all active hpb regions: mark them and do that on the
> >> > next
> >> > read.
> >> >
> >> > Signed-off-by: Avri Altman 
> >> > ---
> >> >  drivers/scsi/ufs/ufshpb.c | 47
> >> ---
> >> >  drivers/scsi/ufs/ufshpb.h |  2 ++
> >> >  2 files changed, 46 insertions(+), 3 deletions(-)
> >> >
> >> > diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
> >> > index 0744feb4d484..0034fa03fdc6 100644
> >> > --- a/drivers/scsi/ufs/ufshpb.c
> >> > +++ b/drivers/scsi/ufs/ufshpb.c
> >> > @@ -642,7 +642,8 @@ int ufshpb_prep(struct ufs_hba *hba, struct
> >> > ufshcd_lrb *lrbp)
> >> >   if (rgn->reads == ACTIVATION_THRESHOLD)
> >> >   activate = true;
> >> >   spin_unlock_irqrestore(>rgn_lock, flags);
> >> > - if (activate) {
> >> > + if (activate ||
> >> > + test_and_clear_bit(RGN_FLAG_UPDATE, >rgn_flags)) {
> >> >   spin_lock_irqsave(>rsp_list_lock, flags);
> >> >   ufshpb_update_active_info(hpb, rgn_idx, srgn_idx);
> >> >   hpb->stats.rb_active_cnt++;
> >> > @@ -1480,6 +1481,20 @@ void ufshpb_rsp_upiu(struct ufs_hba *hba,
> >> > struct ufshcd_lrb *lrbp)
> >> >   case HPB_RSP_DEV_RESET:
> >> >   dev_warn(>sdev_ufs_lu->sdev_dev,
> >> >"UFS device lost HPB information during PM.\n");
> >> > +
> >> > + if (hpb->is_hcm) {
> >> > + struct scsi_device *sdev;
> >> > +
> >> > + __shost_for_each_device(sdev, hba->host) {
> >> > + struct ufshpb_lu *h = sdev->hostdata;
> >> > +
> >> > + if (!h)
> >> > + continue;
> >> > +
> >> > + schedule_work(>ufshpb_lun_reset_work);
> >> > + }
> >> > + }
> >> > +
> >> >   break;
> >> >   default:
> >> >   dev_notice(>sdev_ufs_lu->sdev_dev,
> >> > @@ -1594,6 +1609,25 @@ static void
> >> > ufshpb_run_inactive_region_list(struct ufshpb_lu *hpb)
> >> >   spin_unlock_irqrestore(>rsp_list_lock, flags);
> >> >  }
> >> >
> >> > +static void ufshpb_reset_work_handler(struct work_struct *work)
> >>
> >> Just curious, directly doing below things inside ufshpb_rsp_upiu()
> >> does
> >> not
> >> seem a problem to me, does this really deserve a separate work?
> > I don't know, I never even consider of doing this.
> > The active region list may contain up to few thousands of regions -
> > It is not rare to see configurations that covers the entire device.
> >
> 
> Yes, true, it can be a huge list. But what does the ops
> "HPB_RSP_DEV_RESET"
> really mean? The specs says "Device reset HPB Regions information", but
> I
> don't know what is really happening. Could you please elaborate?
It means that the device informs the host that the L2P cache is no longer valid.
The spec doesn't say what to do in that case.
We thought that in host mode, it make sense to update all the active regions.

I think I will go with your suggestion.
Effectively, in host mode, since it is deactivating "cold" regions,
the lru list is kept relatively small, and contains only "hot" regions.

Thanks,
Avri

> 
> Thanks,
> Can Guo.
> 
> > But yes, I can do that.
> > Better to get ack from Daejun first.
> >
> > Thanks,
> > Avri
> >
> >>
> >> Thanks,
> >> Can Guo.
> >>
> >> > +{
> >> > + struct ufshpb_lu *hpb;
> >> > + struct victim_select_info *lru_info;
> >> > + struct ufshpb_region *rgn;
> >> > + unsigned long flags;
> >> > +
> >> > + hpb = container_of(work, struc

RE: [PATCH v5 06/10] scsi: ufshpb: Add hpb dev reset response

2021-03-17 Thread Avri Altman
> 
> On 2021-03-02 21:24, Avri Altman wrote:
> > The spec does not define what is the host's recommended response when
> > the device send hpb dev reset response (oper 0x2).
> >
> > We will update all active hpb regions: mark them and do that on the
> > next
> > read.
> >
> > Signed-off-by: Avri Altman 
> > ---
> >  drivers/scsi/ufs/ufshpb.c | 47
> ---
> >  drivers/scsi/ufs/ufshpb.h |  2 ++
> >  2 files changed, 46 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
> > index 0744feb4d484..0034fa03fdc6 100644
> > --- a/drivers/scsi/ufs/ufshpb.c
> > +++ b/drivers/scsi/ufs/ufshpb.c
> > @@ -642,7 +642,8 @@ int ufshpb_prep(struct ufs_hba *hba, struct
> > ufshcd_lrb *lrbp)
> >   if (rgn->reads == ACTIVATION_THRESHOLD)
> >   activate = true;
> >   spin_unlock_irqrestore(>rgn_lock, flags);
> > - if (activate) {
> > + if (activate ||
> > + test_and_clear_bit(RGN_FLAG_UPDATE, >rgn_flags)) {
> >   spin_lock_irqsave(>rsp_list_lock, flags);
> >   ufshpb_update_active_info(hpb, rgn_idx, srgn_idx);
> >   hpb->stats.rb_active_cnt++;
> > @@ -1480,6 +1481,20 @@ void ufshpb_rsp_upiu(struct ufs_hba *hba,
> > struct ufshcd_lrb *lrbp)
> >   case HPB_RSP_DEV_RESET:
> >   dev_warn(>sdev_ufs_lu->sdev_dev,
> >"UFS device lost HPB information during PM.\n");
> > +
> > + if (hpb->is_hcm) {
> > + struct scsi_device *sdev;
> > +
> > + __shost_for_each_device(sdev, hba->host) {
> > + struct ufshpb_lu *h = sdev->hostdata;
> > +
> > + if (!h)
> > + continue;
> > +
> > + schedule_work(>ufshpb_lun_reset_work);
> > + }
> > + }
> > +
> >   break;
> >   default:
> >   dev_notice(>sdev_ufs_lu->sdev_dev,
> > @@ -1594,6 +1609,25 @@ static void
> > ufshpb_run_inactive_region_list(struct ufshpb_lu *hpb)
> >   spin_unlock_irqrestore(>rsp_list_lock, flags);
> >  }
> >
> > +static void ufshpb_reset_work_handler(struct work_struct *work)
> 
> Just curious, directly doing below things inside ufshpb_rsp_upiu() does
> not
> seem a problem to me, does this really deserve a separate work?
I don't know, I never even consider of doing this.
The active region list may contain up to few thousands of regions - 
It is not rare to see configurations that covers the entire device.

But yes, I can do that.
Better to get ack from Daejun first.

Thanks,
Avri

> 
> Thanks,
> Can Guo.
> 
> > +{
> > + struct ufshpb_lu *hpb;
> > + struct victim_select_info *lru_info;
> > + struct ufshpb_region *rgn;
> > + unsigned long flags;
> > +
> > + hpb = container_of(work, struct ufshpb_lu, ufshpb_lun_reset_work);
> > +
> > + lru_info = >lru_info;
> > +
> > + spin_lock_irqsave(>rgn_state_lock, flags);
> > +
> > + list_for_each_entry(rgn, _info->lh_lru_rgn, list_lru_rgn)
> > + set_bit(RGN_FLAG_UPDATE, >rgn_flags);
> > +
> > + spin_unlock_irqrestore(>rgn_state_lock, flags);
> > +}
> > +
> >  static void ufshpb_normalization_work_handler(struct work_struct
> > *work)
> >  {
> >   struct ufshpb_lu *hpb;
> > @@ -1798,6 +1832,8 @@ static int ufshpb_alloc_region_tbl(struct
> > ufs_hba *hba, struct ufshpb_lu *hpb)
> >   } else {
> >   rgn->rgn_state = HPB_RGN_INACTIVE;
> >   }
> > +
> > + rgn->rgn_flags = 0;
> >   }
> >
> >   return 0;
> > @@ -2012,9 +2048,12 @@ static int ufshpb_lu_hpb_init(struct ufs_hba
> > *hba, struct ufshpb_lu *hpb)
> >   INIT_LIST_HEAD(>list_hpb_lu);
> >
> >   INIT_WORK(>map_work, ufshpb_map_work_handler);
> > - if (hpb->is_hcm)
> > + if (hpb->is_hcm) {
> >   INIT_WORK(>ufshpb_normalization_work,
> > ufshpb_normalization_work_handler);
> > + INIT_WORK(>ufshpb_lun_reset_work,
> > +   ufshpb_reset_work_handler);
> > + }
> >
> >   hpb->map

RE: [PATCH v5 03/10] scsi: ufshpb: Add region's reads counter

2021-03-17 Thread Avri Altman
> >> > @@ -1079,6 +1113,14 @@ static void __ufshpb_evict_region(struct
> >> > ufshpb_lu *hpb,
> >> >
> >> >   ufshpb_cleanup_lru_info(lru_info, rgn);
> >> >
> >> > + if (hpb->is_hcm) {
> >> > + unsigned long flags;
> >> > +
> >> > + spin_lock_irqsave(>rgn_lock, flags);
> >> > + rgn->reads = 0;
> >> > + spin_unlock_irqrestore(>rgn_lock, flags);
> >> > + }
> >> > +
While at it, Following your comments concerning the unmap request,
Better move this as well outside of __ufshpb_evict_region while rgn_state_lock 
is not held.

Thanks,
Avri


RE: [PATCH v5 05/10] scsi: ufshpb: Region inactivation in host mode

2021-03-17 Thread Avri Altman
> On 2021-03-17 10:28, Daejun Park wrote:
> >>> >> ---
> >>> >>  drivers/scsi/ufs/ufshpb.c | 14 ++
> >>> >>  drivers/scsi/ufs/ufshpb.h |  1 +
> >>> >>  2 files changed, 15 insertions(+)
> >>> >>
> >>> >> diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
> >>> >> index 6f4fd22eaf2f..0744feb4d484 100644
> >>> >> --- a/drivers/scsi/ufs/ufshpb.c
> >>> >> +++ b/drivers/scsi/ufs/ufshpb.c
> >>> >> @@ -907,6 +907,7 @@ static int ufshpb_execute_umap_req(struct
> >>> >> ufshpb_lu *hpb,
> >>> >>
> >>> >>  blk_execute_rq_nowait(q, NULL, req, 1,
> ufshpb_umap_req_compl_fn);
> >>> >>
> >>> >> +hpb->stats.umap_req_cnt++;
> >>> >>  return 0;
> >>> >>  }
> >>> >>
> >>> >> @@ -1103,6 +1104,12 @@ static int ufshpb_issue_umap_req(struct
> >>> >> ufshpb_lu *hpb,
> >>> >>  return -EAGAIN;
> >>> >>  }
> >>> >>
> >>> >> +static int ufshpb_issue_umap_single_req(struct ufshpb_lu *hpb,
> >>> >> +struct ufshpb_region *rgn)
> >>> >> +{
> >>> >> +return ufshpb_issue_umap_req(hpb, rgn);
> >>> >> +}
> >>> >> +
> >>> >>  static int ufshpb_issue_umap_all_req(struct ufshpb_lu *hpb)
> >>> >>  {
> >>> >>  return ufshpb_issue_umap_req(hpb, NULL);
> >>> >> @@ -1115,6 +1122,10 @@ static void __ufshpb_evict_region(struct
> >>> >> ufshpb_lu *hpb,
> >>> >>  struct ufshpb_subregion *srgn;
> >>> >>  int srgn_idx;
> >>> >>
> >>> >> +
> >>> >> +if (hpb->is_hcm && ufshpb_issue_umap_single_req(hpb, rgn))
> >>> >
> >>> > __ufshpb_evict_region() is called with rgn_state_lock held and IRQ
> >>> > disabled,
> >>> > when ufshpb_issue_umap_single_req() invokes
> blk_execute_rq_nowait(),
> >>> > below
> >>> > warning shall pop up every time, fix it?
> >>> >
> >>> > void blk_execute_rq_nowait(struct request_queue *q, struct gendisk
> >>> > *bd_disk,
> >>> >  struct request *rq, int at_head,
> >>> >  rq_end_io_fn *done)
> >>> > {
> >>> >   WARN_ON(irqs_disabled());
> >>> > ...
> >>> >
> >>>
> >>> Moreover, since we are here with rgn_state_lock held and IRQ
> >>> disabled,
> >>> in ufshpb_get_req(), rq = kmem_cache_alloc(hpb->map_req_cache,
> >>> GFP_KERNEL)
> >>> has the GFP_KERNEL flag, scheduling while atomic???
> >> I think your comment applies to  ufshpb_issue_umap_all_req as well,
> >> Which is called from slave_configure/scsi_add_lun.
> >>
> >> Since the host-mode series is utilizing the framework laid by the
> >> device-mode,
> >> Maybe you can add this comment to  Daejun's last version?
> >
> > Hi Avri, Can Guo
> >
> > I think ufshpb_issue_umap_single_req() can be moved to end of
> > ufshpb_evict_region().
> > Then we can avoid rgn_state_lock when it sends unmap command.
> 
> I am not the expert here, please you two fix it. I am just reporting
> what can be wrong. Anyways, ufshpb_issue_umap_single_req() should not
> be called with rgn_state_lock held - think about below (another deadly)
> scenario.
> 
> lock(rgn_state_lock)
>ufshpb_issue_umap_single_req()
>  ufshpb_prep()
> lock(rgn_state_lock)   <-- recursive spin_lock
Will fix.   Will wait for Daejun's v30 to see if anything applies to 
unmap_single.

Thanks,
Avri 

> 
> BTW, @Daejun shouldn't we stop passthrough cmds from stepping
> into ufshpb_prep()? In current code, you are trying to use below
> check to block cmds other than write/discard/read, but a passthrough
> cmd can not be blocked by the check.
> 
>   if (!ufshpb_is_write_or_discard_cmd(cmd) &&
>   !ufshpb_is_read_cmd(cmd))
>   return 0;
> 
> Thanks,
> Can Guo.
> 
> >
> > Thanks,
> > Daejun
> >
> >
> >> Thanks,
> >> Avri
> >>
> >>>
> >>> Can Guo.
> >>>
> >>> > Thanks.
> >>> > Can Guo.
> >>> >
> >>> >> +return;
> >>> >> +
> >>> >>  lru_info = >lru_info;
> >>> >>
> >>> >>  dev_dbg(>sdev_ufs_lu->sdev_dev, "evict region %d\n",
> >>> >> rgn->rgn_idx);
> >>> >> @@ -1855,6 +1866,7 @@ ufshpb_sysfs_attr_show_func(rb_noti_cnt);
> >>> >>  ufshpb_sysfs_attr_show_func(rb_active_cnt);
> >>> >>  ufshpb_sysfs_attr_show_func(rb_inactive_cnt);
> >>> >>  ufshpb_sysfs_attr_show_func(map_req_cnt);
> >>> >> +ufshpb_sysfs_attr_show_func(umap_req_cnt);
> >>> >>
> >>> >>  static struct attribute *hpb_dev_stat_attrs[] = {
> >>> >>  _attr_hit_cnt.attr,
> >>> >> @@ -1863,6 +1875,7 @@ static struct attribute *hpb_dev_stat_attrs[]
> =
> >>> >> {
> >>> >>  _attr_rb_active_cnt.attr,
> >>> >>  _attr_rb_inactive_cnt.attr,
> >>> >>  _attr_map_req_cnt.attr,
> >>> >> +_attr_umap_req_cnt.attr,
> >>> >>  NULL,
> >>> >>  };
> >>> >>
> >>> >> @@ -1978,6 +1991,7 @@ static void ufshpb_stat_init(struct ufshpb_lu
> >>> >> *hpb)
> >>> >>  hpb->stats.rb_active_cnt = 0;
> >>> >>  hpb->stats.rb_inactive_cnt = 0;
> >>> >>  hpb->stats.map_req_cnt = 0;
> >>> >> +hpb->stats.umap_req_cnt = 0;
> >>> >>  }
> >>> >>
> >>> >>  static void ufshpb_param_init(struct ufshpb_lu *hpb)
> >>> >> diff --git a/drivers/scsi/ufs/ufshpb.h 

RE: [PATCH v5 07/10] scsi: ufshpb: Add "Cold" regions timer

2021-03-17 Thread Avri Altman
> On 2021-03-16 17:21, Avri Altman wrote:
> >> > +static void ufshpb_read_to_handler(struct work_struct *work)
> >> > +{
> >> > + struct delayed_work *dwork = to_delayed_work(work);
> >> > + struct ufshpb_lu *hpb;
> >> > + struct victim_select_info *lru_info;
> >> > + struct ufshpb_region *rgn;
> >> > + unsigned long flags;
> >> > + LIST_HEAD(expired_list);
> >> > +
> >> > + hpb = container_of(dwork, struct ufshpb_lu, ufshpb_read_to_work);
> >> > +
> >> > + spin_lock_irqsave(>rgn_state_lock, flags);
> >> > +
> >> > + lru_info = >lru_info;
> >> > +
> >> > + list_for_each_entry(rgn, _info->lh_lru_rgn, list_lru_rgn) {
> >> > + bool timedout = ktime_after(ktime_get(), 
> >> > rgn->read_timeout);
> >> > +
> >> > + if (timedout) {
> >> > + rgn->read_timeout_expiries--;
> >> > + if (is_rgn_dirty(rgn) ||
> >> > + rgn->read_timeout_expiries == 0)
> >> > + list_add(>list_expired_rgn, 
> >> > _list);
> >> > + else
> >> > + rgn->read_timeout = 
> >> > ktime_add_ms(ktime_get(),
> >> > +  READ_TO_MS);
> >> > + }
> >> > + }
> >> > +
> >> > + spin_unlock_irqrestore(>rgn_state_lock, flags);
> >> > +
> >> > + list_for_each_entry(rgn, _list, list_expired_rgn) {
> >>
> >> Here can be problematic - since you don't have the native expired_list
> >> initialized
> >> before use, if above loop did not insert anything to expired_list, it
> >> shall become
> >> a dead loop here.
> > Not sure what you meant by native initialization.
> > LIST_HEAD is statically initializing an empty list, resulting the same
> > outcome as INIT_LIST_HEAD.
> >
> 
> Sorry for making you confused, you should use list_for_each_entry_safe()
> instead of list_for_each_entry() as you are deleting entries within the
> loop,
> otherwise, this can become an infinite loop. Again, have you tested this
> patch
> before upload? I am sure this is problematic - when it becomes an
> inifinite
> loop, below path will hang...
> 
> ufshcd_suspend()->ufshpb_suspend()->cancel_jobs()->cancel_delayed_work()
Ahh  - yes.  You are right.  Originally I used list_for_each_entry_safe.
Not sure why I changed it here.  Will fix it.

I openly disclosed that I am testing the code on gs20 and mi10.
Those are v4.19 platforms, and I am using a driver adopted from the original 
public hpb driver
Published by Samsung with the gs20 code.
I am also concern as those drivers are drifted apart as the review process 
commences.
Will try to bring-up a more advanced platform (gs21) and apply the mainline hpb 
driver.


> 
> >>
> >> And, which lock is protecting rgn->list_expired_rgn? If two
> >> read_to_handler works
> >> are running in parallel, one can be inserting it to its expired_list
> >> while another
> >> can be deleting it.
> > The timeout handler, being a delayed work, is meant to run every
> > polling period.
> > Originally, I had it protected from 2 handlers running concurrently,
> > But I removed it following Daejun's comment, which I accepted,
> > Since it is always scheduled using the same polling period.
> 
> But one can set the delay to 0 through sysfs, right?
Will restore the protection.  Thanks.

Thanks,
Avri

> 
> Thanks,
> Can Guo.
> 
> >
> > Thanks,
> > Avri
> >
> >>
> >> Can Guo.
> >>
> >> > + list_del_init(>list_expired_rgn);
> >> > + spin_lock_irqsave(>rsp_list_lock, flags);
> >> > + ufshpb_update_inactive_info(hpb, rgn->rgn_idx);
> >> > + hpb->stats.rb_inactive_cnt++;
> >> > + spin_unlock_irqrestore(>rsp_list_lock, flags);
> >> > + }
> >> > +
> >> > + ufshpb_kick_map_work(hpb);
> >> > +
> >> > + schedule_delayed_work(>ufshpb_read_to_work,
> >> > +   msecs_to_jiffies(POLLING_INTERVAL_MS));
> >> > +}
> >> > +
> >> >  static void ufshpb_add_lru_info(struct victim_select_info *lru_info,
> >> > 

RE: [PATCH v5 07/10] scsi: ufshpb: Add "Cold" regions timer

2021-03-16 Thread Avri Altman
> > +static void ufshpb_read_to_handler(struct work_struct *work)
> > +{
> > + struct delayed_work *dwork = to_delayed_work(work);
> > + struct ufshpb_lu *hpb;
> > + struct victim_select_info *lru_info;
> > + struct ufshpb_region *rgn;
> > + unsigned long flags;
> > + LIST_HEAD(expired_list);
> > +
> > + hpb = container_of(dwork, struct ufshpb_lu, ufshpb_read_to_work);
> > +
> > + spin_lock_irqsave(>rgn_state_lock, flags);
> > +
> > + lru_info = >lru_info;
> > +
> > + list_for_each_entry(rgn, _info->lh_lru_rgn, list_lru_rgn) {
> > + bool timedout = ktime_after(ktime_get(), rgn->read_timeout);
> > +
> > + if (timedout) {
> > + rgn->read_timeout_expiries--;
> > + if (is_rgn_dirty(rgn) ||
> > + rgn->read_timeout_expiries == 0)
> > + list_add(>list_expired_rgn, 
> > _list);
> > + else
> > + rgn->read_timeout = ktime_add_ms(ktime_get(),
> > +  READ_TO_MS);
> > + }
> > + }
> > +
> > + spin_unlock_irqrestore(>rgn_state_lock, flags);
> > +
> > + list_for_each_entry(rgn, _list, list_expired_rgn) {
> 
> Here can be problematic - since you don't have the native expired_list
> initialized
> before use, if above loop did not insert anything to expired_list, it
> shall become
> a dead loop here.
Not sure what you meant by native initialization.
LIST_HEAD is statically initializing an empty list, resulting the same outcome 
as INIT_LIST_HEAD.

> 
> And, which lock is protecting rgn->list_expired_rgn? If two
> read_to_handler works
> are running in parallel, one can be inserting it to its expired_list
> while another
> can be deleting it.
The timeout handler, being a delayed work, is meant to run every polling period.
Originally, I had it protected from 2 handlers running concurrently,
But I removed it following Daejun's comment, which I accepted,
Since it is always scheduled using the same polling period.

Thanks,
Avri

> 
> Can Guo.
> 
> > + list_del_init(>list_expired_rgn);
> > + spin_lock_irqsave(>rsp_list_lock, flags);
> > + ufshpb_update_inactive_info(hpb, rgn->rgn_idx);
> > + hpb->stats.rb_inactive_cnt++;
> > + spin_unlock_irqrestore(>rsp_list_lock, flags);
> > + }
> > +
> > + ufshpb_kick_map_work(hpb);
> > +
> > + schedule_delayed_work(>ufshpb_read_to_work,
> > +   msecs_to_jiffies(POLLING_INTERVAL_MS));
> > +}
> > +
> >  static void ufshpb_add_lru_info(struct victim_select_info *lru_info,
> >   struct ufshpb_region *rgn)
> >  {
> >   rgn->rgn_state = HPB_RGN_ACTIVE;
> >   list_add_tail(>list_lru_rgn, _info->lh_lru_rgn);
> >   atomic_inc(_info->active_cnt);
> > + if (rgn->hpb->is_hcm) {
> > + rgn->read_timeout = ktime_add_ms(ktime_get(), READ_TO_MS);
> > + rgn->read_timeout_expiries = READ_TO_EXPIRIES;
> > + }
> >  }
> >
> >  static void ufshpb_hit_lru_info(struct victim_select_info *lru_info,
> > @@ -1813,6 +1865,7 @@ static int ufshpb_alloc_region_tbl(struct
> > ufs_hba *hba, struct ufshpb_lu *hpb)
> >
> >   INIT_LIST_HEAD(>list_inact_rgn);
> >   INIT_LIST_HEAD(>list_lru_rgn);
> > + INIT_LIST_HEAD(>list_expired_rgn);
> >
> >   if (rgn_idx == hpb->rgns_per_lu - 1) {
> >   srgn_cnt = ((hpb->srgns_per_lu - 1) %
> > @@ -1834,6 +1887,7 @@ static int ufshpb_alloc_region_tbl(struct
> > ufs_hba *hba, struct ufshpb_lu *hpb)
> >   }
> >
> >   rgn->rgn_flags = 0;
> > + rgn->hpb = hpb;
> >   }
> >
> >   return 0;
> > @@ -2053,6 +2107,8 @@ static int ufshpb_lu_hpb_init(struct ufs_hba
> > *hba, struct ufshpb_lu *hpb)
> > ufshpb_normalization_work_handler);
> >   INIT_WORK(>ufshpb_lun_reset_work,
> > ufshpb_reset_work_handler);
> > + INIT_DELAYED_WORK(>ufshpb_read_to_work,
> > +   ufshpb_read_to_handler);
> >   }
> >
> >   hpb->map_req_cache = kmem_cache_create("ufshpb_req_cache",
> > @@ -2087,6 +2143,10 @@ static int ufshpb_lu_hpb_init(struct ufs_hba
> > *hba, struct ufshpb_lu *hpb)
> >   ufshpb_stat_init(hpb);
> >   ufshpb_param_init(hpb);
> >
> > + if (hpb->is_hcm)
> > + schedule_delayed_work(>ufshpb_read_to_work,
> > +   msecs_to_jiffies(POLLING_INTERVAL_MS));
> > +
> >   return 0;
> >
> >  release_pre_req_mempool:
> > @@ -2154,6 +2214,7 @@ static void ufshpb_discard_rsp_lists(struct
> > ufshpb_lu *hpb)
> >  static void ufshpb_cancel_jobs(struct ufshpb_lu *hpb)
> >  {
> >   if (hpb->is_hcm) {
> > + cancel_delayed_work_sync(>ufshpb_read_to_work);
> >   

RE: [PATCH v5 04/10] scsi: ufshpb: Make eviction depends on region's reads

2021-03-16 Thread Avri Altman
> >   int ret = 0;
> > @@ -1263,6 +1271,16 @@ static int ufshpb_add_region(struct ufshpb_lu
> > *hpb, struct ufshpb_region *rgn)
> >* because the device could detect this region
> >* by not issuing HPB_READ
> >*/
> > +
> > + /*
> > +  * in host control mode, verify that the entering
> > +  * region has enough reads
> > +  */
> 
> Maybe merge the new comments with the original comments above?
Done.

Thanks,
Avri

> Thanks,
> Can Guo.


RE: [PATCH v5 08/10] scsi: ufshpb: Limit the number of inflight map requests

2021-03-16 Thread Avri Altman
> 
> 
> On 2021-03-02 21:25, Avri Altman wrote:
> > in host control mode the host is the originator of map requests. To not
> 
> in -> In
Done.

> 
> Thanks,
> Can Guo.
> 
> > flood the device with map requests, use a simple throttling mechanism
> > that limits the number of inflight map requests.
> >
> > Signed-off-by: Avri Altman 
> > ---
> >  drivers/scsi/ufs/ufshpb.c | 11 +++
> >  drivers/scsi/ufs/ufshpb.h |  1 +
> >  2 files changed, 12 insertions(+)
> >
> > diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
> > index 89a930e72cff..74da69727340 100644
> > --- a/drivers/scsi/ufs/ufshpb.c
> > +++ b/drivers/scsi/ufs/ufshpb.c
> > @@ -21,6 +21,7 @@
> >  #define READ_TO_MS 1000
> >  #define READ_TO_EXPIRIES 100
> >  #define POLLING_INTERVAL_MS 200
> > +#define THROTTLE_MAP_REQ_DEFAULT 1
> >
> >  /* memory management */
> >  static struct kmem_cache *ufshpb_mctx_cache;
> > @@ -750,6 +751,14 @@ static struct ufshpb_req
> > *ufshpb_get_map_req(struct ufshpb_lu *hpb,
> >   struct ufshpb_req *map_req;
> >   struct bio *bio;
> >
> > + if (hpb->is_hcm &&
> > + hpb->num_inflight_map_req >= THROTTLE_MAP_REQ_DEFAULT) {
> > + dev_info(>sdev_ufs_lu->sdev_dev,
> > +  "map_req throttle. inflight %d throttle %d",
> > +  hpb->num_inflight_map_req, THROTTLE_MAP_REQ_DEFAULT);
> > + return NULL;
> > + }
> > +
> >   map_req = ufshpb_get_req(hpb, srgn->rgn_idx, REQ_OP_SCSI_IN);
> >   if (!map_req)
> >   return NULL;
> > @@ -764,6 +773,7 @@ static struct ufshpb_req
> > *ufshpb_get_map_req(struct ufshpb_lu *hpb,
> >
> >   map_req->rb.srgn_idx = srgn->srgn_idx;
> >   map_req->rb.mctx = srgn->mctx;
> > + hpb->num_inflight_map_req++;
> >
> >   return map_req;
> >  }
> > @@ -773,6 +783,7 @@ static void ufshpb_put_map_req(struct ufshpb_lu
> > *hpb,
> >  {
> >   bio_put(map_req->bio);
> >   ufshpb_put_req(hpb, map_req);
> > + hpb->num_inflight_map_req--;
> >  }
> >
> >  static int ufshpb_clear_dirty_bitmap(struct ufshpb_lu *hpb,
> > diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
> > index b49e9a34267f..d83ab488688a 100644
> > --- a/drivers/scsi/ufs/ufshpb.h
> > +++ b/drivers/scsi/ufs/ufshpb.h
> > @@ -212,6 +212,7 @@ struct ufshpb_lu {
> >   struct ufshpb_req *pre_req;
> >   int num_inflight_pre_req;
> >   int throttle_pre_req;
> > + int num_inflight_map_req;
> >   struct list_head lh_pre_req_free;
> >   int cur_read_id;
> >   int pre_req_min_tr_len;


RE: [PATCH v5 05/10] scsi: ufshpb: Region inactivation in host mode

2021-03-16 Thread Avri Altman
> >> ---
> >>  drivers/scsi/ufs/ufshpb.c | 14 ++
> >>  drivers/scsi/ufs/ufshpb.h |  1 +
> >>  2 files changed, 15 insertions(+)
> >>
> >> diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
> >> index 6f4fd22eaf2f..0744feb4d484 100644
> >> --- a/drivers/scsi/ufs/ufshpb.c
> >> +++ b/drivers/scsi/ufs/ufshpb.c
> >> @@ -907,6 +907,7 @@ static int ufshpb_execute_umap_req(struct
> >> ufshpb_lu *hpb,
> >>
> >>  blk_execute_rq_nowait(q, NULL, req, 1, ufshpb_umap_req_compl_fn);
> >>
> >> +hpb->stats.umap_req_cnt++;
> >>  return 0;
> >>  }
> >>
> >> @@ -1103,6 +1104,12 @@ static int ufshpb_issue_umap_req(struct
> >> ufshpb_lu *hpb,
> >>  return -EAGAIN;
> >>  }
> >>
> >> +static int ufshpb_issue_umap_single_req(struct ufshpb_lu *hpb,
> >> +struct ufshpb_region *rgn)
> >> +{
> >> +return ufshpb_issue_umap_req(hpb, rgn);
> >> +}
> >> +
> >>  static int ufshpb_issue_umap_all_req(struct ufshpb_lu *hpb)
> >>  {
> >>  return ufshpb_issue_umap_req(hpb, NULL);
> >> @@ -1115,6 +1122,10 @@ static void __ufshpb_evict_region(struct
> >> ufshpb_lu *hpb,
> >>  struct ufshpb_subregion *srgn;
> >>  int srgn_idx;
> >>
> >> +
> >> +if (hpb->is_hcm && ufshpb_issue_umap_single_req(hpb, rgn))
> >
> > __ufshpb_evict_region() is called with rgn_state_lock held and IRQ
> > disabled,
> > when ufshpb_issue_umap_single_req() invokes blk_execute_rq_nowait(),
> > below
> > warning shall pop up every time, fix it?
> >
> > void blk_execute_rq_nowait(struct request_queue *q, struct gendisk
> > *bd_disk,
> >  struct request *rq, int at_head,
> >  rq_end_io_fn *done)
> > {
> >   WARN_ON(irqs_disabled());
> > ...
> >
> 
> Moreover, since we are here with rgn_state_lock held and IRQ disabled,
> in ufshpb_get_req(), rq = kmem_cache_alloc(hpb->map_req_cache,
> GFP_KERNEL)
> has the GFP_KERNEL flag, scheduling while atomic???
I think your comment applies to  ufshpb_issue_umap_all_req as well,
Which is called from slave_configure/scsi_add_lun.

Since the host-mode series is utilizing the framework laid by the device-mode,
Maybe you can add this comment to  Daejun's last version?

Thanks,
Avri

> 
> Can Guo.
> 
> > Thanks.
> > Can Guo.
> >
> >> +return;
> >> +
> >>  lru_info = >lru_info;
> >>
> >>  dev_dbg(>sdev_ufs_lu->sdev_dev, "evict region %d\n",
> >> rgn->rgn_idx);
> >> @@ -1855,6 +1866,7 @@ ufshpb_sysfs_attr_show_func(rb_noti_cnt);
> >>  ufshpb_sysfs_attr_show_func(rb_active_cnt);
> >>  ufshpb_sysfs_attr_show_func(rb_inactive_cnt);
> >>  ufshpb_sysfs_attr_show_func(map_req_cnt);
> >> +ufshpb_sysfs_attr_show_func(umap_req_cnt);
> >>
> >>  static struct attribute *hpb_dev_stat_attrs[] = {
> >>  _attr_hit_cnt.attr,
> >> @@ -1863,6 +1875,7 @@ static struct attribute *hpb_dev_stat_attrs[] =
> >> {
> >>  _attr_rb_active_cnt.attr,
> >>  _attr_rb_inactive_cnt.attr,
> >>  _attr_map_req_cnt.attr,
> >> +_attr_umap_req_cnt.attr,
> >>  NULL,
> >>  };
> >>
> >> @@ -1978,6 +1991,7 @@ static void ufshpb_stat_init(struct ufshpb_lu
> >> *hpb)
> >>  hpb->stats.rb_active_cnt = 0;
> >>  hpb->stats.rb_inactive_cnt = 0;
> >>  hpb->stats.map_req_cnt = 0;
> >> +hpb->stats.umap_req_cnt = 0;
> >>  }
> >>
> >>  static void ufshpb_param_init(struct ufshpb_lu *hpb)
> >> diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
> >> index bd4308010466..84598a317897 100644
> >> --- a/drivers/scsi/ufs/ufshpb.h
> >> +++ b/drivers/scsi/ufs/ufshpb.h
> >> @@ -186,6 +186,7 @@ struct ufshpb_stats {
> >>  u64 rb_inactive_cnt;
> >>  u64 map_req_cnt;
> >>  u64 pre_req_cnt;
> >> +u64 umap_req_cnt;
> >>  };
> >>
> >>  struct ufshpb_lu {


RE: [PATCH v5 03/10] scsi: ufshpb: Add region's reads counter

2021-03-15 Thread Avri Altman
> > + /* if region is active but has no reads - inactivate it */
> > + spin_lock(>rsp_list_lock);
> > + ufshpb_update_inactive_info(hpb, rgn->rgn_idx);
> 
> Miss a hpb->stats.rb_inactive_cnt++ here?
Thanks.
Also noticed that since rb_inactive_cnt and rb_active_cnt are incremented now 
in more than one place - 
Need to protect that.

Thanks,
Avri

> 
> Thanks,
> Can Guo.
> 
> > + spin_unlock(>rsp_list_lock);
> > + }
> > +}
> > +
> >  static void ufshpb_map_work_handler(struct work_struct *work)
> >  {
> >   struct ufshpb_lu *hpb = container_of(work, struct ufshpb_lu,
> > map_work);
> > @@ -1913,6 +1980,9 @@ static int ufshpb_lu_hpb_init(struct ufs_hba
> > *hba, struct ufshpb_lu *hpb)
> >   INIT_LIST_HEAD(>list_hpb_lu);
> >
> >   INIT_WORK(>map_work, ufshpb_map_work_handler);
> > + if (hpb->is_hcm)
> > + INIT_WORK(>ufshpb_normalization_work,
> > +   ufshpb_normalization_work_handler);
> >
> >   hpb->map_req_cache = kmem_cache_create("ufshpb_req_cache",
> > sizeof(struct ufshpb_req), 0, 0, NULL);
> > @@ -2012,6 +2082,8 @@ static void ufshpb_discard_rsp_lists(struct
> > ufshpb_lu *hpb)
> >
> >  static void ufshpb_cancel_jobs(struct ufshpb_lu *hpb)
> >  {
> > + if (hpb->is_hcm)
> > + cancel_work_sync(>ufshpb_normalization_work);
> >   cancel_work_sync(>map_work);
> >  }
> >
> > diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
> > index 8119b1a3d1e5..bd4308010466 100644
> > --- a/drivers/scsi/ufs/ufshpb.h
> > +++ b/drivers/scsi/ufs/ufshpb.h
> > @@ -121,6 +121,10 @@ struct ufshpb_region {
> >   struct list_head list_lru_rgn;
> >   unsigned long rgn_flags;
> >  #define RGN_FLAG_DIRTY 0
> > +
> > + /* region reads - for host mode */
> > + spinlock_t rgn_lock;
> > + unsigned int reads;
> >  };
> >
> >  #define for_each_sub_region(rgn, i, srgn)\
> > @@ -211,6 +215,7 @@ struct ufshpb_lu {
> >
> >   /* for selecting victim */
> >   struct victim_select_info lru_info;
> > + struct work_struct ufshpb_normalization_work;
> >
> >   /* pinned region information */
> >   u32 lu_pinned_start;


RE: [PATCH v5 03/10] scsi: ufshpb: Add region's reads counter

2021-03-15 Thread Avri Altman
> > +
> > + if (hpb->is_hcm) {
> > + spin_lock_irqsave(>rgn_lock, flags);
> 
> rgn_lock is never used in IRQ contexts, so no need of irqsave and
> irqrestore everywhere, which can impact performance. Please correct
> me if I am wrong.
Thanks.  Will do.

> 
> Meanwhile, have you ever initialized the rgn_lock before use it???
Yep - forgot to do that here (but not in gs20 and mi10).  Thanks.

Thanks,
Avri

> 
> Thanks,
> Can Guo.
> 
> > + rgn->reads = 0;
> > + spin_unlock_irqrestore(>rgn_lock, flags);
> > + }
> > +
> >   return 0;
> >   }
> >
> >   if (!ufshpb_is_support_chunk(hpb, transfer_len))
> >   return 0;
> >
> > + if (hpb->is_hcm) {
> > + bool activate = false;
> > + /*
> > +  * in host control mode, reads are the main source for
> > +  * activation trials.
> > +  */
> > + spin_lock_irqsave(>rgn_lock, flags);
> > + rgn->reads++;
> > + if (rgn->reads == ACTIVATION_THRESHOLD)
> > + activate = true;
> > + spin_unlock_irqrestore(>rgn_lock, flags);
> > + if (activate) {
> > + spin_lock_irqsave(>rsp_list_lock, flags);
> > + ufshpb_update_active_info(hpb, rgn_idx, srgn_idx);
> > + hpb->stats.rb_active_cnt++;
> > + spin_unlock_irqrestore(>rsp_list_lock, flags);
> > + dev_dbg(>sdev_ufs_lu->sdev_dev,
> > + "activate region %d-%d\n", rgn_idx, srgn_idx);
> > + }
> > +
> > + /* keep those counters normalized */
> > + if (rgn->reads > hpb->entries_per_srgn)
> > + schedule_work(>ufshpb_normalization_work);
> > + }
> > +
> >   spin_lock_irqsave(>rgn_state_lock, flags);
> >   if (ufshpb_test_ppn_dirty(hpb, rgn_idx, srgn_idx, srgn_offset,
> >  transfer_len)) {
> > @@ -745,21 +794,6 @@ static int ufshpb_clear_dirty_bitmap(struct
> > ufshpb_lu *hpb,
> >   return 0;
> >  }
> >
> > -static void ufshpb_update_active_info(struct ufshpb_lu *hpb, int
> > rgn_idx,
> > -   int srgn_idx)
> > -{
> > - struct ufshpb_region *rgn;
> > - struct ufshpb_subregion *srgn;
> > -
> > - rgn = hpb->rgn_tbl + rgn_idx;
> > - srgn = rgn->srgn_tbl + srgn_idx;
> > -
> > - list_del_init(>list_inact_rgn);
> > -
> > - if (list_empty(>list_act_srgn))
> > - list_add_tail(>list_act_srgn, >lh_act_srgn);
> > -}
> > -
> >  static void ufshpb_update_inactive_info(struct ufshpb_lu *hpb, int
> > rgn_idx)
> >  {
> >   struct ufshpb_region *rgn;
> > @@ -1079,6 +1113,14 @@ static void __ufshpb_evict_region(struct
> > ufshpb_lu *hpb,
> >
> >   ufshpb_cleanup_lru_info(lru_info, rgn);
> >
> > + if (hpb->is_hcm) {
> > + unsigned long flags;
> > +
> > + spin_lock_irqsave(>rgn_lock, flags);
> > + rgn->reads = 0;
> > + spin_unlock_irqrestore(>rgn_lock, flags);
> > + }
> > +
> >   for_each_sub_region(rgn, srgn_idx, srgn)
> >   ufshpb_purge_active_subregion(hpb, srgn);
> >  }
> > @@ -1523,6 +1565,31 @@ static void
> > ufshpb_run_inactive_region_list(struct ufshpb_lu *hpb)
> >   spin_unlock_irqrestore(>rsp_list_lock, flags);
> >  }
> >
> > +static void ufshpb_normalization_work_handler(struct work_struct
> > *work)
> > +{
> > + struct ufshpb_lu *hpb;
> > + int rgn_idx;
> > + unsigned long flags;
> > +
> > + hpb = container_of(work, struct ufshpb_lu,
> > ufshpb_normalization_work);
> > +
> > + for (rgn_idx = 0; rgn_idx < hpb->rgns_per_lu; rgn_idx++) {
> > + struct ufshpb_region *rgn = hpb->rgn_tbl + rgn_idx;
> > +
> > + spin_lock_irqsave(>rgn_lock, flags);
> > + rgn->reads = (rgn->reads >> 1);
> > + spin_unlock_irqrestore(>rgn_lock, flags);
> > +
> > + if (rgn->rgn_state != HPB_RGN_ACTIVE || rgn->reads)
> > + continue;
> > +
> > + /* if region is active but has no reads - inactivate it */
> > + spin_lock(>rsp_list_lock);
> > + ufshpb_update_inactive_info(hpb, rgn->rgn_idx);
> > + spin_unlock(>rsp_list_lock);
> > + }
> > +}
> > +
> >  static void ufshpb_map_work_handler(struct work_struct *work)
> >  {
> >   struct ufshpb_lu *hpb = container_of(work, struct ufshpb_lu,
> > map_work);
> > @@ -1913,6 +1980,9 @@ static int ufshpb_lu_hpb_init(struct ufs_hba
> > *hba, struct ufshpb_lu *hpb)
> >   INIT_LIST_HEAD(>list_hpb_lu);
> >
> >   INIT_WORK(>map_work, ufshpb_map_work_handler);
> > + if (hpb->is_hcm)
> > + INIT_WORK(>ufshpb_normalization_work,
> > +   ufshpb_normalization_work_handler);
> >
> >   hpb->map_req_cache = 

RE: [PATCH v5 07/10] scsi: ufshpb: Add "Cold" regions timer

2021-03-15 Thread Avri Altman
> >
> > +static void ufshpb_read_to_handler(struct work_struct *work)
> > +{
> > + struct delayed_work *dwork = to_delayed_work(work);
> > + struct ufshpb_lu *hpb;
> 
>  struct ufshpb_lu *hpb = container_of(work, struct ufshpb_lu,
> ufshpb_read_to_work.work);
> 
> usually we use this to get data of a delayed work.
> 
> > + struct victim_select_info *lru_info;
> 
>  struct victim_select_info *lru_info = >lru_info;
> 
> This can save some lines.
Done.

Thanks,
Avri

> Thanks,
> Can Guo.
> 
> > + struct ufshpb_region *rgn;
> > + unsigned long flags;
> > + LIST_HEAD(expired_list);


RE: [PATCH v5 06/10] scsi: ufshpb: Add hpb dev reset response

2021-03-15 Thread Avri Altman
> > +static void ufshpb_reset_work_handler(struct work_struct *work)
> > +{
> > + struct ufshpb_lu *hpb;
> 
>  struct ufshpb_lu *hpb = container_of(work, struct ufshpb_lu,
> ufshpb_lun_reset_work);
> 
> > + struct victim_select_info *lru_info;
> 
>  struct victim_select_info *lru_info = >lru_info;
> 
> This can save some lines.
Done.

Thanks,
Avri
> 
> Thanks,
> Can Guo.
> 


RE: [RFC PATCH 0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend

2021-03-11 Thread Avri Altman
> Avri Altman  writes:
> 
> > The mmc driver has some hooks to support rpmb access, but access is
> > mainly facilitated from user space, e.g. mmc-utils.
> >
> > The ufs driver has no concept of rpmb access - it is facilitated via
> > user space, e.g. ufs-utils and similar.
> >
> > Both for ufs and mmc, rpmb access is defined in their applicable jedec
> > specs. This is the case for few years now - AFAIK No new rpmb-related
> > stuff is expected to be introduced in the near future.
> >
> > What problems, as far as mmc and ufs, are you trying to solve by this
> > new subsystem?
> 
> Well in my case the addition of virtio-rpmb. As yet another RPMB device
> which only supports RPMB transactions and isn't part of a wider block
> device. The API dissonance comes into play when looking to implement it
> as part of wider block device stacks and then having to do things like
> fake 0 length eMMC devices with just an RPMB partition to use existing
> tools.
> 
> I guess that was the original attraction of having a common kernel
> subsystem to interact with RPMB functionality regardless of the
> underlying HW. However from the other comments it seems the preference
> is just to leave it to user-space and domain specific tools for each
> device type.
Yes.  It took us a while to clean those tools to perform by-spec rpmb access.

Anyway, I do see value in Tomas's/your approach, 
that will refrain from the need to register a designated block device.
Provided that each host is allowed to register its own ops.
Those ops can be a super-set of the various device types.
For ufs and mmc rpmb ops contains 7 operations.

Thanks,
Avri


RE: [PATCH v5 05/10] scsi: ufshpb: Region inactivation in host mode

2021-03-11 Thread Avri Altman
> 
> On 2021-03-02 21:24, Avri Altman wrote:
> > I host mode, the host is expected to send HPB-WRITE-BUFFER with
> 
> In host mode,
Done.

> >  static int ufshpb_issue_umap_all_req(struct ufshpb_lu *hpb)
> >  {
> >   return ufshpb_issue_umap_req(hpb, NULL);
> > @@ -1115,6 +1122,10 @@ static void __ufshpb_evict_region(struct
> > ufshpb_lu *hpb,
> >   struct ufshpb_subregion *srgn;
> >   int srgn_idx;
> >
> > +
> 
> No need of this blank line.
Done.

Thanks,
Avri

> 
> Regards,
> Can Guo.


RE: [PATCH v5 04/10] scsi: ufshpb: Make eviction depends on region's reads

2021-03-11 Thread Avri Altman
> > diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
> > index a8f8d13af21a..6f4fd22eaf2f 100644
> > --- a/drivers/scsi/ufs/ufshpb.c
> > +++ b/drivers/scsi/ufs/ufshpb.c
> > @@ -17,6 +17,7 @@
> >  #include "../sd.h"
> >
> >  #define ACTIVATION_THRESHOLD 4 /* 4 IOs */
> > +#define EVICTION_THRESHOLD (ACTIVATION_THRESHOLD << 6) /* 256 IOs
> */
> 
> Same here, can this be added as a sysfs entry?
Yes.  Please see last patch.

Thanks,
Avri

> 
> Thanks,
> Can Guo.


RE: [PATCH v5 03/10] scsi: ufshpb: Add region's reads counter

2021-03-11 Thread Avri Altman
> > diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
> > index 044fec9854a0..a8f8d13af21a 100644
> > --- a/drivers/scsi/ufs/ufshpb.c
> > +++ b/drivers/scsi/ufs/ufshpb.c
> > @@ -16,6 +16,8 @@
> >  #include "ufshpb.h"
> >  #include "../sd.h"
> >
> > +#define ACTIVATION_THRESHOLD 4 /* 4 IOs */
> 
> Can this param be added as a sysfs entry?
Yes.
Daejun asked me that as well, so the last patch makes all logic parameter 
configurable.

Thanks,
Avri

> 
> Thanks,
> Can Guo


RE: [PATCH v3 1/3] scsi: ufshcd: use a function to calculate versions

2021-03-10 Thread Avri Altman
> Hi Avri,
> 
> On 10/03/2021 4:34 pm, Avri Altman wrote:
> >> @@ -9298,10 +9291,7 @@ int ufshcd_init(struct ufs_hba *hba, void
> __iomem
> >> *mmio_base, unsigned int irq)
> >>  /* Get UFS version supported by the controller */
> >>  hba->ufs_version = ufshcd_get_ufs_version(hba);
> >>
> >> -   if ((hba->ufs_version != UFSHCI_VERSION_10) &&
> >> -   (hba->ufs_version != UFSHCI_VERSION_11) &&
> >> -   (hba->ufs_version != UFSHCI_VERSION_20) &&
> >> -   (hba->ufs_version != UFSHCI_VERSION_21))
> >> +   if (hba->ufs_version < ufshci_version(1, 0))
> >>  dev_err(hba->dev, "invalid UFS version 0x%x\n",
> >>  hba->ufs_version);
> > Here you replaces the specific allowable values, with an expression
> > That doesn't really reflects those values.
> 
> I took this approach based on feedback from previous patches:
> 
> https://lore.kernel.org/linux-
> scsi/d1b23943b6b3ae6c1f6af041cc592...@codeaurora.org/
> 
> https://lkml.org/lkml/2020/4/25/159
> 
> 
> Patch 3 of this series removes this check entirely, as it is neither
> accurate or useful.
I noticed that.

> 
> The driver does not fail when printing this error, nor is the list of
> "valid" UFS versions here kept up to date, I struggle to see a situation
> in which that error message would actually be helpful. Responses to
> previous patches (above) that added UFS 3.0 to the list have all
> suggested that removing this check is a more sensible approach.
OK.

Thanks,
Avri


RE: [PATCH v3 1/3] scsi: ufshcd: use a function to calculate versions

2021-03-10 Thread Avri Altman
> @@ -9298,10 +9291,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem
> *mmio_base, unsigned int irq)
> /* Get UFS version supported by the controller */
> hba->ufs_version = ufshcd_get_ufs_version(hba);
> 
> -   if ((hba->ufs_version != UFSHCI_VERSION_10) &&
> -   (hba->ufs_version != UFSHCI_VERSION_11) &&
> -   (hba->ufs_version != UFSHCI_VERSION_20) &&
> -   (hba->ufs_version != UFSHCI_VERSION_21))
> +   if (hba->ufs_version < ufshci_version(1, 0))
> dev_err(hba->dev, "invalid UFS version 0x%x\n",
> hba->ufs_version);
Here you replaces the specific allowable values, with an expression
That doesn't really reflects those values.


[RFC PATCH 0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend

2021-03-09 Thread Avri Altman
The mmc driver has some hooks to support rpmb access, but access is
mainly facilitated from user space, e.g. mmc-utils.

The ufs driver has no concept of rpmb access - it is facilitated via
user space, e.g. ufs-utils and similar.

Both for ufs and mmc, rpmb access is defined in their applicable jedec
specs. This is the case for few years now - AFAIK No new rpmb-related
stuff is expected to be introduced in the near future.

What problems, as far as mmc and ufs, are you trying to solve by this new 
subsystem?

Thanks,
Avri



RE: [PATCH] scsi: ufs: fix error return code of ufshcd_populate_vreg()

2021-03-06 Thread Avri Altman
> 
> When np is NULL or of_parse_phandle() returns NULL, no error return code
> of ufshcd_populate_vreg() is assigned.
> To fix this bug, ret is assigned with -EINVAL or -ENOENT as error return
> code.
This changes the flow of ufshcd_parse_regulator_info so you need to:
a) get a tested-by tag and indicate which platform & devices you used, and
b) explain further why ufshcd_parse_regulator_info doesn't no longer allow
 some of the regulators not to be set via DT, which is the current state.

Thanks,
Avri

> 
> Reported-by: TOTE Robot 
> Signed-off-by: Jia-Ju Bai 
> ---
>  drivers/scsi/ufs/ufshcd-pltfrm.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c 
> b/drivers/scsi/ufs/ufshcd-pltfrm.c
> index 1a69949a4ea1..9f11c416a919 100644
> --- a/drivers/scsi/ufs/ufshcd-pltfrm.c
> +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
> @@ -113,6 +113,7 @@ static int ufshcd_populate_vreg(struct device *dev,
> const char *name,
> 
> if (!np) {
> dev_err(dev, "%s: non DT initialization\n", __func__);
> +   ret = -EINVAL;
> goto out;
> }
> 
> @@ -120,6 +121,7 @@ static int ufshcd_populate_vreg(struct device *dev,
> const char *name,
> if (!of_parse_phandle(np, prop_name, 0)) {
> dev_info(dev, "%s: Unable to find %s regulator, assuming 
> enabled\n",
> __func__, prop_name);
> +   ret = -ENOENT;
> goto out;
> }
> 
> --
> 2.17.1



RE: [PATCH v2 1/3] scsi: ufs: Minor adjustments to error handling

2021-03-03 Thread Avri Altman
> 
> In error handling prepare stage, after SCSI requests are blocked, do a
> down/up_write(clk_scaling_lock) to clean up the queuecommand() path.
> Meanwhile, stop eeh_work in case it disturbs error recovery. Moreover,
> reset ufshcd_state at the entrance of ufshcd_probe_hba(), since it may be
> called multiple times during error recovery.
> 
> Signed-off-by: Can Guo 
Reviewed-by: Avri Altman 


> ---
>  drivers/scsi/ufs/ufshcd.c | 18 --
>  1 file changed, 12 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
> index 80620c8..013eb73 100644
> --- a/drivers/scsi/ufs/ufshcd.c
> +++ b/drivers/scsi/ufs/ufshcd.c
> @@ -4987,6 +4987,7 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba,
> struct ufshcd_lrb *lrbp)
>  * UFS device needs urgent BKOPs.
>  */
> if (!hba->pm_op_in_progress &&
> +   !ufshcd_eh_in_progress(hba) &&
> ufshcd_is_exception_event(lrbp->ucd_rsp_ptr) &&
> schedule_work(>eeh_work)) {
> /*
> @@ -5784,13 +5785,20 @@ static void ufshcd_err_handling_prepare(struct
> ufs_hba *hba)
> ufshcd_suspend_clkscaling(hba);
> ufshcd_clk_scaling_allow(hba, false);
> }
> +   ufshcd_scsi_block_requests(hba);
> +   /* Drain ufshcd_queuecommand() */
> +   down_write(>clk_scaling_lock);
> +   up_write(>clk_scaling_lock);
> +   cancel_work_sync(>eeh_work);
>  }
> 
>  static void ufshcd_err_handling_unprepare(struct ufs_hba *hba)
>  {
> +   ufshcd_scsi_unblock_requests(hba);
> ufshcd_release(hba);
> if (ufshcd_is_clkscaling_supported(hba))
> ufshcd_clk_scaling_suspend(hba, false);
> +   ufshcd_clear_ua_wluns(hba);
> pm_runtime_put(hba->dev);
>  }
> 
> @@ -5882,8 +5890,8 @@ static void ufshcd_err_handler(struct work_struct
> *work)
> spin_unlock_irqrestore(hba->host->host_lock, flags);
> ufshcd_err_handling_prepare(hba);
> spin_lock_irqsave(hba->host->host_lock, flags);
> -   ufshcd_scsi_block_requests(hba);
> -   hba->ufshcd_state = UFSHCD_STATE_RESET;
> +   if (hba->ufshcd_state != UFSHCD_STATE_ERROR)
> +   hba->ufshcd_state = UFSHCD_STATE_RESET;
> 
> /* Complete requests that have door-bell cleared by h/w */
> ufshcd_complete_requests(hba);
> @@ -6042,12 +6050,8 @@ static void ufshcd_err_handler(struct work_struct
> *work)
> }
> ufshcd_clear_eh_in_progress(hba);
> spin_unlock_irqrestore(hba->host->host_lock, flags);
> -   ufshcd_scsi_unblock_requests(hba);
> ufshcd_err_handling_unprepare(hba);
> up(>host_sem);
> -
> -   if (!err && needs_reset)
> -   ufshcd_clear_ua_wluns(hba);
>  }
> 
>  /**
> @@ -7858,6 +7862,8 @@ static int ufshcd_probe_hba(struct ufs_hba *hba,
> bool async)
> unsigned long flags;
> ktime_t start = ktime_get();
> 
> +   hba->ufshcd_state = UFSHCD_STATE_RESET;
> +
> ret = ufshcd_link_startup(hba);
> if (ret)
> goto out;
> --
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux
> Foundation Collaborative Project.



RE: [PATCH v2 1/3] scsi: ufs: Minor adjustments to error handling

2021-03-03 Thread Avri Altman
> 
> 
> In error handling prepare stage, after SCSI requests are blocked, do a
> down/up_write(clk_scaling_lock) to clean up the queuecommand() path.
> Meanwhile, stop eeh_work in case it disturbs error recovery. Moreover,
> reset ufshcd_state at the entrance of ufshcd_probe_hba(), since it may be
> called multiple times during error recovery.
> 
> Signed-off-by: Can Guo 
I noticed that you tagged Adrian's patch -
https://lore.kernel.org/lkml/20210301191940.15247-1-adrian.hun...@intel.com/
So this patch needs to be adjusted accordingly?

Thanks,
Avri

> ---
>  drivers/scsi/ufs/ufshcd.c | 18 --
>  1 file changed, 12 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
> index 80620c8..013eb73 100644
> --- a/drivers/scsi/ufs/ufshcd.c
> +++ b/drivers/scsi/ufs/ufshcd.c
> @@ -4987,6 +4987,7 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba,
> struct ufshcd_lrb *lrbp)
>  * UFS device needs urgent BKOPs.
>  */
> if (!hba->pm_op_in_progress &&
> +   !ufshcd_eh_in_progress(hba) &&
> ufshcd_is_exception_event(lrbp->ucd_rsp_ptr) &&
> schedule_work(>eeh_work)) {
> /*
> @@ -5784,13 +5785,20 @@ static void ufshcd_err_handling_prepare(struct
> ufs_hba *hba)
> ufshcd_suspend_clkscaling(hba);
> ufshcd_clk_scaling_allow(hba, false);
> }
> +   ufshcd_scsi_block_requests(hba);
> +   /* Drain ufshcd_queuecommand() */
> +   down_write(>clk_scaling_lock);
> +   up_write(>clk_scaling_lock);
> +   cancel_work_sync(>eeh_work);
>  }
> 
>  static void ufshcd_err_handling_unprepare(struct ufs_hba *hba)
>  {
> +   ufshcd_scsi_unblock_requests(hba);
> ufshcd_release(hba);
> if (ufshcd_is_clkscaling_supported(hba))
> ufshcd_clk_scaling_suspend(hba, false);
> +   ufshcd_clear_ua_wluns(hba);
> pm_runtime_put(hba->dev);
>  }
> 
> @@ -5882,8 +5890,8 @@ static void ufshcd_err_handler(struct work_struct
> *work)
> spin_unlock_irqrestore(hba->host->host_lock, flags);
> ufshcd_err_handling_prepare(hba);
> spin_lock_irqsave(hba->host->host_lock, flags);
> -   ufshcd_scsi_block_requests(hba);
> -   hba->ufshcd_state = UFSHCD_STATE_RESET;
> +   if (hba->ufshcd_state != UFSHCD_STATE_ERROR)
> +   hba->ufshcd_state = UFSHCD_STATE_RESET;
> 
> /* Complete requests that have door-bell cleared by h/w */
> ufshcd_complete_requests(hba);
> @@ -6042,12 +6050,8 @@ static void ufshcd_err_handler(struct work_struct
> *work)
> }
> ufshcd_clear_eh_in_progress(hba);
> spin_unlock_irqrestore(hba->host->host_lock, flags);
> -   ufshcd_scsi_unblock_requests(hba);
> ufshcd_err_handling_unprepare(hba);
> up(>host_sem);
> -
> -   if (!err && needs_reset)
> -   ufshcd_clear_ua_wluns(hba);
>  }
> 
>  /**
> @@ -7858,6 +7862,8 @@ static int ufshcd_probe_hba(struct ufs_hba *hba,
> bool async)
> unsigned long flags;
> ktime_t start = ktime_get();
> 
> +   hba->ufshcd_state = UFSHCD_STATE_RESET;
> +
> ret = ufshcd_link_startup(hba);
> if (ret)
> goto out;
> --
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux
> Foundation Collaborative Project.



  1   2   3   4   5   6   >