[v18,3/3] scsi: ufs: Prepare HPB read for cached sub-region

Message ID 20201222015854epcms2p1bdc30b8fab8ef01502451b75e7fbaf49@epcms2p1
State Superseded
Headers show
Series
  • [v18,1/3] scsi: ufs: Introduce HPB feature
Related show

Commit Message

Daejun Park Dec. 22, 2020, 1:58 a.m.
This patch changes the read I/O to the HPB read I/O.

If the logical address of the read I/O belongs to active sub-region, the
HPB driver modifies the read I/O command to HPB read. It modifies the UPIU
command of UFS instead of modifying the existing SCSI command.

In the HPB version 1.0, the maximum read I/O size that can be converted to
HPB read is 4KB.

The dirty map of the active sub-region prevents an incorrect HPB read that
has stale physical page number which is updated by previous write I/O.

Reviewed-by: Can Guo <cang@codeaurora.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Acked-by: Avri Altman <Avri.Altman@wdc.com>
Tested-by: Bean Huo <beanhuo@micron.com>
Signed-off-by: Daejun Park <daejun7.park@samsung.com>
---
 drivers/scsi/ufs/ufshcd.c |   2 +
 drivers/scsi/ufs/ufshpb.c | 232 ++++++++++++++++++++++++++++++++++++++
 drivers/scsi/ufs/ufshpb.h |   2 +
 3 files changed, 236 insertions(+)

Comments

Can Guo Jan. 11, 2021, 4:10 a.m. | #1
On 2020-12-22 09:58, Daejun Park wrote:
> This patch changes the read I/O to the HPB read I/O.

> 

> If the logical address of the read I/O belongs to active sub-region, 

> the

> HPB driver modifies the read I/O command to HPB read. It modifies the 

> UPIU

> command of UFS instead of modifying the existing SCSI command.

> 

> In the HPB version 1.0, the maximum read I/O size that can be converted 

> to

> HPB read is 4KB.

> 

> The dirty map of the active sub-region prevents an incorrect HPB read 

> that

> has stale physical page number which is updated by previous write I/O.

> 

> Reviewed-by: Can Guo <cang@codeaurora.org>

> Reviewed-by: Bart Van Assche <bvanassche@acm.org>

> Acked-by: Avri Altman <Avri.Altman@wdc.com>

> Tested-by: Bean Huo <beanhuo@micron.com>

> Signed-off-by: Daejun Park <daejun7.park@samsung.com>

> ---

>  drivers/scsi/ufs/ufshcd.c |   2 +

>  drivers/scsi/ufs/ufshpb.c | 232 ++++++++++++++++++++++++++++++++++++++

>  drivers/scsi/ufs/ufshpb.h |   2 +

>  3 files changed, 236 insertions(+)

> 

> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c

> index 0ec0ed237140..41554afead63 100644

> --- a/drivers/scsi/ufs/ufshcd.c

> +++ b/drivers/scsi/ufs/ufshcd.c

> @@ -2600,6 +2600,8 @@ static int ufshcd_queuecommand(struct Scsi_Host

> *host, struct scsi_cmnd *cmd)

> 

>  	ufshcd_comp_scsi_upiu(hba, lrbp);

> 

> +	ufshpb_prep(hba, lrbp);

> +

>  	err = ufshcd_map_sg(hba, lrbp);

>  	if (err) {

>  		lrbp->cmd = NULL;

> diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c

> index 37ae61fbf36a..d3e6c5b32328 100644

> --- a/drivers/scsi/ufs/ufshpb.c

> +++ b/drivers/scsi/ufs/ufshpb.c

> @@ -31,6 +31,29 @@ bool ufshpb_is_allowed(struct ufs_hba *hba)

>  	return !(hba->ufshpb_dev.hpb_disabled);

>  }

> 

> +static int ufshpb_is_valid_srgn(struct ufshpb_region *rgn,

> +			     struct ufshpb_subregion *srgn)

> +{

> +	return rgn->rgn_state != HPB_RGN_INACTIVE &&

> +		srgn->srgn_state == HPB_SRGN_VALID;

> +}

> +

> +static bool ufshpb_is_read_cmd(struct scsi_cmnd *cmd)

> +{

> +	return req_op(cmd->request) == REQ_OP_READ;

> +}

> +

