On 2021-02-02 16:30, Avri Altman wrote:
The spec does not define what is the host's recommended response when
the device send hpb dev reset response (oper 0x2).

We will update all active hpb regions: mark them and do that on the next
read.

Signed-off-by: Avri Altman <avri.alt...@wdc.com>
---
 drivers/scsi/ufs/ufshpb.c | 54 ++++++++++++++++++++++++++++++++++++---
 drivers/scsi/ufs/ufshpb.h |  1 +
 2 files changed, 52 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c
index 49c74de539b7..28e0025507a1 100644
--- a/drivers/scsi/ufs/ufshpb.c
+++ b/drivers/scsi/ufs/ufshpb.c
@@ -17,6 +17,7 @@
 #include "../sd.h"

 #define WORK_PENDING 0
+#define RESET_PENDING 1
 #define ACTIVATION_THRSHLD 4 /* 4 IOs */
 #define EVICTION_THRSHLD (ACTIVATION_THRSHLD << 6) /* 256 IOs */

@@ -349,7 +350,8 @@ void ufshpb_prep(struct ufs_hba *hba, struct
ufshcd_lrb *lrbp)
                if (rgn->reads == ACTIVATION_THRSHLD)
                        activate = true;
                spin_unlock_irqrestore(&rgn->rgn_lock, flags);
-               if (activate) {
+               if (activate ||
+                   test_and_clear_bit(RGN_FLAG_UPDATE, &rgn->rgn_flags)) {
                        spin_lock_irqsave(&hpb->rsp_list_lock, flags);
                        ufshpb_update_active_info(hpb, rgn_idx, srgn_idx);
                        hpb->stats.rb_active_cnt++;
@@ -1068,6 +1070,24 @@ void ufshpb_rsp_upiu(struct ufs_hba *hba,
struct ufshcd_lrb *lrbp)
        case HPB_RSP_DEV_RESET:
                dev_warn(&hpb->sdev_ufs_lu->sdev_dev,
                         "UFS device lost HPB information during PM.\n");
+
+               if (hpb->is_hcm) {
+                       struct ufshpb_lu *h;
+                       struct scsi_device *sdev;
+
+                       shost_for_each_device(sdev, hba->host) {

I haven't test it yet, but this line shall cause recursive spin lock -
in current code base, ufshpb_rsp_upiu() is called with host_lock held.

Regards,

Can Guo.

+                               h = sdev->hostdata;
+                               if (!h)
+                                       continue;
+
+                               if (test_and_set_bit(RESET_PENDING,
+                                                    &h->work_data_bits))
+                                       continue;
+
+                               schedule_work(&h->ufshpb_lun_reset_work);
+                       }
+               }
+
                break;
        default:
                dev_notice(&hpb->sdev_ufs_lu->sdev_dev,
@@ -1200,6 +1220,27 @@ static void
ufshpb_run_inactive_region_list(struct ufshpb_lu *hpb)
        spin_unlock_irqrestore(&hpb->rsp_list_lock, flags);
 }

+static void ufshpb_reset_work_handler(struct work_struct *work)
+{
+       struct ufshpb_lu *hpb;
+       struct victim_select_info *lru_info;
+       struct ufshpb_region *rgn;
+       unsigned long flags;
+
+       hpb = container_of(work, struct ufshpb_lu, ufshpb_lun_reset_work);
+
+       lru_info = &hpb->lru_info;
+
+       spin_lock_irqsave(&hpb->rgn_state_lock, flags);
+
+       list_for_each_entry(rgn, &lru_info->lh_lru_rgn, list_lru_rgn)
+               set_bit(RGN_FLAG_UPDATE, &rgn->rgn_flags);
+
+       spin_unlock_irqrestore(&hpb->rgn_state_lock, flags);
+
+       clear_bit(RESET_PENDING, &hpb->work_data_bits);
+}
+
static void ufshpb_normalization_work_handler(struct work_struct *work)
 {
        struct ufshpb_lu *hpb;
@@ -1392,6 +1433,8 @@ static int ufshpb_alloc_region_tbl(struct
ufs_hba *hba, struct ufshpb_lu *hpb)
                } else {
                        rgn->rgn_state = HPB_RGN_INACTIVE;
                }
+
+               rgn->rgn_flags = 0;
        }

        return 0;
@@ -1502,9 +1545,12 @@ 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)
+       if (hpb->is_hcm) {
                INIT_WORK(&hpb->ufshpb_normalization_work,
                          ufshpb_normalization_work_handler);
+               INIT_WORK(&hpb->ufshpb_lun_reset_work,
+                         ufshpb_reset_work_handler);
+       }

        hpb->map_req_cache = kmem_cache_create("ufshpb_req_cache",
                          sizeof(struct ufshpb_req), 0, 0, NULL);
@@ -1591,8 +1637,10 @@ static void ufshpb_discard_rsp_lists(struct
ufshpb_lu *hpb)

 static void ufshpb_cancel_jobs(struct ufshpb_lu *hpb)
 {
-       if (hpb->is_hcm)
+       if (hpb->is_hcm) {
+               cancel_work_sync(&hpb->ufshpb_lun_reset_work);
                cancel_work_sync(&hpb->ufshpb_normalization_work);
+       }
        cancel_work_sync(&hpb->map_work);
 }

diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h
index 71b082ee7876..e55892ceb3fc 100644
--- a/drivers/scsi/ufs/ufshpb.h
+++ b/drivers/scsi/ufs/ufshpb.h
@@ -184,6 +184,7 @@ struct ufshpb_lu {
        /* for selecting victim */
        struct victim_select_info lru_info;
        struct work_struct ufshpb_normalization_work;
+       struct work_struct ufshpb_lun_reset_work;
        unsigned long work_data_bits;

        /* pinned region information */

Reply via email to