lkml.org 
[lkml]   [2020]   [Jun]   [15]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Subject[RFC PATCH v2 5/5] scsi: ufs: Prepare HPB read for cached sub-region
    From
    Date
    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.

    Signed-off-by: Daejun Park <daejun7.park@samsung.com>
    ---
    drivers/scsi/ufs/ufshpb.c | 234 ++++++++++++++++++++++++++++++++++++++
    1 file changed, 234 insertions(+)

    diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
    index 6229c6fcb5e1..848f51e9a1a1 100644
    --- a/drivers/scsi/ufs/ufshpb.c
    +++ b/drivers/scsi/ufs/ufshpb.c
    @@ -25,6 +25,22 @@ static inline int ufshpb_is_valid_srgn(struct ufshpb_region *rgn,
    srgn->srgn_state == HPB_SRGN_VALID;
    }

    +static inline bool ufshpb_is_read_cmd(struct scsi_cmnd *cmd)
    +{
    + return req_op(cmd->request) == REQ_OP_READ;
    +}
    +
    +static inline bool ufshpb_is_write_discard_cmd(struct scsi_cmnd *cmd)
    +{
    + return op_is_write(req_op(cmd->request)) ||
    + op_is_discard(req_op(cmd->request));
    +}
    +
    +static inline bool ufshpb_is_support_chunk(int transfer_len)
    +{
    + return transfer_len <= HPB_MULTI_CHUNK_HIGH;
    +}
    +
    static inline bool ufshpb_is_general_lun(int lun)
    {
    return lun < UFS_UPIU_MAX_UNIT_NUM_ID;
    @@ -116,6 +132,224 @@ static inline void ufshpb_lu_put(struct ufshpb_lu *hpb)
    put_device(&hpb->hpb_lu_dev);
    }

    +static inline u32 ufshpb_get_lpn(struct scsi_cmnd *cmnd)
    +{
    + return blk_rq_pos(cmnd->request) >>
    + (ilog2(cmnd->device->sector_size) - 9);
    +}
    +
    +static inline unsigned int ufshpb_get_len(struct scsi_cmnd *cmnd)
    +{
    + return blk_rq_sectors(cmnd->request) >>
    + (ilog2(cmnd->device->sector_size) - 9);
    +}
    +
    +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->hpb_lu_dev,
    + "error. cannot find page in mctx\n");
    + return 0;
    + }
    +
    + ppn_table = page_address(page);
    + if (unlikely(!ppn_table)) {
    + *error = -ENOMEM;
    + dev_err(&hpb->hpb_lu_dev, "error. cannot get ppn_table\n");
    + return 0;
    + }
    +
    + return ppn_table[offset];
    +}
    +
    +static inline 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;
    +}
    +
    +/* routine : READ10 -> HPB_READ */
    +static void ufshpb_prep_fn(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);
    + err = ufshpb_lu_get(hpb);
    + if (unlikely(err))
    + return;
    +
    + WARN_ON(hpb->lun != cmd->device->lun);
    + if (!ufshpb_is_write_discard_cmd(cmd) &&
    + !ufshpb_is_read_cmd(cmd))
    + goto put_hpb;
    +
    + transfer_len = ufshpb_get_len(cmd);
    + if (unlikely(!transfer_len))
    + goto put_hpb;
    +
    + lpn = ufshpb_get_lpn(cmd);
    + 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_discard_cmd(cmd)) {
    + spin_lock_irqsave(&hpb->hpb_state_lock, flags);
    + ufshpb_set_ppn_dirty(hpb, rgn_idx, srgn_idx, srgn_offset,
    + transfer_len);
    + spin_unlock_irqrestore(&hpb->hpb_state_lock, flags);
    + goto put_hpb;
    + }
    +
    + WARN_ON(!ufshpb_is_read_cmd(cmd));
    +
    + if (!ufshpb_is_support_chunk(transfer_len))
    + goto put_hpb;
    +
    + spin_lock_irqsave(&hpb->hpb_state_lock, flags);
    + if (ufshpb_test_ppn_dirty(hpb, rgn_idx, srgn_idx, srgn_offset,
    + transfer_len)) {
    + atomic_inc(&hpb->stats.miss_cnt);
    + spin_unlock_irqrestore(&hpb->hpb_state_lock, flags);
    + goto put_hpb;
    + }
    +
    + ppn = ufshpb_get_ppn(hpb, srgn->mctx, srgn_offset, &err);
    + spin_unlock_irqrestore(&hpb->hpb_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(&hpb->hpb_lu_dev,
    + "ufshpb_get_ppn failed. err %d\n", err);
    + goto put_hpb;
    + }
    +
    + ufshpb_set_hpb_read_to_upiu(hpb, lrbp, lpn, ppn, transfer_len);
    +
    + atomic_inc(&hpb->stats.hit_cnt);
    +put_hpb:
    + ufshpb_lu_put(hpb);
    +}
    +
    static struct ufshpb_req *ufshpb_get_map_req(struct ufshpb_lu *hpb,
    struct ufshpb_subregion *srgn)
    {
    --
    2.17.1

    \
     
     \ /
      Last update: 2020-06-15 11:50    [W:5.059 / U:0.240 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site