> +static bool ufshpb_is_write_or_discard_cmd(struct scsi_cmnd *cmd)

> +{

> +	return op_is_write(req_op(cmd->request)) ||

> +	       op_is_discard(req_op(cmd->request));

> +}

> +

> +static bool ufshpb_is_support_chunk(int transfer_len)

> +{

> +	return transfer_len <= HPB_MULTI_CHUNK_HIGH;

> +}

> +

>  static bool ufshpb_is_general_lun(int lun)

>  {

>  	return lun < UFS_UPIU_MAX_UNIT_NUM_ID;

> @@ -98,6 +121,215 @@ static void ufshpb_set_state(struct ufshpb_lu

> *hpb, int state)

>  	atomic_set(&hpb->hpb_state, state);

>  }

> 

> +static void ufshpb_set_ppn_dirty(struct ufshpb_lu *hpb, int rgn_idx,

> +			     int srgn_idx, int srgn_offset, int cnt)

> +{

> +	struct ufshpb_region *rgn;

> +	struct ufshpb_subregion *srgn;

> +	int set_bit_len;

> +	int bitmap_len = hpb->entries_per_srgn;

> +

> +next_srgn:

> +	rgn = hpb->rgn_tbl + rgn_idx;

> +	srgn = rgn->srgn_tbl + srgn_idx;

> +

> +	if ((srgn_offset + cnt) > bitmap_len)

> +		set_bit_len = bitmap_len - srgn_offset;

> +	else

> +		set_bit_len = cnt;

> +

> +	if (rgn->rgn_state != HPB_RGN_INACTIVE &&

> +	    srgn->srgn_state == HPB_SRGN_VALID)

> +		bitmap_set(srgn->mctx->ppn_dirty, srgn_offset, set_bit_len);

> +

> +	srgn_offset = 0;

> +	if (++srgn_idx == hpb->srgns_per_rgn) {

> +		srgn_idx = 0;

> +		rgn_idx++;

> +	}

> +

> +	cnt -= set_bit_len;

> +	if (cnt > 0)

> +		goto next_srgn;

> +

> +	WARN_ON(cnt < 0);

> +}

> +

> +static bool ufshpb_test_ppn_dirty(struct ufshpb_lu *hpb, int rgn_idx,

> +				   int srgn_idx, int srgn_offset, int cnt)

> +{

> +	struct ufshpb_region *rgn;

> +	struct ufshpb_subregion *srgn;

> +	int bitmap_len = hpb->entries_per_srgn;

> +	int bit_len;

> +

> +next_srgn:

> +	rgn = hpb->rgn_tbl + rgn_idx;

> +	srgn = rgn->srgn_tbl + srgn_idx;

> +

> +	if (!ufshpb_is_valid_srgn(rgn, srgn))

> +		return true;

> +

> +	/*

> +	 * If the region state is active, mctx must be allocated.

> +	 * In this case, check whether the region is evicted or

> +	 * mctx allcation fail.

> +	 */

> +	WARN_ON(!srgn->mctx);

> +

> +	if ((srgn_offset + cnt) > bitmap_len)

> +		bit_len = bitmap_len - srgn_offset;

> +	else

> +		bit_len = cnt;

> +

> +	if (find_next_bit(srgn->mctx->ppn_dirty,

> +			  bit_len, srgn_offset) >= srgn_offset)

> +		return true;

> +

> +	srgn_offset = 0;

> +	if (++srgn_idx == hpb->srgns_per_rgn) {

> +		srgn_idx = 0;

> +		rgn_idx++;

> +	}

> +

> +	cnt -= bit_len;

> +	if (cnt > 0)

> +		goto next_srgn;

> +

> +	return false;

> +}

> +

> +static u64 ufshpb_get_ppn(struct ufshpb_lu *hpb,

> +			  struct ufshpb_map_ctx *mctx, int pos, int *error)

> +{

> +	u64 *ppn_table;

> +	struct page *page;

> +	int index, offset;

> +

> +	index = pos / (PAGE_SIZE / HPB_ENTRY_SIZE);

> +	offset = pos % (PAGE_SIZE / HPB_ENTRY_SIZE);

> +

> +	page = mctx->m_page[index];

> +	if (unlikely(!page)) {

> +		*error = -ENOMEM;

> +		dev_err(&hpb->sdev_ufs_lu->sdev_dev,

> +			"error. cannot find page in mctx\n");

> +		return 0;

> +	}

> +

> +	ppn_table = page_address(page);

> +	if (unlikely(!ppn_table)) {

> +		*error = -ENOMEM;

> +		dev_err(&hpb->sdev_ufs_lu->sdev_dev,

> +			"error. cannot get ppn_table\n");

> +		return 0;

> +	}

> +

> +	return ppn_table[offset];

> +}

