Skip to content

Commit

Permalink
scsi: ufs: ufshpb: Add reads counter
Browse files Browse the repository at this point in the history
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.

Link: https://lore.kernel.org/r/20210712095039.8093-5-avri.altman@wdc.com
Reviewed-by: Daejun Park <daejun7.park@samsung.com>
Signed-off-by: Avri Altman <avri.altman@wdc.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
  • Loading branch information
Avri Altman authored and Martin K. Petersen committed Aug 1, 2021
1 parent 8becf4d commit c76a188
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 6 deletions.
94 changes: 88 additions & 6 deletions drivers/scsi/ufs/ufshpb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -149,7 +154,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;
Expand All @@ -168,15 +173,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->rgn_flags);

spin_lock_irqsave(&hpb->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(&hpb->rgn_state_lock, flags);

if (hpb->is_hcm && prev_srgn != srgn) {
bool activate = false;

spin_lock(&rgn->rgn_lock);
if (set_dirty) {
rgn->reads -= srgn->reads;
srgn->reads = 0;
set_bit(RGN_FLAG_DIRTY, &rgn->rgn_flags);
} else {
srgn->reads++;
rgn->reads++;
if (srgn->reads == ACTIVATION_THRESHOLD)
activate = true;
}
spin_unlock(&rgn->rgn_lock);

if (activate) {
spin_lock_irqsave(&hpb->rsp_list_lock, flags);
ufshpb_update_active_info(hpb, rgn_idx, srgn_idx);
spin_unlock_irqrestore(&hpb->rsp_list_lock, flags);
dev_dbg(&hpb->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;
Expand Down Expand Up @@ -605,6 +634,19 @@ int ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)

WARN_ON_ONCE(transfer_len > HPB_MULTI_CHUNK_HIGH);

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(&hpb->ufshpb_normalization_work);
}

spin_lock_irqsave(&hpb->rgn_state_lock, flags);
if (ufshpb_test_ppn_dirty(hpb, rgn_idx, srgn_idx, srgn_offset,
transfer_len)) {
Expand Down Expand Up @@ -756,6 +798,8 @@ static void ufshpb_update_active_info(struct ufshpb_lu *hpb, int rgn_idx,

if (list_empty(&srgn->list_act_srgn))
list_add_tail(&srgn->list_act_srgn, &hpb->lh_act_srgn);

hpb->stats.rb_active_cnt++;
}

static void ufshpb_update_inactive_info(struct ufshpb_lu *hpb, int rgn_idx)
Expand All @@ -771,6 +815,8 @@ static void ufshpb_update_inactive_info(struct ufshpb_lu *hpb, int rgn_idx)

if (list_empty(&rgn->list_inact_rgn))
list_add_tail(&rgn->list_inact_rgn, &hpb->lh_inact_rgn);

hpb->stats.rb_inactive_cnt++;
}

static void ufshpb_activate_subregion(struct ufshpb_lu *hpb,
Expand Down Expand Up @@ -1084,6 +1130,7 @@ static int ufshpb_evict_region(struct ufshpb_lu *hpb, struct ufshpb_region *rgn)
rgn->rgn_idx);
goto out;
}

if (!list_empty(&rgn->list_lru_rgn)) {
if (ufshpb_check_srgns_issue_state(hpb, rgn)) {
ret = -EBUSY;
Expand Down Expand Up @@ -1278,7 +1325,6 @@ static void ufshpb_rsp_req_region_update(struct ufshpb_lu *hpb,
if (srgn->srgn_state == HPB_SRGN_VALID)
srgn->srgn_state = HPB_SRGN_INVALID;
spin_unlock(&hpb->rgn_state_lock);
hpb->stats.rb_active_cnt++;
}

if (hpb->is_hcm) {
Expand Down Expand Up @@ -1310,7 +1356,6 @@ static void ufshpb_rsp_req_region_update(struct ufshpb_lu *hpb,
}
spin_unlock(&hpb->rgn_state_lock);

hpb->stats.rb_inactive_cnt++;
}

out:
Expand Down Expand Up @@ -1509,6 +1554,36 @@ static void ufshpb_run_inactive_region_list(struct ufshpb_lu *hpb)
spin_unlock_irqrestore(&hpb->rsp_list_lock, flags);
}

static void ufshpb_normalization_work_handler(struct work_struct *work)
{
struct ufshpb_lu *hpb = container_of(work, struct ufshpb_lu,
ufshpb_normalization_work);
int rgn_idx;

for (rgn_idx = 0; rgn_idx < hpb->rgns_per_lu; rgn_idx++) {
struct ufshpb_region *rgn = hpb->rgn_tbl + rgn_idx;
int srgn_idx;

spin_lock(&rgn->rgn_lock);
rgn->reads = 0;
for (srgn_idx = 0; srgn_idx < hpb->srgns_per_rgn; srgn_idx++) {
struct ufshpb_subregion *srgn = rgn->srgn_tbl + srgn_idx;

srgn->reads >>= 1;
rgn->reads += srgn->reads;
}
spin_unlock(&rgn->rgn_lock);

if (rgn->rgn_state != HPB_RGN_ACTIVE || rgn->reads)
continue;

/* if region is active but has no reads - inactivate it */
spin_lock(&hpb->rsp_list_lock);
ufshpb_update_inactive_info(hpb, rgn->rgn_idx);
spin_unlock(&hpb->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);
Expand Down Expand Up @@ -1667,6 +1742,8 @@ static int ufshpb_alloc_region_tbl(struct ufs_hba *hba, struct ufshpb_lu *hpb)
rgn = rgn_table + rgn_idx;
rgn->rgn_idx = rgn_idx;

spin_lock_init(&rgn->rgn_lock);

INIT_LIST_HEAD(&rgn->list_inact_rgn);
INIT_LIST_HEAD(&rgn->list_lru_rgn);

Expand Down Expand Up @@ -1908,6 +1985,9 @@ static int ufshpb_lu_hpb_init(struct ufs_hba *hba, struct ufshpb_lu *hpb)
INIT_LIST_HEAD(&hpb->list_hpb_lu);

INIT_WORK(&hpb->map_work, ufshpb_map_work_handler);
if (hpb->is_hcm)
INIT_WORK(&hpb->ufshpb_normalization_work,
ufshpb_normalization_work_handler);

hpb->map_req_cache = kmem_cache_create("ufshpb_req_cache",
sizeof(struct ufshpb_req), 0, 0, NULL);
Expand Down Expand Up @@ -2007,6 +2087,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(&hpb->ufshpb_normalization_work);
cancel_work_sync(&hpb->map_work);
}

Expand Down
9 changes: 9 additions & 0 deletions drivers/scsi/ufs/ufshpb.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ struct ufshpb_subregion {
int rgn_idx;
int srgn_idx;
bool is_last;

/* subregion reads - for host mode */
unsigned int reads;

/* below information is used by rsp_list */
struct list_head list_act_srgn;
};
Expand All @@ -123,6 +127,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) \
Expand Down Expand Up @@ -212,6 +220,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;
Expand Down

0 comments on commit c76a188

Please sign in to comment.