Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 018d1dc9 authored by Matthew R. Ochs's avatar Matthew R. Ochs Committed by James Bottomley
Browse files

cxlflash: Fix to avoid stall while waiting on TMF



Borrowing the TMF waitq's spinlock causes a stall condition when
waiting for the TMF to complete. To remedy, introduce our own spin
lock to serialize TMF and use the appropriate wait services.

Also add a timeout while waiting for a TMF completion. When a TMF
times out, report back a failure such that a bigger hammer reset
can occur.

Signed-off-by: default avatarMatthew R. Ochs <mrochs@linux.vnet.ibm.com>
Signed-off-by: default avatarManoj N. Kumar <manoj@linux.vnet.ibm.com>
Reviewed-by: default avatarBrian King <brking@linux.vnet.ibm.com>
Reviewed-by: default avatarTomas Henzl <thenzl@redhat.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Odin.com>
parent 4392ba49
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -124,6 +124,7 @@ struct cxlflash_cfg {
	struct list_head lluns; /* list of llun_info structs */

	wait_queue_head_t tmf_waitq;
	spinlock_t tmf_slock;
	bool tmf_active;
	wait_queue_head_t reset_waitq;
	enum cxlflash_state state;
+33 −22
Original line number Diff line number Diff line
@@ -249,11 +249,10 @@ static void cmd_complete(struct afu_cmd *cmd)
		scp->scsi_done(scp);

		if (cmd_is_tmf) {
			spin_lock_irqsave(&cfg->tmf_waitq.lock, lock_flags);
			spin_lock_irqsave(&cfg->tmf_slock, lock_flags);
			cfg->tmf_active = false;
			wake_up_all_locked(&cfg->tmf_waitq);
			spin_unlock_irqrestore(&cfg->tmf_waitq.lock,
					       lock_flags);
			spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags);
		}
	} else
		complete(&cmd->cevent);
@@ -420,6 +419,7 @@ static int send_tmf(struct afu *afu, struct scsi_cmnd *scp, u64 tmfcmd)
	struct device *dev = &cfg->dev->dev;
	ulong lock_flags;
	int rc = 0;
	ulong to;

	cmd = cmd_checkout(afu);
	if (unlikely(!cmd)) {
@@ -428,15 +428,15 @@ static int send_tmf(struct afu *afu, struct scsi_cmnd *scp, u64 tmfcmd)
		goto out;
	}

	/* If a Task Management Function is active, do not send one more.
	 */
	spin_lock_irqsave(&cfg->tmf_waitq.lock, lock_flags);
	/* When Task Management Function is active do not send another */
	spin_lock_irqsave(&cfg->tmf_slock, lock_flags);
	if (cfg->tmf_active)
		wait_event_interruptible_locked_irq(cfg->tmf_waitq,
						    !cfg->tmf_active);
		wait_event_interruptible_lock_irq(cfg->tmf_waitq,
						  !cfg->tmf_active,
						  cfg->tmf_slock);
	cfg->tmf_active = true;
	cmd->cmd_tmf = true;
	spin_unlock_irqrestore(&cfg->tmf_waitq.lock, lock_flags);
	spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags);

	cmd->rcb.ctx_id = afu->ctx_hndl;
	cmd->rcb.port_sel = port_sel;
@@ -457,15 +457,24 @@ static int send_tmf(struct afu *afu, struct scsi_cmnd *scp, u64 tmfcmd)
	rc = send_cmd(afu, cmd);
	if (unlikely(rc)) {
		cmd_checkin(cmd);
		spin_lock_irqsave(&cfg->tmf_waitq.lock, lock_flags);
		spin_lock_irqsave(&cfg->tmf_slock, lock_flags);
		cfg->tmf_active = false;
		spin_unlock_irqrestore(&cfg->tmf_waitq.lock, lock_flags);
		spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags);
		goto out;
	}

	spin_lock_irqsave(&cfg->tmf_waitq.lock, lock_flags);
	wait_event_interruptible_locked_irq(cfg->tmf_waitq, !cfg->tmf_active);
	spin_unlock_irqrestore(&cfg->tmf_waitq.lock, lock_flags);
	spin_lock_irqsave(&cfg->tmf_slock, lock_flags);
	to = msecs_to_jiffies(5000);
	to = wait_event_interruptible_lock_irq_timeout(cfg->tmf_waitq,
						       !cfg->tmf_active,
						       cfg->tmf_slock,
						       to);
	if (!to) {
		cfg->tmf_active = false;
		dev_err(dev, "%s: TMF timed out!\n", __func__);
		rc = -1;
	}
	spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags);
out:
	return rc;
}
@@ -512,16 +521,17 @@ static int cxlflash_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scp)
			    get_unaligned_be32(&((u32 *)scp->cmnd)[2]),
			    get_unaligned_be32(&((u32 *)scp->cmnd)[3]));

	/* If a Task Management Function is active, wait for it to complete
	/*
	 * If a Task Management Function is active, wait for it to complete
	 * before continuing with regular commands.
	 */
	spin_lock_irqsave(&cfg->tmf_waitq.lock, lock_flags);
	spin_lock_irqsave(&cfg->tmf_slock, lock_flags);
	if (cfg->tmf_active) {
		spin_unlock_irqrestore(&cfg->tmf_waitq.lock, lock_flags);
		spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags);
		rc = SCSI_MLQUEUE_HOST_BUSY;
		goto out;
	}
	spin_unlock_irqrestore(&cfg->tmf_waitq.lock, lock_flags);
	spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags);

	switch (cfg->state) {
	case STATE_RESET:
@@ -713,11 +723,12 @@ static void cxlflash_remove(struct pci_dev *pdev)
	/* If a Task Management Function is active, wait for it to complete
	 * before continuing with remove.
	 */
	spin_lock_irqsave(&cfg->tmf_waitq.lock, lock_flags);
	spin_lock_irqsave(&cfg->tmf_slock, lock_flags);
	if (cfg->tmf_active)
		wait_event_interruptible_locked_irq(cfg->tmf_waitq,
						    !cfg->tmf_active);
	spin_unlock_irqrestore(&cfg->tmf_waitq.lock, lock_flags);
		wait_event_interruptible_lock_irq(cfg->tmf_waitq,
						  !cfg->tmf_active,
						  cfg->tmf_slock);
	spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags);

	cfg->state = STATE_FAILTERM;
	cxlflash_stop_term_user_contexts(cfg);