> +

> +static void

> +ufshpb_get_pos_from_lpn(struct ufshpb_lu *hpb, unsigned long lpn, int 

> *rgn_idx,

> +			int *srgn_idx, int *offset)

> +{

> +	int rgn_offset;

> +

> +	*rgn_idx = lpn >> hpb->entries_per_rgn_shift;

> +	rgn_offset = lpn & hpb->entries_per_rgn_mask;

> +	*srgn_idx = rgn_offset >> hpb->entries_per_srgn_shift;

> +	*offset = rgn_offset & hpb->entries_per_srgn_mask;

> +}

> +

> +static void

> +ufshpb_set_hpb_read_to_upiu(struct ufshpb_lu *hpb, struct ufshcd_lrb 

> *lrbp,

> +				  u32 lpn, u64 ppn,  unsigned int transfer_len)

> +{

> +	unsigned char *cdb = lrbp->ucd_req_ptr->sc.cdb;

> +

> +	cdb[0] = UFSHPB_READ;


You are only replacing opcode in cdb[0], but ufshcd_add_command_trace() 
is
counting on lrbp->cmd->cmnd. This will lead to wrong opcode recorded by 
UFS ftrace.

Can Guo.

> +

> +	put_unaligned_be64(ppn, &cdb[6]);

> +	cdb[14] = transfer_len;

> +}

> +

> +/*

> + * This function will set up HPB read command using host-side L2P map 

> data.

> + * In HPB v1.0, maximum size of HPB read command is 4KB.

> + */

> +void ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)

> +{

> +	struct ufshpb_lu *hpb;

> +	struct ufshpb_region *rgn;

> +	struct ufshpb_subregion *srgn;

> +	struct scsi_cmnd *cmd = lrbp->cmd;

> +	u32 lpn;

> +	u64 ppn;

> +	unsigned long flags;

> +	int transfer_len, rgn_idx, srgn_idx, srgn_offset;

> +	int err = 0;

> +

> +	hpb = ufshpb_get_hpb_data(cmd->device);

> +	if (!hpb)

> +		return;

> +

> +	if (ufshpb_get_state(hpb) != HPB_PRESENT) {

> +		dev_notice(&hpb->sdev_ufs_lu->sdev_dev,

> +			   "%s: ufshpb state is not PRESENT", __func__);

> +		return;

> +	}

> +

> +	if (!ufshpb_is_write_or_discard_cmd(cmd) &&

> +	    !ufshpb_is_read_cmd(cmd))

> +		return;

> +

> +	transfer_len = sectors_to_logical(cmd->device, 

> blk_rq_sectors(cmd->request));

> +	if (unlikely(!transfer_len))

> +		return;

> +

> +	lpn = sectors_to_logical(cmd->device, blk_rq_pos(cmd->request));

> +	ufshpb_get_pos_from_lpn(hpb, lpn, &rgn_idx, &srgn_idx, &srgn_offset);

> +	rgn = hpb->rgn_tbl + rgn_idx;

> +	srgn = rgn->srgn_tbl + srgn_idx;

> +

> +	/* If command type is WRITE or DISCARD, set bitmap as drity */

> +	if (ufshpb_is_write_or_discard_cmd(cmd)) {

> +		spin_lock_irqsave(&hpb->rgn_state_lock, flags);

> +		ufshpb_set_ppn_dirty(hpb, rgn_idx, srgn_idx, srgn_offset,

> +				 transfer_len);

> +		spin_unlock_irqrestore(&hpb->rgn_state_lock, flags);

> +		return;

> +	}

> +

> +	if (!ufshpb_is_support_chunk(transfer_len))

> +		return;

> +

> +	spin_lock_irqsave(&hpb->rgn_state_lock, flags);

> +	if (ufshpb_test_ppn_dirty(hpb, rgn_idx, srgn_idx, srgn_offset,

> +				   transfer_len)) {

> +		hpb->stats.miss_cnt++;

> +		spin_unlock_irqrestore(&hpb->rgn_state_lock, flags);

> +		return;

> +	}

> +

> +	ppn = ufshpb_get_ppn(hpb, srgn->mctx, srgn_offset, &err);

> +	spin_unlock_irqrestore(&hpb->rgn_state_lock, flags);

> +	if (unlikely(err)) {

> +		/*

> +		 * In this case, the region state is active,

> +		 * but the ppn table is not allocated.

> +		 * Make sure that ppn table must be allocated on

> +		 * active state.

> +		 */

> +		WARN_ON(true);

> +		dev_err(hba->dev, "ufshpb_get_ppn failed. err %d\n", err);

> +		return;

> +	}

> +

> +	ufshpb_set_hpb_read_to_upiu(hpb, lrbp, lpn, ppn, transfer_len);

> +

> +	hpb->stats.hit_cnt++;

> +}

