Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[linux-2.6-microblaze.git] / drivers / ufs / core / ufshcd.c
index 0dc6437..6bc679d 100644 (file)
@@ -2953,37 +2953,59 @@ ufshcd_dev_cmd_completion(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
 static int ufshcd_wait_for_dev_cmd(struct ufs_hba *hba,
                struct ufshcd_lrb *lrbp, int max_timeout)
 {
-       int err = 0;
-       unsigned long time_left;
+       unsigned long time_left = msecs_to_jiffies(max_timeout);
        unsigned long flags;
+       bool pending;
+       int err;
 
+retry:
        time_left = wait_for_completion_timeout(hba->dev_cmd.complete,
-                       msecs_to_jiffies(max_timeout));
+                                               time_left);
 
-       spin_lock_irqsave(hba->host->host_lock, flags);
-       hba->dev_cmd.complete = NULL;
        if (likely(time_left)) {
+               /*
+                * The completion handler called complete() and the caller of
+                * this function still owns the @lrbp tag so the code below does
+                * not trigger any race conditions.
+                */
+               hba->dev_cmd.complete = NULL;
                err = ufshcd_get_tr_ocs(lrbp);
                if (!err)
                        err = ufshcd_dev_cmd_completion(hba, lrbp);
-       }
-       spin_unlock_irqrestore(hba->host->host_lock, flags);
-
-       if (!time_left) {
+       } else {
                err = -ETIMEDOUT;
                dev_dbg(hba->dev, "%s: dev_cmd request timedout, tag %d\n",
                        __func__, lrbp->task_tag);
-               if (!ufshcd_clear_cmds(hba, 1U << lrbp->task_tag))
+               if (ufshcd_clear_cmds(hba, 1U << lrbp->task_tag) == 0) {
                        /* successfully cleared the command, retry if needed */
                        err = -EAGAIN;
-               /*
-                * in case of an error, after clearing the doorbell,
-                * we also need to clear the outstanding_request
-                * field in hba
-                */
-               spin_lock_irqsave(&hba->outstanding_lock, flags);
-               __clear_bit(lrbp->task_tag, &hba->outstanding_reqs);
-               spin_unlock_irqrestore(&hba->outstanding_lock, flags);
+                       /*
+                        * Since clearing the command succeeded we also need to
+                        * clear the task tag bit from the outstanding_reqs
+                        * variable.
+                        */
+                       spin_lock_irqsave(&hba->outstanding_lock, flags);
+                       pending = test_bit(lrbp->task_tag,
+                                          &hba->outstanding_reqs);
+                       if (pending) {
+                               hba->dev_cmd.complete = NULL;
+                               __clear_bit(lrbp->task_tag,
+                                           &hba->outstanding_reqs);
+                       }
+                       spin_unlock_irqrestore(&hba->outstanding_lock, flags);
+
+                       if (!pending) {
+                               /*
+                                * The completion handler ran while we tried to
+                                * clear the command.
+                                */
+                               time_left = 1;
+                               goto retry;
+                       }
+               } else {
+                       dev_err(hba->dev, "%s: failed to clear tag %d\n",
+                               __func__, lrbp->task_tag);
+               }
        }
 
        return err;
@@ -5744,7 +5766,7 @@ int ufshcd_wb_toggle(struct ufs_hba *hba, bool enable)
        }
 
        hba->dev_info.wb_enabled = enable;
-       dev_info(hba->dev, "%s Write Booster %s\n",
+       dev_dbg(hba->dev, "%s Write Booster %s\n",
                        __func__, enable ? "enabled" : "disabled");
 
        return ret;
@@ -7259,7 +7281,7 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba *hba)
        hba->silence_err_logs = false;
 
        /* scale up clocks to max frequency before full reinitialization */
-       ufshcd_set_clk_freq(hba, true);
+       ufshcd_scale_clks(hba, true);
 
        err = ufshcd_hba_enable(hba);
 
@@ -9508,7 +9530,7 @@ void ufshcd_remove(struct ufs_hba *hba)
        ufs_bsg_remove(hba);
        ufshpb_remove(hba);
        ufs_sysfs_remove_nodes(hba->dev);
-       blk_cleanup_queue(hba->tmf_queue);
+       blk_mq_destroy_queue(hba->tmf_queue);
        blk_mq_free_tag_set(&hba->tmf_tag_set);
        scsi_remove_host(hba->host);
        /* disable interrupts */
@@ -9804,7 +9826,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
        return 0;
 
 free_tmf_queue:
-       blk_cleanup_queue(hba->tmf_queue);
+       blk_mq_destroy_queue(hba->tmf_queue);
 free_tmf_tag_set:
        blk_mq_free_tag_set(&hba->tmf_tag_set);
 out_remove_scsi_host: