|
@@ -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
|
|
|
- * @sdev: scsi device to abort commands
|
|
|
- *
|
|
|
- * 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.
|
|
|
+ * ibmvfc_match_rport - Match function for specified remote port
|
|
|
+ * @evt: ibmvfc event struct
|
|
|
+ * @device: device to match (rport)
|
|
|
*
|
|
|
* 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 *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;
|
|
|
+ struct fc_rport *cmd_rport;
|
|
|
|
|
|
- spin_lock_irqsave(vhost->host->host_lock, flags);
|
|
|
- found_evt = NULL;
|
|
|
- list_for_each_entry(evt, &vhost->sent, queue) {
|
|
|
- if (evt->cmnd && evt->cmnd->device == sdev) {
|
|
|
- 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);
|
|
|
+ if (evt->cmnd) {
|
|
|
+ cmd_rport = starget_to_rport(scsi_target(evt->cmnd->device));
|
|
|
+ if (cmd_rport == rport)
|
|
|
+ return 1;
|
|
|
}
|
|
|
+ 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);
|
|
|
- return -EIO;
|
|
|
- }
|
|
|
+/**
|
|
|
+ * ibmvfc_match_lun - Match function for specified LUN
|
|
|
+ * @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)
|
|
|
- rsp_code = ibmvfc_get_err_result(&rsp_iu.cmd);
|
|
|
+ ENTER;
|
|
|
+ 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 = ∁
|
|
|
+ wait++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
|
|
|
|
|
|
- if (rsp_code) {
|
|
|
- if (fc_rsp->flags & FCP_RSP_LEN_VALID)
|
|
|
- rsp_code = fc_rsp->data.info.rsp_code;
|
|
|
+ if (wait) {
|
|
|
+ timeout = wait_for_completion_timeout(&comp, timeout);
|
|
|
|
|
|
- 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");
|
|
|
+ if (!timeout) {
|
|
|
+ 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 = NULL;
|
|
|
+ wait++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ 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);
|
|
|
- ibmvfc_free_event(evt);
|
|
|
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
|
|
|
- return rsp_rc;
|
|
|
+ LEAVE;
|
|
|
+ return SUCCESS;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -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
|
|
|
- * @device: device to match (starget)
|
|
|
+ * @key: cancel key to match
|
|
|
*
|
|
|
* 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)
|
|
|
- return 1;
|
|
|
- return 0;
|
|
|
-}
|
|
|
+ unsigned long cancel_key = (unsigned long)key;
|
|
|
|
|
|
-/**
|
|
|
- * ibmvfc_match_lun - Match function for specified LUN
|
|
|
- * @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)
|
|
|
+ if (evt->crq.format == IBMVFC_CMD_FORMAT &&
|
|
|
+ evt->iu.cmd.cancel_key == cancel_key)
|
|
|
return 1;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * ibmvfc_wait_for_ops - Wait for ops to complete
|
|
|
- * @vhost: ibmvfc host struct
|
|
|
- * @device: device to match (starget or sdev)
|
|
|
- * @match: match function
|
|
|
+ * ibmvfc_abort_task_set - Abort outstanding commands to the device
|
|
|
+ * @sdev: scsi device to abort commands
|
|
|
+ *
|
|
|
+ * 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:
|
|
|
- * SUCCESS / FAILED
|
|
|
+ * 0 on success / other on failure
|
|
|
**/
|
|
|
-static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device,
|
|
|
- int (*match) (struct ibmvfc_event *, void *))
|
|
|
+static int ibmvfc_abort_task_set(struct scsi_device *sdev)
|
|
|
{
|
|
|
- struct ibmvfc_event *evt;
|
|
|
- DECLARE_COMPLETION_ONSTACK(comp);
|
|
|
- int wait;
|
|
|
- unsigned long flags;
|
|
|
- signed long timeout = IBMVFC_ABORT_WAIT_TIMEOUT * HZ;
|
|
|
+ struct ibmvfc_host *vhost = shost_priv(sdev->host);
|
|
|
+ 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 rc, rsp_rc = -EBUSY;
|
|
|
+ unsigned long flags, timeout = IBMVFC_ABORT_TIMEOUT;
|
|
|
+ int rsp_code = 0;
|
|
|
|
|
|
- ENTER;
|
|
|
- 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 = ∁
|
|
|
- wait++;
|
|
|
- }
|
|
|
+ spin_lock_irqsave(vhost->host->host_lock, flags);
|
|
|
+ found_evt = NULL;
|
|
|
+ list_for_each_entry(evt, &vhost->sent, queue) {
|
|
|
+ if (evt->cmnd && evt->cmnd->device == sdev) {
|
|
|
+ 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 (wait) {
|
|
|
- timeout = wait_for_completion_timeout(&comp, timeout);
|
|
|
+ if (vhost->state == IBMVFC_ACTIVE) {
|
|
|
+ evt = ibmvfc_get_event(vhost);
|
|
|
+ ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT);
|
|
|
|
|
|
- if (!timeout) {
|
|
|
- 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 = NULL;
|
|
|
- wait++;
|
|
|
- }
|
|
|
- }
|
|
|
- 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;
|
|
|
- }
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ 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;
|
|
|
- return SUCCESS;
|
|
|
+ if (rc) {
|
|
|
+ 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)
|
|
|
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
|
|
|
* @sdev: scsi device struct
|
|
@@ -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);
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * 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
|
|
|
* @cmd: scsi command struct
|
|
@@ -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)
|
|
|
{
|
|
|
- struct scsi_target *starget = to_scsi_target(&rport->dev);
|
|
|
- struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
|
|
|
+ struct Scsi_Host *shost = rport_to_shost(rport);
|
|
|
struct ibmvfc_host *vhost = shost_priv(shost);
|
|
|
- unsigned long cancel_rc = 0;
|
|
|
- unsigned long abort_rc = 0;
|
|
|
- int rc = FAILED;
|
|
|
+ struct fc_rport *dev_rport;
|
|
|
+ struct scsi_device *sdev;
|
|
|
+ unsigned long rc;
|
|
|
|
|
|
ENTER;
|
|
|
- starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_abts);
|
|
|
- starget_for_each_device(starget, &abort_rc, ibmvfc_dev_abort_all);
|
|
|
+ shost_for_each_device(sdev, shost) {
|
|
|
+ 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, starget, ibmvfc_match_target);
|
|
|
+ rc = ibmvfc_wait_for_ops(vhost, rport, ibmvfc_match_rport);
|
|
|
|
|
|
if (rc == FAILED)
|
|
|
ibmvfc_issue_fc_host_lip(shost);
|