> +

>  static struct ufshpb_req *ufshpb_get_map_req(struct ufshpb_lu *hpb,

>  					     struct ufshpb_subregion *srgn)

>  {

> diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h

> index e40b016971ac..2c43a03b66b6 100644

> --- a/drivers/scsi/ufs/ufshpb.h

> +++ b/drivers/scsi/ufs/ufshpb.h

> @@ -198,6 +198,7 @@ struct ufs_hba;

>  struct ufshcd_lrb;

> 

>  #ifndef CONFIG_SCSI_UFS_HPB

> +static void ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) 

> {}

>  static void ufshpb_rsp_upiu(struct ufs_hba *hba, struct ufshcd_lrb 

> *lrbp) {}

>  static void ufshpb_resume(struct ufs_hba *hba) {}

>  static void ufshpb_suspend(struct ufs_hba *hba) {}

> @@ -211,6 +212,7 @@ static bool ufshpb_is_allowed(struct ufs_hba *hba)

> { return false; }

>  static void ufshpb_get_geo_info(struct ufs_hba *hba, u8 *geo_buf) {}

>  static void ufshpb_get_dev_info(struct ufs_hba *hba, u8 *desc_buf) {}

>  #else

> +void ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb *lrbp);

>  void ufshpb_rsp_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp);

>  void ufshpb_resume(struct ufs_hba *hba);

>  void ufshpb_suspend(struct ufs_hba *hba);
Daejun Park Jan. 13, 2021, 1:36 a.m. | #2
Hi Can Guo,

> > +static void

> > +ufshpb_set_hpb_read_to_upiu(struct ufshpb_lu *hpb, struct ufshcd_lrb 

> > *lrbp,

> > +				  u32 lpn, u64 ppn,  unsigned int transfer_len)

