diff mbox series

[v2,6/9] scsi: ufshpb: Add hpb dev reset response

Message ID 20210202083007.104050-7-avri.altman@wdc.com
State New
Headers show
Series Add Host control mode to HPB | expand

Commit Message

Avri Altman Feb. 2, 2021, 8:30 a.m. UTC
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.altman@wdc.com>
---
 drivers/scsi/ufs/ufshpb.c | 54 ++++++++++++++++++++++++++++++++++++---
 drivers/scsi/ufs/ufshpb.h |  1 +
 2 files changed, 52 insertions(+), 3 deletions(-)

Comments

Can Guo Feb. 8, 2021, 6:34 a.m. UTC | #1
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.altman@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 */
Avri Altman Feb. 8, 2021, 7:50 a.m. UTC | #2
> > +             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.

Yayks Ouch.  Will fix.

Thanks,
Avri
diff mbox series

Patch

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) {
+				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 */