diff mbox series

[RFC,v2,4/5] ufs: Use blk_{get,put}_request() to allocate and free TMFs

Message ID 20191105004226.232635-5-bvanassche@acm.org (mailing list archive)
State Superseded
Headers show
Series Simplify and optimize the UFS driver | expand

Commit Message

Bart Van Assche Nov. 5, 2019, 12:42 a.m. UTC
Manage TMF tags with blk_{get,put}_request() instead of
ufshcd_get_tm_free_slot() / ufshcd_put_tm_slot(). Store a per-request
completion pointer in request.end_io_data instead of using a waitqueue
to report TMF completion.

Cc: Yaniv Gardi <ygardi@codeaurora.org>
Cc: Subhash Jadavani <subhashj@codeaurora.org>
Cc: Stanley Chu <stanley.chu@mediatek.com>
Cc: Avri Altman <avri.altman@wdc.com>
Cc: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 drivers/scsi/ufs/ufshcd.c | 84 +++++++++++++++++----------------------
 drivers/scsi/ufs/ufshcd.h |  9 -----
 2 files changed, 36 insertions(+), 57 deletions(-)

Comments

Bean Huo Nov. 5, 2019, 1:50 p.m. UTC | #1
Hi, Bart

> -	wait_event(hba->tm_tag_wq, ufshcd_get_tm_free_slot(hba,
> &free_slot));
> +	req = blk_get_request(q, REQ_OP_DRV_OUT, BLK_MQ_REQ_RESERVED);
> +	req->end_io_data = &wait;
> +	free_slot = req->tag;
> +	WARN_ON_ONCE(free_slot < 0 || free_slot >= hba->nutmrs);
>  	ufshcd_hold(hba, false);
> 
Understand now , you delete ufshcd_get_tm_free_slot(). Run a big circle to get a free_slot from reserved tags by calling blk_get_request().
But UFS data transfer queue depth is 32, not 32 + hba->nutmrs.  How to make sure we see the tag is consistent across block/scsi/ufs?
Thanks,
 
//Bean
Bart Van Assche Nov. 5, 2019, 5:05 p.m. UTC | #2
On 11/5/19 5:50 AM, Bean Huo (beanhuo) wrote:
>> -	wait_event(hba->tm_tag_wq, ufshcd_get_tm_free_slot(hba,
>> &free_slot));
>> +	req = blk_get_request(q, REQ_OP_DRV_OUT, BLK_MQ_REQ_RESERVED);
>> +	req->end_io_data = &wait;
>> +	free_slot = req->tag;
>> +	WARN_ON_ONCE(free_slot < 0 || free_slot >= hba->nutmrs);
>>   	ufshcd_hold(hba, false);
>>
> Understand now , you delete ufshcd_get_tm_free_slot(). Run a big circle to get a free_slot from reserved tags by calling blk_get_request().
> But UFS data transfer queue depth is 32, not 32 + hba->nutmrs.  How to make sure we see the tag is consistent across block/scsi/ufs?

Hi Bean,

Please have a look at the blk_mq_get_tag() function in the block layer. 
The implementation of that function makes it clear that the tags with 
numbers [0 .. nr_reserved) are considered reserved tags and also that 
the tags with numbers [nr_reserved .. queue_depth) are considered 
regular tags. In other words, adding hba->nutmrs to can_queue does not 
increase the queue depth because the same number of tags are considered 
reserved tags.