> > +{

> > +	unsigned char *cdb = lrbp->ucd_req_ptr->sc.cdb;

> > +

> > +	cdb[0] = UFSHPB_READ;

> 

> You are only replacing opcode in cdb[0], but ufshcd_add_command_trace() 

> is

> counting on lrbp->cmd->cmnd. This will lead to wrong opcode recorded by 

> UFS ftrace.

> 

You're comment is good point for improving this patch. But there is no "case" for HPB read (0xF8) in ufshcd_add_command_trace().
So I will add codes to support tracing HPB read command in ufshcd_add_command_trace() on next patch.

Thanks,
Daejun
Can Guo Jan. 14, 2021, 4:09 a.m. | #3
Hi Daejun,

On 2021-01-13 09:36, Daejun Park wrote:
> Hi Can Guo,

> 

>> > +static void

>> > +ufshpb_set_hpb_read_to_upiu(struct ufshpb_lu *hpb, struct ufshcd_lrb

>> > *lrbp,

>> > +				  u32 lpn, u64 ppn,  unsigned int transfer_len)

>> > +{

>> > +	unsigned char *cdb = lrbp->ucd_req_ptr->sc.cdb;

>> > +

>> > +	cdb[0] = UFSHPB_READ;

>> 

>> You are only replacing opcode in cdb[0], but 

>> ufshcd_add_command_trace()

>> is

>> counting on lrbp->cmd->cmnd. This will lead to wrong opcode recorded 

>> by

>> UFS ftrace.

>> 

> You're comment is good point for improving this patch. But there is no

> "case" for HPB read (0xF8) in ufshcd_add_command_trace().

> So I will add codes to support tracing HPB read command in

> ufshcd_add_command_trace() on next patch.

> 


It is not just about ftrace. If HPB READ cmd fails with sense key infos.
When SCSI layer prints the cmd, it still prints the READ(10) CDB, which 
is
misleading.

Thanks,
Can Guo.

> Thanks,

> Daejun
Daejun Park Jan. 15, 2021, 7:59 a.m. | #4
Hi Can Guo,

> 

> On 2021-01-13 09:36, Daejun Park wrote:

> > Hi Can Guo,

> > 

> >> > +static void

> >> > +ufshpb_set_hpb_read_to_upiu(struct ufshpb_lu *hpb, struct ufshcd_lrb

> >> > *lrbp,

> >> > +				  u32 lpn, u64 ppn,  unsigned int transfer_len)

> >> > +{

> >> > +	unsigned char *cdb = lrbp->ucd_req_ptr->sc.cdb;

> >> > +

> >> > +	cdb[0] = UFSHPB_READ;

> >> 

> >> You are only replacing opcode in cdb[0], but 

> >> ufshcd_add_command_trace()

> >> is

> >> counting on lrbp->cmd->cmnd. This will lead to wrong opcode recorded 

> >> by

> >> UFS ftrace.

> >> 

> > You're comment is good point for improving this patch. But there is no

> > "case" for HPB read (0xF8) in ufshcd_add_command_trace().

> > So I will add codes to support tracing HPB read command in

> > ufshcd_add_command_trace() on next patch.

> > 

> 

> It is not just about ftrace. If HPB READ cmd fails with sense key infos.

> When SCSI layer prints the cmd, it still prints the READ(10) CDB, which 

> is

> misleading.


Oh, thanks. I will fix this problem on next patch.


Daejun

Patch

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 0ec0ed237140..41554afead63 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -2600,6 +2600,8 @@  static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
 
 	ufshcd_comp_scsi_upiu(hba, lrbp);
 
+	ufshpb_prep(hba, lrbp);
+
 	err = ufshcd_map_sg(hba, lrbp);
 	if (err) {
 		lrbp->cmd = NULL;
diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 37ae61fbf36a..d3e6c5b32328 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -31,6 +31,29 @@  bool ufshpb_is_allowed(struct ufs_hba *hba)
 	return !(hba->ufshpb_dev.hpb_disabled);
 }
 
+static int ufshpb_is_valid_srgn(struct ufshpb_region *rgn,
+			     struct ufshpb_subregion *srgn)
+{
+	return rgn->rgn_state != HPB_RGN_INACTIVE &&
+		srgn->srgn_state == HPB_SRGN_VALID;
+}
+
+static bool ufshpb_is_read_cmd(struct scsi_cmnd *cmd)
+{
+	return req_op(cmd->request) == REQ_OP_READ;
+}
+
+static bool ufshpb_is_write_or_discard_cmd(struct scsi_cmnd *cmd)
+{
+	return op_is_write(req_op(cmd->request)) ||
+	       op_is_discard(req_op(cmd->request));
+}
+
+static bool ufshpb_is_support_chunk(int transfer_len)
+{
+	return transfer_len <= HPB_MULTI_CHUNK_HIGH;
+}
+
 static bool ufshpb_is_general_lun(int lun)
 {
 	return lun < UFS_UPIU_MAX_UNIT_NUM_ID;
@@ -98,6 +121,215 @@  static void ufshpb_set_state(struct ufshpb_lu *hpb, int state)
 	atomic_set(&hpb->hpb_state, state);
 }
 
+static void ufshpb_set_ppn_dirty(struct ufshpb_lu *hpb, int rgn_idx,
+			     int srgn_idx, int srgn_offset, int cnt)
+{
+	struct ufshpb_region *rgn;
+	struct ufshpb_subregion *srgn;
+	int set_bit_len;
+	int bitmap_len = hpb->entries_per_srgn;
+
+next_srgn:
+	rgn = hpb->rgn_tbl + rgn_idx;
+	srgn = rgn->srgn_tbl + srgn_idx;
+
+	if ((srgn_offset + cnt) > bitmap_len)
+		set_bit_len = bitmap_len - srgn_offset;
+	else
+		set_bit_len = cnt;
+
+	if (rgn->rgn_state != HPB_RGN_INACTIVE &&
+	    srgn->srgn_state == HPB_SRGN_VALID)
+		bitmap_set(srgn->mctx->ppn_dirty, srgn_offset, set_bit_len);
+
+	srgn_offset = 0;
+	if (++srgn_idx == hpb->srgns_per_rgn) {
+		srgn_idx = 0;
+		rgn_idx++;
+	}
+
+	cnt -= set_bit_len;
+	if (cnt > 0)
+		goto next_srgn;
+
+	WARN_ON(cnt < 0);
+}
+
+static bool ufshpb_test_ppn_dirty(struct ufshpb_lu *hpb, int rgn_idx,
+				   int srgn_idx, int srgn_offset, int cnt)
+{
+	struct ufshpb_region *rgn;
+	struct ufshpb_subregion *srgn;
+	int bitmap_len = hpb->entries_per_srgn;
+	int bit_len;
+
+next_srgn:
+	rgn = hpb->rgn_tbl + rgn_idx;
+	srgn = rgn->srgn_tbl + srgn_idx;
+
+	if (!ufshpb_is_valid_srgn(rgn, srgn))
+		return true;
+
+	/*
+	 * If the region state is active, mctx must be allocated.
+	 * In this case, check whether the region is evicted or
+	 * mctx allcation fail.
+	 */
+	WARN_ON(!srgn->mctx);
+
+	if ((srgn_offset + cnt) > bitmap_len)
+		bit_len = bitmap_len - srgn_offset;
+	else
+		bit_len = cnt;
+
+	if (find_next_bit(srgn->mctx->ppn_dirty,
+			  bit_len, srgn_offset) >= srgn_offset)
+		return true;
+
+	srgn_offset = 0;
+	if (++srgn_idx == hpb->srgns_per_rgn) {
+		srgn_idx = 0;
+		rgn_idx++;
+	}
+
+	cnt -= bit_len;
+	if (cnt > 0)
+		goto next_srgn;
+
+	return false;
+}
+
+static u64 ufshpb_get_ppn(struct ufshpb_lu *hpb,
+			  struct ufshpb_map_ctx *mctx, int pos, int *error)
+{
+	u64 *ppn_table;
+	struct page *page;
+	int index, offset;
+
+	index = pos / (PAGE_SIZE / HPB_ENTRY_SIZE);
+	offset = pos % (PAGE_SIZE / HPB_ENTRY_SIZE);
+
+	page = mctx->m_page[index];
+	if (unlikely(!page)) {
+		*error = -ENOMEM;
+		dev_err(&hpb->sdev_ufs_lu->sdev_dev,
+			"error. cannot find page in mctx\n");
+		return 0;
+	}
+
+	ppn_table = page_address(page);
+	if (unlikely(!ppn_table)) {
+		*error = -ENOMEM;
+		dev_err(&hpb->sdev_ufs_lu->sdev_dev,
+			"error. cannot get ppn_table\n");
+		return 0;
+	}
+
+	return ppn_table[offset];
+}
+
+static void
+ufshpb_get_pos_from_lpn(struct ufshpb_lu *hpb, unsigned long lpn, int *rgn_idx,
+			int *srgn_idx, int *offset)
+{
+	int rgn_offset;
+
+	*rgn_idx = lpn >> hpb->entries_per_rgn_shift;
+	rgn_offset = lpn & hpb->entries_per_rgn_mask;
+	*srgn_idx = rgn_offset >> hpb->entries_per_srgn_shift;
+	*offset = rgn_offset & hpb->entries_per_srgn_mask;
+}
+
+static void
+ufshpb_set_hpb_read_to_upiu(struct ufshpb_lu *hpb, struct ufshcd_lrb *lrbp,
+				  u32 lpn, u64 ppn,  unsigned int transfer_len)
+{
+	unsigned char *cdb = lrbp->ucd_req_ptr->sc.cdb;
+
+	cdb[0] = UFSHPB_READ;
+
+	put_unaligned_be64(ppn, &cdb[6]);
+	cdb[14] = transfer_len;
+}
+
+/*
+ * This function will set up HPB read command using host-side L2P map data.
+ * In HPB v1.0, maximum size of HPB read command is 4KB.
+ */
+void ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
+{
+	struct ufshpb_lu *hpb;
+	struct ufshpb_region *rgn;
+	struct ufshpb_subregion *srgn;
+	struct scsi_cmnd *cmd = lrbp->cmd;
+	u32 lpn;
+	u64 ppn;
+	unsigned long flags;
+	int transfer_len, rgn_idx, srgn_idx, srgn_offset;
+	int err = 0;
+
+	hpb = ufshpb_get_hpb_data(cmd->device);
+	if (!hpb)
+		return;
+
+	if (ufshpb_get_state(hpb) != HPB_PRESENT) {
+		dev_notice(&hpb->sdev_ufs_lu->sdev_dev,
+			   "%s: ufshpb state is not PRESENT", __func__);
+		return;
+	}
+
+	if (!ufshpb_is_write_or_discard_cmd(cmd) &&
+	    !ufshpb_is_read_cmd(cmd))
+		return;
+
+	transfer_len = sectors_to_logical(cmd->device, blk_rq_sectors(cmd->request));
+	if (unlikely(!transfer_len))
+		return;
+
+	lpn = sectors_to_logical(cmd->device, blk_rq_pos(cmd->request));
+	ufshpb_get_pos_from_lpn(hpb, lpn, &rgn_idx, &srgn_idx, &srgn_offset);
+	rgn = hpb->rgn_tbl + rgn_idx;
+	srgn = rgn->srgn_tbl + srgn_idx;
+
+	/* If command type is WRITE or DISCARD, set bitmap as drity */
+	if (ufshpb_is_write_or_discard_cmd(cmd)) {
+		spin_lock_irqsave(&hpb->rgn_state_lock, flags);
+		ufshpb_set_ppn_dirty(hpb, rgn_idx, srgn_idx, srgn_offset,
+				 transfer_len);
+		spin_unlock_irqrestore(&hpb->rgn_state_lock, flags);
+		return;
+	}
+
+	if (!ufshpb_is_support_chunk(transfer_len))
+		return;
+
+	spin_lock_irqsave(&hpb->rgn_state_lock, flags);
+	if (ufshpb_test_ppn_dirty(hpb, rgn_idx, srgn_idx, srgn_offset,
+				   transfer_len)) {
+		hpb->stats.miss_cnt++;
+		spin_unlock_irqrestore(&hpb->rgn_state_lock, flags);
+		return;
+	}
+
+	ppn = ufshpb_get_ppn(hpb, srgn->mctx, srgn_offset, &err);
+	spin_unlock_irqrestore(&hpb->rgn_state_lock, flags);
+	if (unlikely(err)) {
+		/*
+		 * In this case, the region state is active,
+		 * but the ppn table is not allocated.
+		 * Make sure that ppn table must be allocated on
+		 * active state.
+		 */
+		WARN_ON(true);
+		dev_err(hba->dev, "ufshpb_get_ppn failed. err %d\n", err);
+		return;
+	}
+
+	ufshpb_set_hpb_read_to_upiu(hpb, lrbp, lpn, ppn, transfer_len);
+
+	hpb->stats.hit_cnt++;
+}
+
 static struct ufshpb_req *ufshpb_get_map_req(struct ufshpb_lu *hpb,
 					     struct ufshpb_subregion *srgn)
 {
diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
index e40b016971ac..2c43a03b66b6 100644
--- a/drivers/scsi/ufs/ufshpb.h
+++ b/drivers/scsi/ufs/ufshpb.h
@@ -198,6 +198,7 @@  struct ufs_hba;
 struct ufshcd_lrb;
 
 #ifndef CONFIG_SCSI_UFS_HPB
+static void ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) {}
 static void ufshpb_rsp_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) {}
 static void ufshpb_resume(struct ufs_hba *hba) {}
 static void ufshpb_suspend(struct ufs_hba *hba) {}
@@ -211,6 +212,7 @@  static bool ufshpb_is_allowed(struct ufs_hba *hba) { return false; }
 static void ufshpb_get_geo_info(struct ufs_hba *hba, u8 *geo_buf) {}
 static void ufshpb_get_dev_info(struct ufs_hba *hba, u8 *desc_buf) {}
 #else
+void ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb *lrbp);
 void ufshpb_rsp_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp);
 void ufshpb_resume(struct ufs_hba *hba);
 void ufshpb_suspend(struct ufs_hba *hba);