|
@@ -758,6 +758,9 @@ static void ibmvfc_scsi_eh_done(struct ibmvfc_event *evt)
|
|
|
cmnd->scsi_done(cmnd);
|
|
|
}
|
|
|
|
|
|
+ if (evt->eh_comp)
|
|
|
+ complete(evt->eh_comp);
|
|
|
+
|
|
|
ibmvfc_free_event(evt);
|
|
|
}
|
|
|
|
|
@@ -1245,6 +1248,7 @@ static void ibmvfc_init_event(struct ibmvfc_event *evt,
|
|
|
evt->sync_iu = NULL;
|
|
|
evt->crq.format = format;
|
|
|
evt->done = done;
|
|
|
+ evt->eh_comp = NULL;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1485,6 +1489,9 @@ static void ibmvfc_scsi_done(struct ibmvfc_event *evt)
|
|
|
cmnd->scsi_done(cmnd);
|
|
|
}
|
|
|
|
|
|
+ if (evt->eh_comp)
|
|
|
+ complete(evt->eh_comp);
|
|
|
+
|
|
|
ibmvfc_free_event(evt);
|
|
|
}
|
|
|
|
|
@@ -1785,7 +1792,8 @@ static int ibmvfc_abort_task_set(struct scsi_device *sdev)
|
|
|
static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
|
|
|
{
|
|
|
struct ibmvfc_host *vhost = shost_priv(sdev->host);
|
|
|
- struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
|
|
|
+ struct scsi_target *starget = scsi_target(sdev);
|
|
|
+ struct fc_rport *rport = starget_to_rport(starget);
|
|
|
struct ibmvfc_tmf *tmf;
|
|
|
struct ibmvfc_event *evt, *found_evt;
|
|
|
union ibmvfc_iu rsp;
|
|
@@ -1823,7 +1831,7 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
|
|
|
int_to_scsilun(sdev->lun, &tmf->lun);
|
|
|
tmf->flags = (type | IBMVFC_TMF_LUA_VALID);
|
|
|
tmf->cancel_key = (unsigned long)sdev->hostdata;
|
|
|
- tmf->my_cancel_key = (IBMVFC_TMF_CANCEL_KEY | (unsigned long)sdev->hostdata);
|
|
|
+ tmf->my_cancel_key = (unsigned long)starget->hostdata;
|
|
|
|
|
|
evt->sync_iu = &rsp;
|
|
|
init_completion(&evt->comp);
|
|
@@ -1854,6 +1862,91 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * 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;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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 = init_timeout * HZ;
|
|
|
+
|
|
|
+ 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 (wait) {
|
|
|
+ timeout = wait_for_completion_timeout(&comp, timeout);
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+ LEAVE;
|
|
|
+ return SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* ibmvfc_eh_abort_handler - Abort a command
|
|
|
* @cmd: scsi command to abort
|
|
@@ -1863,29 +1956,21 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
|
|
|
**/
|
|
|
static int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd)
|
|
|
{
|
|
|
- struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
|
|
|
- struct ibmvfc_event *evt, *pos;
|
|
|
+ struct scsi_device *sdev = cmd->device;
|
|
|
+ struct ibmvfc_host *vhost = shost_priv(sdev->host);
|
|
|
int cancel_rc, abort_rc;
|
|
|
- unsigned long flags;
|
|
|
+ int rc = FAILED;
|
|
|
|
|
|
ENTER;
|
|
|
ibmvfc_wait_while_resetting(vhost);
|
|
|
- cancel_rc = ibmvfc_cancel_all(cmd->device, IBMVFC_TMF_ABORT_TASK_SET);
|
|
|
- abort_rc = ibmvfc_abort_task_set(cmd->device);
|
|
|
+ cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
|
|
|
+ abort_rc = ibmvfc_abort_task_set(sdev);
|
|
|
|
|
|
- if (!cancel_rc && !abort_rc) {
|
|
|
- spin_lock_irqsave(vhost->host->host_lock, flags);
|
|
|
- list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
|
|
|
- if (evt->cmnd && evt->cmnd->device == cmd->device)
|
|
|
- ibmvfc_fail_request(evt, DID_ABORT);
|
|
|
- }
|
|
|
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
|
|
|
- LEAVE;
|
|
|
- return SUCCESS;
|
|
|
- }
|
|
|
+ if (!cancel_rc && !abort_rc)
|
|
|
+ rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun);
|
|
|
|
|
|
LEAVE;
|
|
|
- return FAILED;
|
|
|
+ return rc;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1897,29 +1982,21 @@ static int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd)
|
|
|
**/
|
|
|
static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd)
|
|
|
{
|
|
|
- struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
|
|
|
- struct ibmvfc_event *evt, *pos;
|
|
|
+ struct scsi_device *sdev = cmd->device;
|
|
|
+ struct ibmvfc_host *vhost = shost_priv(sdev->host);
|
|
|
int cancel_rc, reset_rc;
|
|
|
- unsigned long flags;
|
|
|
+ int rc = FAILED;
|
|
|
|
|
|
ENTER;
|
|
|
ibmvfc_wait_while_resetting(vhost);
|
|
|
- cancel_rc = ibmvfc_cancel_all(cmd->device, IBMVFC_TMF_LUN_RESET);
|
|
|
- reset_rc = ibmvfc_reset_device(cmd->device, IBMVFC_LUN_RESET, "LUN");
|
|
|
+ cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_LUN_RESET);
|
|
|
+ reset_rc = ibmvfc_reset_device(sdev, IBMVFC_LUN_RESET, "LUN");
|
|
|
|
|
|
- if (!cancel_rc && !reset_rc) {
|
|
|
- spin_lock_irqsave(vhost->host->host_lock, flags);
|
|
|
- list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
|
|
|
- if (evt->cmnd && evt->cmnd->device == cmd->device)
|
|
|
- ibmvfc_fail_request(evt, DID_ABORT);
|
|
|
- }
|
|
|
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
|
|
|
- LEAVE;
|
|
|
- return SUCCESS;
|
|
|
- }
|
|
|
+ if (!cancel_rc && !reset_rc)
|
|
|
+ rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun);
|
|
|
|
|
|
LEAVE;
|
|
|
- return FAILED;
|
|
|
+ return rc;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1955,31 +2032,23 @@ static void ibmvfc_dev_abort_all(struct scsi_device *sdev, void *data)
|
|
|
**/
|
|
|
static int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd)
|
|
|
{
|
|
|
- struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
|
|
|
- struct scsi_target *starget = scsi_target(cmd->device);
|
|
|
- struct ibmvfc_event *evt, *pos;
|
|
|
+ struct scsi_device *sdev = cmd->device;
|
|
|
+ struct ibmvfc_host *vhost = shost_priv(sdev->host);
|
|
|
+ struct scsi_target *starget = scsi_target(sdev);
|
|
|
int reset_rc;
|
|
|
+ int rc = FAILED;
|
|
|
unsigned long cancel_rc = 0;
|
|
|
- unsigned long flags;
|
|
|
|
|
|
ENTER;
|
|
|
ibmvfc_wait_while_resetting(vhost);
|
|
|
starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all);
|
|
|
- reset_rc = ibmvfc_reset_device(cmd->device, IBMVFC_TARGET_RESET, "target");
|
|
|
+ reset_rc = ibmvfc_reset_device(sdev, IBMVFC_TARGET_RESET, "target");
|
|
|
|
|
|
- if (!cancel_rc && !reset_rc) {
|
|
|
- spin_lock_irqsave(vhost->host->host_lock, flags);
|
|
|
- list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
|
|
|
- if (evt->cmnd && scsi_target(evt->cmnd->device) == starget)
|
|
|
- ibmvfc_fail_request(evt, DID_ABORT);
|
|
|
- }
|
|
|
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
|
|
|
- LEAVE;
|
|
|
- return SUCCESS;
|
|
|
- }
|
|
|
+ if (!cancel_rc && !reset_rc)
|
|
|
+ rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target);
|
|
|
|
|
|
LEAVE;
|
|
|
- return FAILED;
|
|
|
+ return rc;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -2009,23 +2078,18 @@ 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 ibmvfc_host *vhost = shost_priv(shost);
|
|
|
- struct ibmvfc_event *evt, *pos;
|
|
|
unsigned long cancel_rc = 0;
|
|
|
unsigned long abort_rc = 0;
|
|
|
- unsigned long flags;
|
|
|
+ int rc = FAILED;
|
|
|
|
|
|
ENTER;
|
|
|
starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all);
|
|
|
starget_for_each_device(starget, &abort_rc, ibmvfc_dev_abort_all);
|
|
|
|
|
|
- if (!cancel_rc && !abort_rc) {
|
|
|
- spin_lock_irqsave(shost->host_lock, flags);
|
|
|
- list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
|
|
|
- if (evt->cmnd && scsi_target(evt->cmnd->device) == starget)
|
|
|
- ibmvfc_fail_request(evt, DID_ABORT);
|
|
|
- }
|
|
|
- spin_unlock_irqrestore(shost->host_lock, flags);
|
|
|
- } else
|
|
|
+ if (!cancel_rc && !abort_rc)
|
|
|
+ rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target);
|
|
|
+
|
|
|
+ if (rc == FAILED)
|
|
|
ibmvfc_issue_fc_host_lip(shost);
|
|
|
LEAVE;
|
|
|
}
|
|
@@ -2258,6 +2322,28 @@ static int ibmvfc_slave_alloc(struct scsi_device *sdev)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * ibmvfc_target_alloc - Setup the target's task set value
|
|
|
+ * @starget: struct scsi_target
|
|
|
+ *
|
|
|
+ * Set the target's task set value so that error handling works as
|
|
|
+ * expected.
|
|
|
+ *
|
|
|
+ * Returns:
|
|
|
+ * 0 on success / -ENXIO if device does not exist
|
|
|
+ **/
|
|
|
+static int ibmvfc_target_alloc(struct scsi_target *starget)
|
|
|
+{
|
|
|
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
|
|
|
+ struct ibmvfc_host *vhost = shost_priv(shost);
|
|
|
+ unsigned long flags = 0;
|
|
|
+
|
|
|
+ spin_lock_irqsave(shost->host_lock, flags);
|
|
|
+ starget->hostdata = (void *)(unsigned long)vhost->task_set++;
|
|
|
+ spin_unlock_irqrestore(shost->host_lock, flags);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* ibmvfc_slave_configure - Configure the device
|
|
|
* @sdev: struct scsi_device device to configure
|
|
@@ -2537,6 +2623,7 @@ static struct scsi_host_template driver_template = {
|
|
|
.eh_host_reset_handler = ibmvfc_eh_host_reset_handler,
|
|
|
.slave_alloc = ibmvfc_slave_alloc,
|
|
|
.slave_configure = ibmvfc_slave_configure,
|
|
|
+ .target_alloc = ibmvfc_target_alloc,
|
|
|
.scan_finished = ibmvfc_scan_finished,
|
|
|
.change_queue_depth = ibmvfc_change_queue_depth,
|
|
|
.change_queue_type = ibmvfc_change_queue_type,
|