[SCSI] ibmvfc: Fix terminate_rport_io
[pandora-kernel.git] / drivers / scsi / ibmvscsi / ibmvfc.c
index fef4952..9f75a6d 100644 (file)
@@ -433,6 +433,9 @@ static void ibmvfc_set_tgt_action(struct ibmvfc_target *tgt,
 {
        switch (tgt->action) {
        case IBMVFC_TGT_ACTION_DEL_RPORT:
+               if (action == IBMVFC_TGT_ACTION_DELETED_RPORT)
+                       tgt->action = action;
+       case IBMVFC_TGT_ACTION_DELETED_RPORT:
                break;
        default:
                if (action == IBMVFC_TGT_ACTION_DEL_RPORT)
@@ -504,12 +507,23 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
                if (vhost->action == IBMVFC_HOST_ACTION_ALLOC_TGTS)
                        vhost->action = action;
                break;
-       case IBMVFC_HOST_ACTION_LOGO:
        case IBMVFC_HOST_ACTION_INIT:
        case IBMVFC_HOST_ACTION_TGT_DEL:
+               switch (vhost->action) {
+               case IBMVFC_HOST_ACTION_RESET:
+               case IBMVFC_HOST_ACTION_REENABLE:
+                       break;
+               default:
+                       vhost->action = action;
+                       break;
+               };
+               break;
+       case IBMVFC_HOST_ACTION_LOGO:
        case IBMVFC_HOST_ACTION_QUERY_TGTS:
        case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
        case IBMVFC_HOST_ACTION_NONE:
+       case IBMVFC_HOST_ACTION_RESET:
+       case IBMVFC_HOST_ACTION_REENABLE:
        default:
                vhost->action = action;
                break;
@@ -641,7 +655,7 @@ static int ibmvfc_send_crq_init_complete(struct ibmvfc_host *vhost)
  **/
 static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost)
 {
-       long rc;
+       long rc = 0;
        struct vio_dev *vdev = to_vio_dev(vhost->dev);
        struct ibmvfc_crq_queue *crq = &vhost->crq;
 
@@ -649,6 +663,8 @@ static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost)
        free_irq(vdev->irq, vhost);
        tasklet_kill(&vhost->tasklet);
        do {
+               if (rc)
+                       msleep(100);
                rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
        } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
 
@@ -667,11 +683,13 @@ static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost)
  **/
 static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)
 {
-       int rc;
+       int rc = 0;
        struct vio_dev *vdev = to_vio_dev(vhost->dev);
 
        /* Re-enable the CRQ */
        do {
+               if (rc)
+                       msleep(100);
                rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address);
        } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc));
 
@@ -690,15 +708,19 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)
  **/
 static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
 {
-       int rc;
+       int rc = 0;
+       unsigned long flags;
        struct vio_dev *vdev = to_vio_dev(vhost->dev);
        struct ibmvfc_crq_queue *crq = &vhost->crq;
 
        /* Close the CRQ */
        do {
+               if (rc)
+                       msleep(100);
                rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
        } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
 
+       spin_lock_irqsave(vhost->host->host_lock, flags);
        vhost->state = IBMVFC_NO_CRQ;
        vhost->logged_in = 0;
        ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
@@ -716,6 +738,7 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
                dev_warn(vhost->dev, "Partner adapter not ready\n");
        else if (rc != 0)
                dev_warn(vhost->dev, "Couldn't register crq (rc=%d)\n", rc);
+       spin_unlock_irqrestore(vhost->host->host_lock, flags);
 
        return rc;
 }
