scsi: ufs: Fix task management completion
PATCH 1/1 scsi: ufs: Fix task management completion
The UFS driver uses blk_mq_tagset_busy_iter() when identifying task management requests to complete, however blk_mq_tagset_busy_iter() doesn't work.
blk_mq_tagset_busy_iter() only iterates requests dispatched by the block layer. That appears as if it might have started since commit 37f4a24c2469a1 ("blk-mq: centralise related handling into blk_mq_get_driver_tag") which removed 'data->hctx->tags->rqs[rq->tag] = rq' from blk_mq_rq_ctx_init() which gets called:
blk_get_request
blk_mq_alloc_request
__blk_mq_alloc_request
blk_mq_rq_ctx_init
Since UFS task management requests are not dispatched by the block layer, hctx->tags->rqs[rq->tag] remains NULL, and since blk_mq_tagset_busy_iter() relies on finding requests using hctx->tags->rqs[rq->tag], UFS task management requests are never found by blk_mq_tagset_busy_iter().
By using blk_mq_tagset_busy_iter(), the UFS driver was relying on internal details of the block layer, which was fragile and subsequently got broken. Fix by removing the use of blk_mq_tagset_busy_iter() and having the driver keep track of task management requests.
--- drivers/scsi/ufs/ufshcd.c | 52 +++++++++++++++++---------------------- drivers/scsi/ufs/ufshcd.h | 1 + 2 files changed, 23 insertions(+), 30 deletions(-)
drivers/scsi/ufs/ufshcd.c +22 -30
@@ -6378,27 +6378,6 @@ static irqreturn_t ufshcd_check_errors(struct ufs_hba *hba, u32 intr_status)return retval; } -struct ctm_info { -struct ufs_hba *hba; -unsigned long pending; -unsigned int ncpl; -}; - -static bool ufshcd_compl_tm (struct request *req,void *priv,bool reserved) -{ -struct ctm_info *const ci = priv; -struct completion *c; - -WARN_ON_ONCE (reserved); -if (test_bit (req->tag, &ci->pending)) -return true; - ci->ncpl++; - c = req->end_io_data; -if (c) -complete (c); -return true; -} -/** * ufshcd_tmc_handler - handle task management function completion * @hba: per adapter instance
@@ -6409,18 +6388,24 @@ static bool ufshcd_compl_tm(struct request *req, void *priv, bool reserved)*/ static irqreturn_t ufshcd_tmc_handler (struct ufs_hba *hba) { -unsigned long flags; -struct request_queue *q = hba->tmf_queue; -struct ctm_info ci = { - .hba = hba, - }; +unsigned long flags, pending, issued; +irqreturn_t ret =IRQ_NONE ; +int tag; + + pending =ufshcd_readl (hba,REG_UTP_TASK_REQ_DOOR_BELL );spin_lock_irqsave (hba->host->host_lock, flags); - ci.pending =ufshcd_readl (hba,REG_UTP_TASK_REQ_DOOR_BELL ); -blk_mq_tagset_busy_iter (q->tag_set, ufshcd_compl_tm, &ci); + issued = hba->outstanding_tasks & ~pending; +for_each_set_bit (tag, &issued, hba->nutmrs) { +struct request *req = hba->tmf_rqs[tag]; +struct completion *c = req->end_io_data; + +complete (c); + ret =IRQ_HANDLED ; + }spin_unlock_irqrestore (hba->host->host_lock, flags); -return ci.ncpl ?IRQ_HANDLED :IRQ_NONE ; +return ret; }/**
@@ -6543,9 +6528,9 @@ static int __ufshcd_issue_tm_cmd(struct ufs_hba *hba,ufshcd_hold (hba, false);spin_lock_irqsave (host->host_lock, flags); -blk_mq_start_request (req); task_tag = req->tag; + hba->tmf_rqs[req->tag] = req; treq->upiu_req.req_header.dword_0 |=cpu_to_be32 (task_tag);memcpy (hba->utmrdl_base_addr + task_tag, treq,sizeof (*treq));
@@ -6586,6 +6571,7 @@ static int __ufshcd_issue_tm_cmd(struct ufs_hba *hba, }spin_lock_irqsave (hba->host->host_lock, flags); + hba->tmf_rqs[req->tag] =NULL ;__clear_bit (task_tag, &hba->outstanding_tasks);spin_unlock_irqrestore (hba->host->host_lock, flags);
@@ -9636,6 +9622,12 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) err =PTR_ERR (hba->tmf_queue); goto free_tmf_tag_set; } + hba->tmf_rqs =devm_kcalloc (hba->dev, hba->nutmrs, +sizeof (*hba->tmf_rqs),GFP_KERNEL ); +if (!hba->tmf_rqs) { + err = -ENOMEM ; + goto free_tmf_queue; + }/* Reset the attached device */ ufshcd_device_reset (hba);
drivers/scsi/ufs/ufshcd.h +1 -0
@@ -828,6 +828,7 @@ struct ufs_hba {struct blk_mq_tag_set tmf_tag_set;struct request_queue *tmf_queue; +struct request **tmf_rqs;struct uic_command *active_uic_cmd;struct mutex uic_cmd_mutex;
Signature · 1 line
2.17.1
Quoted 21 lines · Adrian Hunter
The UFS driver uses
blk_mq_tagset_busy_iter()when identifying task management requests to complete, howeverblk_mq_tagset_busy_iter()doesn't work.
blk_mq_tagset_busy_iter()only iterates requests dispatched by the block layer. That appears as if it might have started since commit37f4a24c2469a1("blk-mq: centralise related handling intoblk_mq_get_driver_tag") which removed 'data->hctx->tags->rqs[rq->tag] = rq' fromblk_mq_rq_ctx_init()which gets called:
blk_get_request
blk_mq_alloc_request
__blk_mq_alloc_request
blk_mq_rq_ctx_initSince UFS task management requests are not dispatched by the block layer, hctx->tags->rqs[rq->tag] remains NULL, and since
blk_mq_tagset_busy_iter()relies on finding requests using hctx->tags->rqs[rq->tag], UFS task management requests are never found byblk_mq_tagset_busy_iter().
Quoted 4 lines
By using
blk_mq_tagset_busy_iter(), the UFS driver was relying on internal details of the block layer, which was fragile and subsequently got broken. Fix by removing the use ofblk_mq_tagset_busy_iter()and having the driver keep track of task management requests.
Thanks for the detailed analysis. I agree that using blk_mq_tagset_busy_iter() no longer works due to recent changes in the block layer. Has it been considered to export blk_mq_all_tag_iter() and to use that function instead of blk_mq_tagset_busy_iter()?
Thanks,
Bart.
Quoted 27 lines · Bart Van Assche
Quoted 21 lines · Adrian Hunter
The UFS driver uses
blk_mq_tagset_busy_iter()when identifying task management requests to complete, howeverblk_mq_tagset_busy_iter()doesn't work.
blk_mq_tagset_busy_iter()only iterates requests dispatched by the block layer. That appears as if it might have started since commit37f4a24c2469a1("blk-mq: centralise related handling intoblk_mq_get_driver_tag") which removed 'data->hctx->tags->rqs[rq->tag] = rq' fromblk_mq_rq_ctx_init()which gets called:blk_get_request
blk_mq_alloc_request
__blk_mq_alloc_request
blk_mq_rq_ctx_initSince UFS task management requests are not dispatched by the block layer, hctx->tags->rqs[rq->tag] remains NULL, and since
blk_mq_tagset_busy_iter()relies on finding requests using hctx->tags->rqs[rq->tag], UFS task management requests are never found byblk_mq_tagset_busy_iter().Quoted 4 lines
By using
blk_mq_tagset_busy_iter(), the UFS driver was relying on internal details of the block layer, which was fragile and subsequently got broken. Fix by removing the use ofblk_mq_tagset_busy_iter()and having the driver keep track of task management requests.
Quoted 4 lines
Thanks for the detailed analysis. I agree that using
blk_mq_tagset_busy_iter()no longer works due to recent changes in the block layer. Has it been considered to exportblk_mq_all_tag_iter()and to use that function instead ofblk_mq_tagset_busy_iter()?
It seemed better not to be the only driver using the block layer in a different way. Having the UFS driver self-contained in this regard seemed more robust. If anything, the code without blk_mq_tagset_busy_iter() is slightly shorter, so using a block layer iterator doesn't help much.
Quoted 4 lines · Adrian Hunter
By using
blk_mq_tagset_busy_iter(), the UFS driver was relying on internal details of the block layer, which was fragile and subsequently got broken. Fix by removing the use ofblk_mq_tagset_busy_iter()and having the driver keep track of task management requests.
Martin K. Petersen
AppliedQuoted 11 lines · Adrian Hunter
The UFS driver uses
blk_mq_tagset_busy_iter()when identifying task management requests to complete, howeverblk_mq_tagset_busy_iter()doesn't work.
blk_mq_tagset_busy_iter()only iterates requests dispatched by the block layer. That appears as if it might have started since commit37f4a24c2469a1("blk-mq: centralise related handling intoblk_mq_get_driver_tag") which removed 'data->hctx->tags->rqs[rq->tag] = rq' fromblk_mq_rq_ctx_init()which gets called:[...]
Applied to 5.15/scsi-fixes, thanks!
[1/1] scsi: ufs: Fix task management completion
https://git.kernel.org/mkp/scsi/c/f5ef336fd2e4
Signature · 1 line
Martin K. Petersen Oracle Linux Engineering