diff mbox series

[1/4] scsi: ufs: core: Introduce a new host register lock

Message ID 20241021120313.493371-2-avri.altman@wdc.com (mailing list archive)
State Superseded
Headers show
Series Untie the host lock entanglement - part 1 | expand

Commit Message

Avri Altman Oct. 21, 2024, 12:03 p.m. UTC
Introduce a new host register read/write lock.  Use it to protect access
to the task management doorbell register: UTMRLDBR.  This is not the
UTRLDBR which is already protected by its own outstanding_lock.

Signed-off-by: Avri Altman <avri.altman@wdc.com>
---
 drivers/ufs/core/ufshcd.c | 26 ++++++++++++++++++--------
 include/ufs/ufshcd.h      |  3 +++
 2 files changed, 21 insertions(+), 8 deletions(-)

Comments

Bart Van Assche Oct. 21, 2024, 6:35 p.m. UTC | #1
On 10/21/24 5:03 AM, Avri Altman wrote:
> Introduce a new host register read/write lock.  Use it to protect access
> to the task management doorbell register: UTMRLDBR.  This is not the
> UTRLDBR which is already protected by its own outstanding_lock.

Why is this necessary? I think it is allowed to submit host controller
reads and writes simultaneously from different threads. Only 
read/modify/writes have to be serialized.

Thanks,

Bart.
Avri Altman Oct. 22, 2024, 6:02 a.m. UTC | #2
> On 10/21/24 5:03 AM, Avri Altman wrote:
> > Introduce a new host register read/write lock.  Use it to protect
> > access to the task management doorbell register: UTMRLDBR.  This is
> > not the UTRLDBR which is already protected by its own outstanding_lock.
> 
> Why is this necessary? I think it is allowed to submit host controller reads
> and writes simultaneously from different threads. Only read/modify/writes
> have to be serialized.
Done.

> 
> Thanks,
> 
> Bart.
diff mbox series

Patch

diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 9e6d008f4ea4..21eda055fb7d 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -1245,11 +1245,13 @@  static bool ufshcd_is_devfreq_scaling_required(struct ufs_hba *hba,
 static u32 ufshcd_pending_cmds(struct ufs_hba *hba)
 {
 	const struct scsi_device *sdev;
+	unsigned long flags;
 	u32 pending = 0;
 
-	lockdep_assert_held(hba->host->host_lock);
+	spin_lock_irqsave(hba->host->host_lock, flags);
 	__shost_for_each_device(sdev, hba->host)
 		pending += sbitmap_weight(&sdev->budget_map);
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
 
 	return pending;
 }
@@ -1271,7 +1273,6 @@  static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
 	ktime_t start;
 
 	ufshcd_hold(hba);
-	spin_lock_irqsave(hba->host->host_lock, flags);
 	/*
 	 * Wait for all the outstanding tasks/transfer requests.
 	 * Verify by checking the doorbell registers are clear.
@@ -1283,7 +1284,10 @@  static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
 			goto out;
 		}
 
+		spin_lock_irqsave(&hba->reg_lock, flags);
 		tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL);
+		spin_unlock_irqrestore(&hba->reg_lock, flags);
+
 		tr_pending = ufshcd_pending_cmds(hba);
 		if (!tm_doorbell && !tr_pending) {
 			timeout = false;
@@ -1292,7 +1296,6 @@  static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
 			break;
 		}
 
-		spin_unlock_irqrestore(hba->host->host_lock, flags);
 		io_schedule_timeout(msecs_to_jiffies(20));
 		if (ktime_to_us(ktime_sub(ktime_get(), start)) >
 		    wait_timeout_us) {
@@ -1304,7 +1307,6 @@  static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
 			 */
 			do_last_check = true;
 		}
-		spin_lock_irqsave(hba->host->host_lock, flags);
 	} while (tm_doorbell || tr_pending);
 
 	if (timeout) {
@@ -1314,7 +1316,6 @@  static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
 		ret = -EBUSY;
 	}
 out:
-	spin_unlock_irqrestore(hba->host->host_lock, flags);
 	ufshcd_release(hba);
 	return ret;
 }
@@ -6881,9 +6882,13 @@  static irqreturn_t ufshcd_tmc_handler(struct ufs_hba *hba)
 	irqreturn_t ret = IRQ_NONE;
 	int tag;
 
-	spin_lock_irqsave(hba->host->host_lock, flags);
+	spin_lock_irqsave(&hba->reg_lock, flags);
 	pending = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL);
+	spin_unlock_irqrestore(&hba->reg_lock, flags);
+
 	issued = hba->outstanding_tasks & ~pending;
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
 	for_each_set_bit(tag, &issued, hba->nutmrs) {
 		struct request *req = hba->tmf_rqs[tag];
 		struct completion *c = req->end_io_data;
@@ -7065,11 +7070,13 @@  static int __ufshcd_issue_tm_cmd(struct ufs_hba *hba,
 	memcpy(hba->utmrdl_base_addr + task_tag, treq, sizeof(*treq));
 	ufshcd_vops_setup_task_mgmt(hba, task_tag, tm_function);
 
+	spin_unlock_irqrestore(host->host_lock, flags);
+
+	spin_lock_irqsave(&hba->reg_lock, flags);
 	/* send command to the controller */
 	__set_bit(task_tag, &hba->outstanding_tasks);
 	ufshcd_writel(hba, 1 << task_tag, REG_UTP_TASK_REQ_DOOR_BELL);
-
-	spin_unlock_irqrestore(host->host_lock, flags);
+	spin_unlock_irqrestore(&hba->reg_lock, flags);
 
 	ufshcd_add_tm_upiu_trace(hba, task_tag, UFS_TM_SEND);
 
@@ -10469,6 +10476,9 @@  int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
 
 	sema_init(&hba->host_sem, 1);
 
+	/* Initialize host register read/write spinlock */
+	spin_lock_init(&hba->reg_lock);
+
 	/* Initialize UIC command mutex */
 	mutex_init(&hba->uic_cmd_mutex);
 
diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h
index a95282b9f743..6712833cc156 100644
--- a/include/ufs/ufshcd.h
+++ b/include/ufs/ufshcd.h
@@ -847,6 +847,7 @@  enum ufshcd_mcq_opr {
  * @host: Scsi_Host instance of the driver
  * @dev: device handle
  * @ufs_device_wlun: WLUN that controls the entire UFS device.
+ * @reg_lock: protect register reads/writes
  * @hwmon_device: device instance registered with the hwmon core.
  * @curr_dev_pwr_mode: active UFS device power mode.
  * @uic_link_state: active state of the link to the UFS device.
@@ -978,6 +979,8 @@  struct ufs_hba {
 	struct device *dev;
 	struct scsi_device *ufs_device_wlun;
 
+	spinlock_t reg_lock;
+
 #ifdef CONFIG_SCSI_UFS_HWMON
 	struct device *hwmon_device;
 #endif