@@ -821,17 +844,9 @@ static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code)
  **/
 static void ibmvfc_hard_reset_host(struct ibmvfc_host *vhost)
 {
-       int rc;
-
-       scsi_block_requests(vhost->host);
        ibmvfc_purge_requests(vhost, DID_ERROR);
-       if ((rc = ibmvfc_reset_crq(vhost)) ||
-           (rc = ibmvfc_send_crq_init(vhost)) ||
-           (rc = vio_enable_interrupts(to_vio_dev(vhost->dev)))) {
-               dev_err(vhost->dev, "Error after reset rc=%d\n", rc);
-               ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
-       } else
-               ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+       ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+       ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_RESET);
 }
 
 /**
@@ -2024,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;
 }
 
 /**
@@ -2200,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;
 }
 
 /**
@@ -2299,6 +2369,7 @@ static int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd)
        int rc = FAILED;
 
        ENTER;
+       fc_block_scsi_eh(cmd);
        ibmvfc_wait_while_resetting(vhost);
        cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
        abort_rc = ibmvfc_abort_task_set(sdev);
@@ -2325,6 +2396,7 @@ static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd)
        int rc = FAILED;
 
        ENTER;
+       fc_block_scsi_eh(cmd);
        ibmvfc_wait_while_resetting(vhost);
        cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_LUN_RESET);
        reset_rc = ibmvfc_reset_device(sdev, IBMVFC_LUN_RESET, "LUN");
@@ -2336,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
@@ -2360,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
@@ -2389,6 +2437,7 @@ static int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd)
        unsigned long cancel_rc = 0;
 
        ENTER;
+       fc_block_scsi_eh(cmd);
        ibmvfc_wait_while_resetting(vhost);
        starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_reset);
        reset_rc = ibmvfc_reset_device(sdev, IBMVFC_TARGET_RESET, "target");
@@ -2410,6 +2459,7 @@ static int ibmvfc_eh_host_reset_handler(struct scsi_cmnd *cmd)
        int rc;
        struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
 
+       fc_block_scsi_eh(cmd);
        dev_err(vhost->dev, "Resetting connection due to error recovery\n");
        rc = ibmvfc_issue_fc_host_lip(vhost->host);
        return rc ? FAILED : SUCCESS;
@@ -2424,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);
@@ -2606,22 +2659,13 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost)
                        dev_info(vhost->dev, "Re-enabling adapter\n");
                        vhost->client_migrated = 1;
                        ibmvfc_purge_requests(vhost, DID_REQUEUE);
-                       if ((rc = ibmvfc_reenable_crq_queue(vhost)) ||
-                           (rc = ibmvfc_send_crq_init(vhost))) {
-                               ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
-                               dev_err(vhost->dev, "Error after enable (rc=%ld)\n", rc);
-                       } else
-                               ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+                       ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+                       ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_REENABLE);
                } else {
                        dev_err(vhost->dev, "Virtual adapter failed (rc=%d)\n", crq->format);
-
                        ibmvfc_purge_requests(vhost, DID_ERROR);
-                       if ((rc = ibmvfc_reset_crq(vhost)) ||
-                           (rc = ibmvfc_send_crq_init(vhost))) {
-                               ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
-                               dev_err(vhost->dev, "Error after reset (rc=%ld)\n", rc);
-                       } else
-                               ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+                       ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+                       ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_RESET);
                }
                return;
        case IBMVFC_CRQ_CMD_RSP:
@@ -4123,6 +4167,8 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
        case IBMVFC_HOST_ACTION_TGT_DEL:
        case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
        case IBMVFC_HOST_ACTION_QUERY:
+       case IBMVFC_HOST_ACTION_RESET:
+       case IBMVFC_HOST_ACTION_REENABLE:
        default:
                break;
        };
@@ -4184,11 +4230,15 @@ static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt)
        if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
                tgt_dbg(tgt, "Deleting rport\n");
                list_del(&tgt->queue);
+               ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
                spin_unlock_irqrestore(vhost->host->host_lock, flags);
                fc_remote_port_delete(rport);
                del_timer_sync(&tgt->timer);
                kref_put(&tgt->kref, ibmvfc_release_tgt);
                return;
+       } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DELETED_RPORT) {
+               spin_unlock_irqrestore(vhost->host->host_lock, flags);
+               return;
        }
 
        if (rport) {
@@ -4220,6 +4270,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
        struct ibmvfc_target *tgt;
        unsigned long flags;
        struct fc_rport *rport;
+       int rc;
 
        ibmvfc_log_ae(vhost, vhost->events_to_log);
        spin_lock_irqsave(vhost->host->host_lock, flags);
@@ -4229,6 +4280,27 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
        case IBMVFC_HOST_ACTION_LOGO_WAIT:
        case IBMVFC_HOST_ACTION_INIT_WAIT:
                break;
+       case IBMVFC_HOST_ACTION_RESET:
+               vhost->action = IBMVFC_HOST_ACTION_TGT_DEL;
+               spin_unlock_irqrestore(vhost->host->host_lock, flags);
+               rc = ibmvfc_reset_crq(vhost);
+               spin_lock_irqsave(vhost->host->host_lock, flags);
+               if (rc || (rc = ibmvfc_send_crq_init(vhost)) ||
+                   (rc = vio_enable_interrupts(to_vio_dev(vhost->dev)))) {
+                       ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+                       dev_err(vhost->dev, "Error after reset (rc=%d)\n", rc);
+               }
+               break;
+       case IBMVFC_HOST_ACTION_REENABLE:
+               vhost->action = IBMVFC_HOST_ACTION_TGT_DEL;
+               spin_unlock_irqrestore(vhost->host->host_lock, flags);
+               rc = ibmvfc_reenable_crq_queue(vhost);
+               spin_lock_irqsave(vhost->host->host_lock, flags);
+               if (rc || (rc = ibmvfc_send_crq_init(vhost))) {
+                       ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+                       dev_err(vhost->dev, "Error after enable (rc=%d)\n", rc);
+               }
+               break;
        case IBMVFC_HOST_ACTION_LOGO:
                vhost->job_step(vhost);
                break;
@@ -4266,6 +4338,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
                                rport = tgt->rport;
                                tgt->rport = NULL;
                                list_del(&tgt->queue);
+                               ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
                                spin_unlock_irqrestore(vhost->host->host_lock, flags);
                                if (rport)
                                        fc_remote_port_delete(rport);