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: Matthew R. Ochs <mrochs@linux.vnet.ibm.com> Signed-off-by: Manoj N. Kumar <manoj@linux.vnet.ibm.com> Reviewed-by: Brian King <brking@linux.vnet.ibm.com> Reviewed-by: Tomas Henzl <thenzl@redhat.com> Signed-off-by: James Bottomley <JBottomley@Odin.com>
This commit is contained in:

committed by
James Bottomley

parent
4392ba49eb
commit
018d1dc955
@@ -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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
/* 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);
|
||||||
|
Reference in New Issue
Block a user