Commit d2fab5cf authored by Brian King's avatar Brian King Committed by James Bottomley

[SCSI] ibmvfc: Fix terminate_rport_io

The ibmvfc driver was incorrectly obtaining a scsi_target pointer
from an fc_rport. The way it is coded ensures that ibmvfc's
terminate_rport_io handler does absolutely nothing. Fix this up
to iterate through affected devices differently, sending cancel
and abort task set as appropriate. Without this patch,
fast_io_fail_tmo is broken for ibmvfc.
Signed-off-by: default avatarBrian King <brking@linux.vnet.ibm.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@suse.de>
parent d5da3040
...@@ -2039,95 +2039,108 @@ static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc) ...@@ -2039,95 +2039,108 @@ static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc)
} }
/** /**
* ibmvfc_abort_task_set - Abort outstanding commands to the device * ibmvfc_match_rport - Match function for specified remote port
* @sdev: scsi device to abort commands * @evt: ibmvfc event struct
* * @device: device to match (rport)
* This sends an Abort Task Set to the VIOS for the specified device. This does
* NOT send any cancel to the VIOS. That must be done separately.
* *
* Returns: * Returns:
* 0 on success / other on failure * 1 if event matches rport / 0 if event does not match rport
**/ **/
static int ibmvfc_abort_task_set(struct scsi_device *sdev) static int ibmvfc_match_rport(struct ibmvfc_event *evt, void *rport)
{ {
struct ibmvfc_host *vhost = shost_priv(sdev->host); struct fc_rport *cmd_rport;
struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
struct ibmvfc_cmd *tmf;
struct ibmvfc_event *evt, *found_evt;
union ibmvfc_iu rsp_iu;
struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp;
int rsp_rc = -EBUSY;
unsigned long flags;
int rsp_code = 0;
spin_lock_irqsave(vhost->host->host_lock, flags); if (evt->cmnd) {
found_evt = NULL; cmd_rport = starget_to_rport(scsi_target(evt->cmnd->device));
list_for_each_entry(evt, &vhost->sent, queue) { if (cmd_rport == rport)
if (evt->cmnd && evt->cmnd->device == sdev) { return 1;
found_evt = evt;
break;
}
}
if (!found_evt) {
if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
sdev_printk(KERN_INFO, sdev, "No events found to abort\n");
spin_unlock_irqrestore(vhost->host->host_lock, flags);
return 0;
}
if (vhost->state == IBMVFC_ACTIVE) {
evt = ibmvfc_get_event(vhost);
ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT);
tmf = &evt->iu.cmd;
memset(tmf, 0, sizeof(*tmf));
tmf->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
tmf->resp.len = sizeof(tmf->rsp);
tmf->frame_type = IBMVFC_SCSI_FCP_TYPE;
tmf->payload_len = sizeof(tmf->iu);
tmf->resp_len = sizeof(tmf->rsp);
tmf->cancel_key = (unsigned long)sdev->hostdata;
tmf->tgt_scsi_id = rport->port_id;
int_to_scsilun(sdev->lun, &tmf->iu.lun);
tmf->flags = (IBMVFC_NO_MEM_DESC | IBMVFC_TMF);
tmf->iu.tmf_flags = IBMVFC_ABORT_TASK_SET;
evt->sync_iu = &rsp_iu;
init_completion(&evt->comp);
rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
} }
return 0;
}
spin_unlock_irqrestore(vhost->host->host_lock, flags); /**
* ibmvfc_match_target - Match function for specified target
* @evt: ibmvfc event struct
* @device: device to match (starget)
*
* Returns:
* 1 if event matches starget / 0 if event does not match starget
**/
static int ibmvfc_match_target(struct ibmvfc_event *evt, void *device)
{
if (evt->cmnd && scsi_target(evt->cmnd->device) == device)
return 1;
return 0;
}
if (rsp_rc != 0) { /**
sdev_printk(KERN_ERR, sdev, "Failed to send abort. rc=%d\n", rsp_rc); * ibmvfc_match_lun - Match function for specified LUN
return -EIO; * @evt: ibmvfc event struct
} * @device: device to match (sdev)
*
* Returns:
* 1 if event matches sdev / 0 if event does not match sdev
**/
static int ibmvfc_match_lun(struct ibmvfc_event *evt, void *device)
{
if (evt->cmnd && evt->cmnd->device == device)
return 1;
return 0;
}
sdev_printk(KERN_INFO, sdev, "Aborting outstanding commands\n"); /**
wait_for_completion(&evt->comp); * ibmvfc_wait_for_ops - Wait for ops to complete
* @vhost: ibmvfc host struct
* @device: device to match (starget or sdev)
* @match: match function
*
* Returns:
* SUCCESS / FAILED
**/
static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device,
int (*match) (struct ibmvfc_event *, void *))
{
struct ibmvfc_event *evt;
DECLARE_COMPLETION_ONSTACK(comp);
int wait;
unsigned long flags;
signed long timeout = IBMVFC_ABORT_WAIT_TIMEOUT * HZ;
if (rsp_iu.cmd.status) ENTER;
rsp_code = ibmvfc_get_err_result(&rsp_iu.cmd); do {
wait = 0;
spin_lock_irqsave(vhost->host->host_lock, flags);
list_for_each_entry(evt, &vhost->sent, queue) {
if (match(evt, device)) {
evt->eh_comp = &comp;
wait++;
}
}
spin_unlock_irqrestore(vhost->host->host_lock, flags);
if (rsp_code) { if (wait) {
if (fc_rsp->flags & FCP_RSP_LEN_VALID) timeout = wait_for_completion_timeout(&comp, timeout);
rsp_code = fc_rsp->data.info.rsp_code;
sdev_printk(KERN_ERR, sdev, "Abort failed: %s (%x:%x) " if (!timeout) {
"flags: %x fcp_rsp: %x, scsi_status: %x\n", wait = 0;
ibmvfc_get_cmd_error(rsp_iu.cmd.status, rsp_iu.cmd.error), spin_lock_irqsave(vhost->host->host_lock, flags);
rsp_iu.cmd.status, rsp_iu.cmd.error, fc_rsp->flags, rsp_code, list_for_each_entry(evt, &vhost->sent, queue) {
fc_rsp->scsi_status); if (match(evt, device)) {
rsp_rc = -EIO; evt->eh_comp = NULL;
} else wait++;
sdev_printk(KERN_INFO, sdev, "Abort successful\n"); }
}
spin_unlock_irqrestore(vhost->host->host_lock, flags);
if (wait)
dev_err(vhost->dev, "Timed out waiting for aborted commands\n");
LEAVE;
return wait ? FAILED : SUCCESS;
}
}
} while (wait);
spin_lock_irqsave(vhost->host->host_lock, flags); LEAVE;
ibmvfc_free_event(evt); return SUCCESS;
spin_unlock_irqrestore(vhost->host->host_lock, flags);
return rsp_rc;
} }
/** /**
...@@ -2215,88 +2228,130 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type) ...@@ -2215,88 +2228,130 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
} }
/** /**
* ibmvfc_match_target - Match function for specified target * ibmvfc_match_key - Match function for specified cancel key
* @evt: ibmvfc event struct * @evt: ibmvfc event struct
* @device: device to match (starget) * @key: cancel key to match
* *
* Returns: * Returns:
* 1 if event matches starget / 0 if event does not match starget * 1 if event matches key / 0 if event does not match key
**/ **/
static int ibmvfc_match_target(struct ibmvfc_event *evt, void *device) static int ibmvfc_match_key(struct ibmvfc_event *evt, void *key)
{ {
if (evt->cmnd && scsi_target(evt->cmnd->device) == device) unsigned long cancel_key = (unsigned long)key;
return 1;
return 0;
}
/** if (evt->crq.format == IBMVFC_CMD_FORMAT &&
* ibmvfc_match_lun - Match function for specified LUN evt->iu.cmd.cancel_key == cancel_key)
* @evt: ibmvfc event struct
* @device: device to match (sdev)
*
* Returns:
* 1 if event matches sdev / 0 if event does not match sdev
**/
static int ibmvfc_match_lun(struct ibmvfc_event *evt, void *device)
{
if (evt->cmnd && evt->cmnd->device == device)
return 1; return 1;
return 0; return 0;
} }
/** /**
* ibmvfc_wait_for_ops - Wait for ops to complete * ibmvfc_abort_task_set - Abort outstanding commands to the device
* @vhost: ibmvfc host struct * @sdev: scsi device to abort commands
* @device: device to match (starget or sdev) *
* @match: match function * This sends an Abort Task Set to the VIOS for the specified device. This does
* NOT send any cancel to the VIOS. That must be done separately.
* *
* Returns: * Returns:
* SUCCESS / FAILED * 0 on success / other on failure
**/ **/
static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device, static int ibmvfc_abort_task_set(struct scsi_device *sdev)
int (*match) (struct ibmvfc_event *, void *))
{ {
struct ibmvfc_event *evt; struct ibmvfc_host *vhost = shost_priv(sdev->host);
DECLARE_COMPLETION_ONSTACK(comp); struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
int wait; struct ibmvfc_cmd *tmf;
unsigned long flags; struct ibmvfc_event *evt, *found_evt;
signed long timeout = IBMVFC_ABORT_WAIT_TIMEOUT * HZ; union ibmvfc_iu rsp_iu;
struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp;
int rc, rsp_rc = -EBUSY;
unsigned long flags, timeout = IBMVFC_ABORT_TIMEOUT;
int rsp_code = 0;
ENTER; spin_lock_irqsave(vhost->host->host_lock, flags);
do { found_evt = NULL;
wait = 0; list_for_each_entry(evt, &vhost->sent, queue) {
spin_lock_irqsave(vhost->host->host_lock, flags); if (evt->cmnd && evt->cmnd->device == sdev) {
list_for_each_entry(evt, &vhost->sent, queue) { found_evt = evt;
if (match(evt, device)) { break;
evt->eh_comp = &comp;
wait++;
}
} }
}
if (!found_evt) {
if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
sdev_printk(KERN_INFO, sdev, "No events found to abort\n");
spin_unlock_irqrestore(vhost->host->host_lock, flags); spin_unlock_irqrestore(vhost->host->host_lock, flags);
return 0;
}
if (wait) { if (vhost->state == IBMVFC_ACTIVE) {
timeout = wait_for_completion_timeout(&comp, timeout); evt = ibmvfc_get_event(vhost);
ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT);
if (!timeout) { tmf = &evt->iu.cmd;
wait = 0; memset(tmf, 0, sizeof(*tmf));
spin_lock_irqsave(vhost->host->host_lock, flags); tmf->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
list_for_each_entry(evt, &vhost->sent, queue) { tmf->resp.len = sizeof(tmf->rsp);
if (match(evt, device)) { tmf->frame_type = IBMVFC_SCSI_FCP_TYPE;
evt->eh_comp = NULL; tmf->payload_len = sizeof(tmf->iu);
wait++; tmf->resp_len = sizeof(tmf->rsp);
} tmf->cancel_key = (unsigned long)sdev->hostdata;
} tmf->tgt_scsi_id = rport->port_id;
spin_unlock_irqrestore(vhost->host->host_lock, flags); int_to_scsilun(sdev->lun, &tmf->iu.lun);
if (wait) tmf->flags = (IBMVFC_NO_MEM_DESC | IBMVFC_TMF);
dev_err(vhost->dev, "Timed out waiting for aborted commands\n"); tmf->iu.tmf_flags = IBMVFC_ABORT_TASK_SET;
LEAVE; evt->sync_iu = &rsp_iu;
return wait ? FAILED : SUCCESS;
} init_completion(&evt->comp);
rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
}
spin_unlock_irqrestore(vhost->host->host_lock, flags);
if (rsp_rc != 0) {
sdev_printk(KERN_ERR, sdev, "Failed to send abort. rc=%d\n", rsp_rc);
return -EIO;
}
sdev_printk(KERN_INFO, sdev, "Aborting outstanding commands\n");
timeout = wait_for_completion_timeout(&evt->comp, timeout);
if (!timeout) {
rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
if (!rc) {
rc = ibmvfc_wait_for_ops(vhost, sdev->hostdata, ibmvfc_match_key);
if (rc == SUCCESS)
rc = 0;
} }
} while (wait);
LEAVE; if (rc) {
return SUCCESS; sdev_printk(KERN_INFO, sdev, "Cancel failed, resetting host\n");
ibmvfc_reset_host(vhost);
rsp_rc = 0;
goto out;
}
}
if (rsp_iu.cmd.status)
rsp_code = ibmvfc_get_err_result(&rsp_iu.cmd);
if (rsp_code) {
if (fc_rsp->flags & FCP_RSP_LEN_VALID)
rsp_code = fc_rsp->data.info.rsp_code;
sdev_printk(KERN_ERR, sdev, "Abort failed: %s (%x:%x) "
"flags: %x fcp_rsp: %x, scsi_status: %x\n",
ibmvfc_get_cmd_error(rsp_iu.cmd.status, rsp_iu.cmd.error),
rsp_iu.cmd.status, rsp_iu.cmd.error, fc_rsp->flags, rsp_code,
fc_rsp->scsi_status);
rsp_rc = -EIO;
} else
sdev_printk(KERN_INFO, sdev, "Abort successful\n");
out:
spin_lock_irqsave(vhost->host->host_lock, flags);
ibmvfc_free_event(evt);
spin_unlock_irqrestore(vhost->host->host_lock, flags);
return rsp_rc;
} }
/** /**
...@@ -2353,18 +2408,6 @@ static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd) ...@@ -2353,18 +2408,6 @@ static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd)
return rc; return rc;
} }
/**
* ibmvfc_dev_cancel_all_abts - Device iterated cancel all function
* @sdev: scsi device struct
* @data: return code
*
**/
static void ibmvfc_dev_cancel_all_abts(struct scsi_device *sdev, void *data)
{
unsigned long *rc = data;
*rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
}
/** /**
* ibmvfc_dev_cancel_all_reset - Device iterated cancel all function * ibmvfc_dev_cancel_all_reset - Device iterated cancel all function
* @sdev: scsi device struct * @sdev: scsi device struct
...@@ -2377,18 +2420,6 @@ static void ibmvfc_dev_cancel_all_reset(struct scsi_device *sdev, void *data) ...@@ -2377,18 +2420,6 @@ static void ibmvfc_dev_cancel_all_reset(struct scsi_device *sdev, void *data)
*rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_TGT_RESET); *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_TGT_RESET);
} }
/**
* ibmvfc_dev_abort_all - Device iterated abort task set function
* @sdev: scsi device struct
* @data: return code
*
**/
static void ibmvfc_dev_abort_all(struct scsi_device *sdev, void *data)
{
unsigned long *rc = data;
*rc |= ibmvfc_abort_task_set(sdev);
}
/** /**
* ibmvfc_eh_target_reset_handler - Reset the target * ibmvfc_eh_target_reset_handler - Reset the target
* @cmd: scsi command struct * @cmd: scsi command struct
...@@ -2443,19 +2474,22 @@ static int ibmvfc_eh_host_reset_handler(struct scsi_cmnd *cmd) ...@@ -2443,19 +2474,22 @@ static int ibmvfc_eh_host_reset_handler(struct scsi_cmnd *cmd)
**/ **/
static void ibmvfc_terminate_rport_io(struct fc_rport *rport) static void ibmvfc_terminate_rport_io(struct fc_rport *rport)
{ {
struct scsi_target *starget = to_scsi_target(&rport->dev); struct Scsi_Host *shost = rport_to_shost(rport);
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
struct ibmvfc_host *vhost = shost_priv(shost); struct ibmvfc_host *vhost = shost_priv(shost);
unsigned long cancel_rc = 0; struct fc_rport *dev_rport;
unsigned long abort_rc = 0; struct scsi_device *sdev;
int rc = FAILED; unsigned long rc;
ENTER; ENTER;
starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_abts); shost_for_each_device(sdev, shost) {
starget_for_each_device(starget, &abort_rc, ibmvfc_dev_abort_all); dev_rport = starget_to_rport(scsi_target(sdev));
if (dev_rport != rport)
continue;
ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
ibmvfc_abort_task_set(sdev);
}
if (!cancel_rc && !abort_rc) rc = ibmvfc_wait_for_ops(vhost, rport, ibmvfc_match_rport);
rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target);
if (rc == FAILED) if (rc == FAILED)
ibmvfc_issue_fc_host_lip(shost); ibmvfc_issue_fc_host_lip(shost);
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#define IBMVFC_ADISC_PLUS_CANCEL_TIMEOUT \ #define IBMVFC_ADISC_PLUS_CANCEL_TIMEOUT \
(IBMVFC_ADISC_TIMEOUT + IBMVFC_ADISC_CANCEL_TIMEOUT) (IBMVFC_ADISC_TIMEOUT + IBMVFC_ADISC_CANCEL_TIMEOUT)
#define IBMVFC_INIT_TIMEOUT 120 #define IBMVFC_INIT_TIMEOUT 120
#define IBMVFC_ABORT_TIMEOUT 8
#define IBMVFC_ABORT_WAIT_TIMEOUT 40 #define IBMVFC_ABORT_WAIT_TIMEOUT 40
#define IBMVFC_MAX_REQUESTS_DEFAULT 100 #define IBMVFC_MAX_REQUESTS_DEFAULT 100
......
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