There are some limitations to activations / inactivations in host control mode - Add those. Signed-off-by: Avri Altman <avri.altman@xxxxxxx> --- drivers/scsi/ufs/ufshpb.c | 35 +++++++++++++++++++++++++++++++++++ drivers/scsi/ufs/ufshpb.h | 6 ++++++ 2 files changed, 41 insertions(+) diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c index 183bdf35f2d0..5fa1f5bc08e6 100644 --- a/drivers/scsi/ufs/ufshpb.c +++ b/drivers/scsi/ufs/ufshpb.c @@ -140,6 +140,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->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); @@ -201,6 +203,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->rgn_flags); +} + static u64 ufshpb_get_ppn(struct ufshpb_lu *hpb, struct ufshpb_map_ctx *mctx, int pos, int *error) { @@ -380,8 +387,12 @@ 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; + WARN_ON(!srgn->mctx); bitmap_zero(srgn->mctx->ppn_dirty, hpb->entries_per_srgn); + rgn = hpb->rgn_tbl + srgn->rgn_idx; + clear_bit(RGN_FLAG_DIRTY, &rgn->rgn_flags); return 0; } @@ -814,17 +825,39 @@ static void ufshpb_rsp_req_region_update(struct ufshpb_lu *hpb, */ spin_lock(&hpb->rsp_list_lock); for (i = 0; i < rsp_field->active_rgn_cnt; i++) { + struct ufshpb_region *rgn; + rgn_idx = be16_to_cpu(rsp_field->hpb_active_field[i].active_rgn); srgn_idx = be16_to_cpu(rsp_field->hpb_active_field[i].active_srgn); + rgn = hpb->rgn_tbl + rgn_idx; + if (ufshpb_mode == HPB_HOST_CONTROL && + (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(&hpb->sdev_ufs_lu->sdev_dev, "activate(%d) region %d - %d\n", i, rgn_idx, srgn_idx); ufshpb_update_active_info(hpb, rgn_idx, srgn_idx); hpb->stats.rb_active_cnt++; } + if (ufshpb_mode == HPB_HOST_CONTROL) { + /* + * in host control mode the device is not allowed to inactivate + * regions + */ + goto out_unlock; + } + for (i = 0; i < rsp_field->inactive_rgn_cnt; i++) { rgn_idx = be16_to_cpu(rsp_field->hpb_inactive_field[i]); dev_dbg(&hpb->sdev_ufs_lu->sdev_dev, @@ -832,6 +865,8 @@ static void ufshpb_rsp_req_region_update(struct ufshpb_lu *hpb, ufshpb_update_inactive_info(hpb, rgn_idx); hpb->stats.rb_inactive_cnt++; } + +out_unlock: spin_unlock(&hpb->rsp_list_lock); dev_dbg(&hpb->sdev_ufs_lu->sdev_dev, "Noti: #ACT %u #INACT %u\n", diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h index 2c43a03b66b6..8a34b0f42754 100644 --- a/drivers/scsi/ufs/ufshpb.h +++ b/drivers/scsi/ufs/ufshpb.h @@ -48,6 +48,11 @@ enum UFSHPB_MODE { HPB_DEVICE_CONTROL, }; +enum HPB_RGN_FLAGS { + RGN_FLAG_UPDATE = 0, + RGN_FLAG_DIRTY, +}; + enum UFSHPB_STATE { HPB_PRESENT = 1, HPB_SUSPEND, @@ -109,6 +114,7 @@ struct ufshpb_region { /* below information is used by lru */ struct list_head list_lru_rgn; + unsigned long rgn_flags; }; #define for_each_sub_region(rgn, i, srgn) \ -- 2.25.1