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

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
...@@ -124,6 +124,7 @@ struct cxlflash_cfg { ...@@ -124,6 +124,7 @@ struct cxlflash_cfg {
struct list_head lluns; /* list of llun_info structs */ struct list_head lluns; /* list of llun_info structs */
wait_queue_head_t tmf_waitq; wait_queue_head_t tmf_waitq;
spinlock_t tmf_slock;
bool tmf_active; bool tmf_active;
wait_queue_head_t reset_waitq; wait_queue_head_t reset_waitq;
enum cxlflash_state state; enum cxlflash_state state;
......
...@@ -249,11 +249,10 @@ static void cmd_complete(struct afu_cmd *cmd) ...@@ -249,11 +249,10 @@ static void cmd_complete(struct afu_cmd *cmd)
scp->scsi_done(scp); scp->scsi_done(scp);
if (cmd_is_tmf) { 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; cfg->tmf_active = false;
wake_up_all_locked(&cfg->tmf_waitq); wake_up_all_locked(&cfg->tmf_waitq);
spin_unlock_irqrestore(&cfg->tmf_waitq.lock, spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags);
lock_flags);
} }
} else } else
complete(&cmd->cevent); complete(&cmd->cevent);
...@@ -420,6 +419,7 @@ static int send_tmf(struct afu *afu, struct scsi_cmnd *scp, u64 tmfcmd) ...@@ -420,6 +419,7 @@ static int send_tmf(struct afu *afu, struct scsi_cmnd *scp, u64 tmfcmd)
struct device *dev = &cfg->dev->dev; struct device *dev = &cfg->dev->dev;
ulong lock_flags; ulong lock_flags;
int rc = 0; int rc = 0;
ulong to;
cmd = cmd_checkout(afu); cmd = cmd_checkout(afu);
if (unlikely(!cmd)) { if (unlikely(!cmd)) {
...@@ -428,15 +428,15 @@ static int send_tmf(struct afu *afu, struct scsi_cmnd *scp, u64 tmfcmd) ...@@ -428,15 +428,15 @@ static int send_tmf(struct afu *afu, struct scsi_cmnd *scp, u64 tmfcmd)
goto out; goto out;
} }
/* If a Task Management Function is active, do not send one more. /* When Task Management Function is active do not send another */
*/ spin_lock_irqsave(&cfg->tmf_slock, lock_flags);
spin_lock_irqsave(&cfg->tmf_waitq.lock, lock_flags);
if (cfg->tmf_active) if (cfg->tmf_active)
wait_event_interruptible_locked_irq(cfg->tmf_waitq, wait_event_interruptible_lock_irq(cfg->tmf_waitq,
!cfg->tmf_active); !cfg->tmf_active,
cfg->tmf_slock);
cfg->tmf_active = true; cfg->tmf_active = true;
cmd->cmd_tmf = 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.ctx_id = afu->ctx_hndl;
cmd->rcb.port_sel = port_sel; cmd->rcb.port_sel = port_sel;
...@@ -457,15 +457,24 @@ static int send_tmf(struct afu *afu, struct scsi_cmnd *scp, u64 tmfcmd) ...@@ -457,15 +457,24 @@ static int send_tmf(struct afu *afu, struct scsi_cmnd *scp, u64 tmfcmd)
rc = send_cmd(afu, cmd); rc = send_cmd(afu, cmd);
if (unlikely(rc)) { if (unlikely(rc)) {
cmd_checkin(cmd); cmd_checkin(cmd);
spin_lock_irqsave(&cfg->tmf_waitq.lock, lock_flags); spin_lock_irqsave(&cfg->tmf_slock, lock_flags);
cfg->tmf_active = false; cfg->tmf_active = false;
spin_unlock_irqrestore(&cfg->tmf_waitq.lock, lock_flags); spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags);
goto out; goto out;
} }
spin_lock_irqsave(&cfg->tmf_waitq.lock, lock_flags); spin_lock_irqsave(&cfg->tmf_slock, lock_flags);
wait_event_interruptible_locked_irq(cfg->tmf_waitq, !cfg->tmf_active); to = msecs_to_jiffies(5000);
spin_unlock_irqrestore(&cfg->tmf_waitq.lock, lock_flags); 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: out:
return rc; return rc;
} }
...@@ -512,16 +521,17 @@ static int cxlflash_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scp) ...@@ -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)[2]),
get_unaligned_be32(&((u32 *)scp->cmnd)[3])); 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. * 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) { 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; rc = SCSI_MLQUEUE_HOST_BUSY;
goto out; goto out;
} }
spin_unlock_irqrestore(&cfg->tmf_waitq.lock, lock_flags); spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags);
switch (cfg->state) { switch (cfg->state) {
case STATE_RESET: case STATE_RESET:
...@@ -713,11 +723,12 @@ static void cxlflash_remove(struct pci_dev *pdev) ...@@ -713,11 +723,12 @@ static void cxlflash_remove(struct pci_dev *pdev)
/* 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 remove. * 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) if (cfg->tmf_active)
wait_event_interruptible_locked_irq(cfg->tmf_waitq, wait_event_interruptible_lock_irq(cfg->tmf_waitq,
!cfg->tmf_active); !cfg->tmf_active,
spin_unlock_irqrestore(&cfg->tmf_waitq.lock, lock_flags); cfg->tmf_slock);
spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags);
cfg->state = STATE_FAILTERM; cfg->state = STATE_FAILTERM;
cxlflash_stop_term_user_contexts(cfg); cxlflash_stop_term_user_contexts(cfg);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment