@@ -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",
@@ -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) \
There are some limitations to activations / inactivations in host control mode - Add those. Signed-off-by: Avri Altman <avri.altman@wdc.com> --- drivers/scsi/ufs/ufshpb.c | 35 +++++++++++++++++++++++++++++++++++ drivers/scsi/ufs/ufshpb.h | 6 ++++++ 2 files changed, 41 insertions(+)