Bart.
Bean Huo Nov. 5, 2019, 9:59 p.m. UTC | #3
> On 11/5/19 5:50 AM, Bean Huo (beanhuo) wrote:
> >> -	wait_event(hba->tm_tag_wq, ufshcd_get_tm_free_slot(hba,
> >> &free_slot));
> >> +	req = blk_get_request(q, REQ_OP_DRV_OUT, BLK_MQ_REQ_RESERVED);
> >> +	req->end_io_data = &wait;
> >> +	free_slot = req->tag;
> >> +	WARN_ON_ONCE(free_slot < 0 || free_slot >= hba->nutmrs);
> >>   	ufshcd_hold(hba, false);
> >>
> > Understand now , you delete ufshcd_get_tm_free_slot(). Run a big circle to
> get a free_slot from reserved tags by calling blk_get_request().
> > But UFS data transfer queue depth is 32, not 32 + hba->nutmrs.  How to make
> sure we see the tag is consistent across block/scsi/ufs?
> 
> Hi Bean,
> 
> Please have a look at the blk_mq_get_tag() function in the block layer.
> The implementation of that function makes it clear that the tags with numbers
> [0 .. nr_reserved) are considered reserved tags and also that the tags with
> numbers [nr_reserved .. queue_depth) are considered regular tags. In other
> words, adding hba->nutmrs to can_queue does not increase the queue depth
> because the same number of tags are considered reserved tags.
> 
> Bart.

Hi, Bart
Yes, I saw that. And actually, task management requests and regular data transfer
Requests will be stored to different queues, and use different door-bell registers.
So as you said, to introduce a new tag set for TMFs is better.
Thanks,

//Bean
diff mbox series

Patch

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index c8124db6665e..a2100f9d51a3 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -645,40 +645,6 @@  static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp)
 	return le32_to_cpu(lrbp->utr_descriptor_ptr->header.dword_2) & MASK_OCS;
 }
 
-/**
- * ufshcd_get_tm_free_slot - get a free slot for task management request
- * @hba: per adapter instance
- * @free_slot: pointer to variable with available slot value
- *
- * Get a free tag and lock it until ufshcd_put_tm_slot() is called.
- * Returns 0 if free slot is not available, else return 1 with tag value
- * in @free_slot.
- */
-static bool ufshcd_get_tm_free_slot(struct ufs_hba *hba, int *free_slot)
-{
-	int tag;
-	bool ret = false;
-
-	if (!free_slot)
-		goto out;
-
-	do {
-		tag = find_first_zero_bit(&hba->tm_slots_in_use, hba->nutmrs);
-		if (tag >= hba->nutmrs)
-			goto out;
-	} while (test_and_set_bit_lock(tag, &hba->tm_slots_in_use));
-
-	*free_slot = tag;
-	ret = true;
-out:
-	return ret;
-}
-
-static inline void ufshcd_put_tm_slot(struct ufs_hba *hba, int slot)
-{
-	clear_bit_unlock(slot, &hba->tm_slots_in_use);
-}
-
 /**
  * ufshcd_utrl_clear - Clear a bit in UTRLCLR register
  * @hba: per adapter instance
@@ -5519,17 +5485,35 @@  static void ufshcd_check_errors(struct ufs_hba *hba)
 	 */
 }
 
+struct ctm_info {
+	struct ufs_hba *hba;
+	unsigned long pending;
+};
+
+static bool ufshcd_compl_tm(struct request *req, void *priv, bool reserved)
+{
+	const struct ctm_info *const ci = priv;
+	struct completion *c;
+
+	if (!reserved || test_bit(req->tag, &ci->pending))
+		return true;
+	c = req->end_io_data;
+	if (c)
+		complete(c);
+	return true;
+}
+
 /**
  * ufshcd_tmc_handler - handle task management function completion
  * @hba: per adapter instance
  */
 static void ufshcd_tmc_handler(struct ufs_hba *hba)
 {
-	u32 tm_doorbell;
+	struct request_queue *q = hba->tag_alloc_queue;
+	struct ctm_info ci = { .hba = hba };
 
-	tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL);
-	hba->tm_condition = tm_doorbell ^ hba->outstanding_tasks;
-	wake_up(&hba->tm_wq);
+	ci.pending = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL);
+	blk_mq_tagset_busy_iter(q->tag_set, ufshcd_compl_tm, &ci);
 }
 
 /**
@@ -5622,7 +5606,10 @@  static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag)
 static int __ufshcd_issue_tm_cmd(struct ufs_hba *hba,
 		struct utp_task_req_desc *treq, u8 tm_function)
 {
+	struct request_queue *q = hba->tag_alloc_queue;
 	struct Scsi_Host *host = hba->host;
+	DECLARE_COMPLETION_ONSTACK(wait);
+	struct request *req;
 	unsigned long flags;
 	int free_slot, task_tag, err;
 
@@ -5631,7 +5618,10 @@  static int __ufshcd_issue_tm_cmd(struct ufs_hba *hba,
 	 * Even though we use wait_event() which sleeps indefinitely,
 	 * the maximum wait time is bounded by %TM_CMD_TIMEOUT.
 	 */
-	wait_event(hba->tm_tag_wq, ufshcd_get_tm_free_slot(hba, &free_slot));
+	req = blk_get_request(q, REQ_OP_DRV_OUT, BLK_MQ_REQ_RESERVED);
+	req->end_io_data = &wait;
+	free_slot = req->tag;
+	WARN_ON_ONCE(free_slot < 0 || free_slot >= hba->nutmrs);
 	ufshcd_hold(hba, false);
 
 	spin_lock_irqsave(host->host_lock, flags);
@@ -5657,10 +5647,14 @@  static int __ufshcd_issue_tm_cmd(struct ufs_hba *hba,
 	ufshcd_add_tm_upiu_trace(hba, task_tag, "tm_send");
 
 	/* wait until the task management command is completed */
-	err = wait_event_timeout(hba->tm_wq,
-			test_bit(free_slot, &hba->tm_condition),
+	err = wait_for_completion_io_timeout(&wait,
 			msecs_to_jiffies(TM_CMD_TIMEOUT));
 	if (!err) {
+		/*
+		 * Make sure that ufshcd_compl_tm() does not trigger a
+		 * use-after-free.
+		 */
+		req->end_io_data = NULL;
 		ufshcd_add_tm_upiu_trace(hba, task_tag, "tm_complete_err");
 		dev_err(hba->dev, "%s: task management cmd 0x%.2x timed-out\n",
 				__func__, tm_function);
@@ -5679,9 +5673,7 @@  static int __ufshcd_issue_tm_cmd(struct ufs_hba *hba,
 	__clear_bit(free_slot, &hba->outstanding_tasks);
 	spin_unlock_irqrestore(hba->host->host_lock, flags);
 
-	clear_bit(free_slot, &hba->tm_condition);
-	ufshcd_put_tm_slot(hba, free_slot);
-	wake_up(&hba->tm_tag_wq);
+	blk_put_request(req);
 
 	ufshcd_release(hba);
 	return err;
@@ -8315,10 +8307,6 @@  int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
 
 	hba->max_pwr_info.is_valid = false;
 
-	/* Initailize wait queue for task management */
-	init_waitqueue_head(&hba->tm_wq);
-	init_waitqueue_head(&hba->tm_tag_wq);
-
 	/* Initialize work queues */
 	INIT_WORK(&hba->eh_work, ufshcd_err_handler);
 	INIT_WORK(&hba->eeh_work, ufshcd_exception_event_handler);
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 8fa33fb71237..0d8867db43db 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -493,11 +493,7 @@  struct ufs_stats {
  * @irq: Irq number of the controller
  * @active_uic_cmd: handle of active UIC command
  * @uic_cmd_mutex: mutex for uic command
- * @tm_wq: wait queue for task management
- * @tm_tag_wq: wait queue for free task management slots
- * @tm_slots_in_use: bit map of task management request slots in use
  * @pwr_done: completion for power mode change
- * @tm_condition: condition variable for task management
  * @ufshcd_state: UFSHCD states
  * @eh_flags: Error handling flags
  * @intr_mask: Interrupt Mask Bits
@@ -641,11 +637,6 @@  struct ufs_hba {
 	/* Device deviations from standard UFS device spec. */
 	unsigned int dev_quirks;
 
-	wait_queue_head_t tm_wq;
-	wait_queue_head_t tm_tag_wq;
-	unsigned long tm_condition;
-	unsigned long tm_slots_in_use;
-
 	struct uic_command *active_uic_cmd;
 	struct mutex uic_cmd_mutex;
 	struct completion *uic_async_done;