aboutsummaryrefslogtreecommitdiff
path: root/drivers/scsi/ibmvscsi/ibmvfc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/ibmvscsi/ibmvfc.c')
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c238
1 files changed, 169 insertions, 69 deletions
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 9f75a6d519a..8dd47689d58 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -50,7 +50,6 @@ static unsigned int max_lun = IBMVFC_MAX_LUN;
static unsigned int max_targets = IBMVFC_MAX_TARGETS;
static unsigned int max_requests = IBMVFC_MAX_REQUESTS_DEFAULT;
static unsigned int disc_threads = IBMVFC_MAX_DISC_THREADS;
-static unsigned int dev_loss_tmo = IBMVFC_DEV_LOSS_TMO;
static unsigned int ibmvfc_debug = IBMVFC_DEBUG;
static unsigned int log_level = IBMVFC_DEFAULT_LOG_LEVEL;
static LIST_HEAD(ibmvfc_head);
@@ -84,11 +83,6 @@ MODULE_PARM_DESC(disc_threads, "Number of device discovery threads to use. "
module_param_named(debug, ibmvfc_debug, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Enable driver debug information. "
"[Default=" __stringify(IBMVFC_DEBUG) "]");
-module_param_named(dev_loss_tmo, dev_loss_tmo, uint, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(dev_loss_tmo, "Maximum number of seconds that the FC "
- "transport should insulate the loss of a remote port. Once this "
- "value is exceeded, the scsi target is removed. "
- "[Default=" __stringify(IBMVFC_DEV_LOSS_TMO) "]");
module_param_named(log_level, log_level, uint, 0);
MODULE_PARM_DESC(log_level, "Set to 0 - 4 for increasing verbosity of device driver. "
"[Default=" __stringify(IBMVFC_DEFAULT_LOG_LEVEL) "]");
@@ -1612,7 +1606,7 @@ static inline int ibmvfc_host_chkready(struct ibmvfc_host *vhost)
* Returns:
* 0 on success / other on failure
**/
-static int ibmvfc_queuecommand(struct scsi_cmnd *cmnd,
+static int ibmvfc_queuecommand_lck(struct scsi_cmnd *cmnd,
void (*done) (struct scsi_cmnd *))
{
struct ibmvfc_host *vhost = shost_priv(cmnd->device->host);
@@ -1678,6 +1672,8 @@ static int ibmvfc_queuecommand(struct scsi_cmnd *cmnd,
return 0;
}
+static DEF_SCSI_QCMD(ibmvfc_queuecommand)
+
/**
* ibmvfc_sync_completion - Signal that a synchronous command has completed
* @evt: ibmvfc event struct
@@ -2183,7 +2179,7 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
return 0;
}
- if (vhost->state == IBMVFC_ACTIVE) {
+ if (vhost->logged_in) {
evt = ibmvfc_get_event(vhost);
ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT);
@@ -2194,7 +2190,12 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
tmf->common.length = sizeof(*tmf);
tmf->scsi_id = rport->port_id;
int_to_scsilun(sdev->lun, &tmf->lun);
- tmf->flags = (type | IBMVFC_TMF_LUA_VALID);
+ if (!(vhost->login_buf->resp.capabilities & IBMVFC_CAN_SUPPRESS_ABTS))
+ type &= ~IBMVFC_TMF_SUPPRESS_ABTS;
+ if (vhost->state == IBMVFC_ACTIVE)
+ tmf->flags = (type | IBMVFC_TMF_LUA_VALID);
+ else
+ tmf->flags = ((type & IBMVFC_TMF_SUPPRESS_ABTS) | IBMVFC_TMF_LUA_VALID);
tmf->cancel_key = (unsigned long)sdev->hostdata;
tmf->my_cancel_key = (unsigned long)starget->hostdata;
@@ -2207,7 +2208,10 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
if (rsp_rc != 0) {
sdev_printk(KERN_ERR, sdev, "Failed to send cancel event. rc=%d\n", rsp_rc);
- return -EIO;
+ /* If failure is received, the host adapter is most likely going
+ through reset, return success so the caller will wait for the command
+ being cancelled to get returned */
+ return 0;
}
sdev_printk(KERN_INFO, sdev, "Cancelling outstanding commands.\n");
@@ -2220,7 +2224,15 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
if (status != IBMVFC_MAD_SUCCESS) {
sdev_printk(KERN_WARNING, sdev, "Cancel failed with rc=%x\n", status);
- return -EIO;
+ switch (status) {
+ case IBMVFC_MAD_DRIVER_FAILED:
+ case IBMVFC_MAD_CRQ_ERROR:
+ /* Host adapter most likely going through reset, return success to
+ the caller will wait for the command being cancelled to get returned */
+ return 0;
+ default:
+ return -EIO;
+ };
}
sdev_printk(KERN_INFO, sdev, "Successfully cancelled outstanding commands\n");
@@ -2246,6 +2258,21 @@ static int ibmvfc_match_key(struct ibmvfc_event *evt, void *key)
}
/**
+ * ibmvfc_match_evt - Match function for specified event
+ * @evt: ibmvfc event struct
+ * @match: event to match
+ *
+ * Returns:
+ * 1 if event matches key / 0 if event does not match key
+ **/
+static int ibmvfc_match_evt(struct ibmvfc_event *evt, void *match)
+{
+ if (evt == match)
+ return 1;
+ return 0;
+}
+
+/**
* ibmvfc_abort_task_set - Abort outstanding commands to the device
* @sdev: scsi device to abort commands
*
@@ -2316,7 +2343,7 @@ static int ibmvfc_abort_task_set(struct scsi_device *sdev)
timeout = wait_for_completion_timeout(&evt->comp, timeout);
if (!timeout) {
- rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
+ rc = ibmvfc_cancel_all(sdev, 0);
if (!rc) {
rc = ibmvfc_wait_for_ops(vhost, sdev->hostdata, ibmvfc_match_key);
if (rc == SUCCESS)
@@ -2326,7 +2353,20 @@ static int ibmvfc_abort_task_set(struct scsi_device *sdev)
if (rc) {
sdev_printk(KERN_INFO, sdev, "Cancel failed, resetting host\n");
ibmvfc_reset_host(vhost);
- rsp_rc = 0;
+ rsp_rc = -EIO;
+ rc = ibmvfc_wait_for_ops(vhost, sdev->hostdata, ibmvfc_match_key);
+
+ if (rc == SUCCESS)
+ rsp_rc = 0;
+
+ rc = ibmvfc_wait_for_ops(vhost, evt, ibmvfc_match_evt);
+ if (rc != SUCCESS) {
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ ibmvfc_hard_reset_host(vhost);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ rsp_rc = 0;
+ }
+
goto out;
}
}
@@ -2359,24 +2399,30 @@ out:
* @cmd: scsi command to abort
*
* Returns:
- * SUCCESS / FAILED
+ * SUCCESS / FAST_IO_FAIL / FAILED
**/
static int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd)
{
struct scsi_device *sdev = cmd->device;
struct ibmvfc_host *vhost = shost_priv(sdev->host);
- int cancel_rc, abort_rc;
+ int cancel_rc, block_rc;
int rc = FAILED;
ENTER;
- fc_block_scsi_eh(cmd);
+ block_rc = 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);
+ if (block_rc != FAST_IO_FAIL) {
+ cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
+ ibmvfc_abort_task_set(sdev);
+ } else
+ cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS);
- if (!cancel_rc && !abort_rc)
+ if (!cancel_rc)
rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun);
+ if (block_rc == FAST_IO_FAIL && rc != FAILED)
+ rc = FAST_IO_FAIL;
+
LEAVE;
return rc;
}
@@ -2386,29 +2432,47 @@ static int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd)
* @cmd: scsi command struct
*
* Returns:
- * SUCCESS / FAILED
+ * SUCCESS / FAST_IO_FAIL / FAILED
**/
static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd)
{
struct scsi_device *sdev = cmd->device;
struct ibmvfc_host *vhost = shost_priv(sdev->host);
- int cancel_rc, reset_rc;
+ int cancel_rc, block_rc, reset_rc = 0;
int rc = FAILED;
ENTER;
- fc_block_scsi_eh(cmd);
+ block_rc = 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");
+ if (block_rc != FAST_IO_FAIL) {
+ cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_LUN_RESET);
+ reset_rc = ibmvfc_reset_device(sdev, IBMVFC_LUN_RESET, "LUN");
+ } else
+ cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS);
if (!cancel_rc && !reset_rc)
rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun);
+ if (block_rc == FAST_IO_FAIL && rc != FAILED)
+ rc = FAST_IO_FAIL;
+
LEAVE;
return rc;
}
/**
+ * ibmvfc_dev_cancel_all_noreset - Device iterated cancel all function
+ * @sdev: scsi device struct
+ * @data: return code
+ *
+ **/
+static void ibmvfc_dev_cancel_all_noreset(struct scsi_device *sdev, void *data)
+{
+ unsigned long *rc = data;
+ *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS);
+}
+
+/**
* ibmvfc_dev_cancel_all_reset - Device iterated cancel all function
* @sdev: scsi device struct
* @data: return code
@@ -2425,26 +2489,33 @@ static void ibmvfc_dev_cancel_all_reset(struct scsi_device *sdev, void *data)
* @cmd: scsi command struct
*
* Returns:
- * SUCCESS / FAILED
+ * SUCCESS / FAST_IO_FAIL / FAILED
**/
static int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd)
{
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 block_rc;
+ int reset_rc = 0;
int rc = FAILED;
unsigned long cancel_rc = 0;
ENTER;
- fc_block_scsi_eh(cmd);
+ block_rc = 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");
+ if (block_rc != FAST_IO_FAIL) {
+ starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_reset);
+ reset_rc = ibmvfc_reset_device(sdev, IBMVFC_TARGET_RESET, "target");
+ } else
+ starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_noreset);
if (!cancel_rc && !reset_rc)
rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target);
+ if (block_rc == FAST_IO_FAIL && rc != FAILED)
+ rc = FAST_IO_FAIL;
+
LEAVE;
return rc;
}
@@ -2456,12 +2527,16 @@ static int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd)
**/
static int ibmvfc_eh_host_reset_handler(struct scsi_cmnd *cmd)
{
- int rc;
+ int rc, block_rc;
struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
- fc_block_scsi_eh(cmd);
+ block_rc = fc_block_scsi_eh(cmd);
dev_err(vhost->dev, "Resetting connection due to error recovery\n");
rc = ibmvfc_issue_fc_host_lip(vhost->host);
+
+ if (block_rc == FAST_IO_FAIL)
+ return FAST_IO_FAIL;
+
return rc ? FAILED : SUCCESS;
}
@@ -2485,8 +2560,7 @@ static void ibmvfc_terminate_rport_io(struct fc_rport *rport)
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);
+ ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS);
}
rc = ibmvfc_wait_for_ops(vhost, rport, ibmvfc_match_rport);
@@ -2496,41 +2570,66 @@ static void ibmvfc_terminate_rport_io(struct fc_rport *rport)
LEAVE;
}
-static const struct {
- enum ibmvfc_async_event ae;
- const char *desc;
-} ae_desc [] = {
- { IBMVFC_AE_ELS_PLOGI, "PLOGI" },
- { IBMVFC_AE_ELS_LOGO, "LOGO" },
- { IBMVFC_AE_ELS_PRLO, "PRLO" },
- { IBMVFC_AE_SCN_NPORT, "N-Port SCN" },
- { IBMVFC_AE_SCN_GROUP, "Group SCN" },
- { IBMVFC_AE_SCN_DOMAIN, "Domain SCN" },
- { IBMVFC_AE_SCN_FABRIC, "Fabric SCN" },
- { IBMVFC_AE_LINK_UP, "Link Up" },
- { IBMVFC_AE_LINK_DOWN, "Link Down" },
- { IBMVFC_AE_LINK_DEAD, "Link Dead" },
- { IBMVFC_AE_HALT, "Halt" },
- { IBMVFC_AE_RESUME, "Resume" },
- { IBMVFC_AE_ADAPTER_FAILED, "Adapter Failed" },
+static const struct ibmvfc_async_desc ae_desc [] = {
+ { "PLOGI", IBMVFC_AE_ELS_PLOGI, IBMVFC_DEFAULT_LOG_LEVEL + 1 },
+ { "LOGO", IBMVFC_AE_ELS_LOGO, IBMVFC_DEFAULT_LOG_LEVEL + 1 },
+ { "PRLO", IBMVFC_AE_ELS_PRLO, IBMVFC_DEFAULT_LOG_LEVEL + 1 },
+ { "N-Port SCN", IBMVFC_AE_SCN_NPORT, IBMVFC_DEFAULT_LOG_LEVEL + 1 },
+ { "Group SCN", IBMVFC_AE_SCN_GROUP, IBMVFC_DEFAULT_LOG_LEVEL + 1 },
+ { "Domain SCN", IBMVFC_AE_SCN_DOMAIN, IBMVFC_DEFAULT_LOG_LEVEL },
+ { "Fabric SCN", IBMVFC_AE_SCN_FABRIC, IBMVFC_DEFAULT_LOG_LEVEL },
+ { "Link Up", IBMVFC_AE_LINK_UP, IBMVFC_DEFAULT_LOG_LEVEL },
+ { "Link Down", IBMVFC_AE_LINK_DOWN, IBMVFC_DEFAULT_LOG_LEVEL },
+ { "Link Dead", IBMVFC_AE_LINK_DEAD, IBMVFC_DEFAULT_LOG_LEVEL },
+ { "Halt", IBMVFC_AE_HALT, IBMVFC_DEFAULT_LOG_LEVEL },
+ { "Resume", IBMVFC_AE_RESUME, IBMVFC_DEFAULT_LOG_LEVEL },
+ { "Adapter Failed", IBMVFC_AE_ADAPTER_FAILED, IBMVFC_DEFAULT_LOG_LEVEL },
};
-static const char *unknown_ae = "Unknown async";
+static const struct ibmvfc_async_desc unknown_ae = {
+ "Unknown async", 0, IBMVFC_DEFAULT_LOG_LEVEL
+};
/**
* ibmvfc_get_ae_desc - Get text description for async event
* @ae: async event
*
**/
-static const char *ibmvfc_get_ae_desc(u64 ae)
+static const struct ibmvfc_async_desc *ibmvfc_get_ae_desc(u64 ae)
{
int i;
for (i = 0; i < ARRAY_SIZE(ae_desc); i++)
if (ae_desc[i].ae == ae)
- return ae_desc[i].desc;
+ return &ae_desc[i];
- return unknown_ae;
+ return &unknown_ae;
+}
+
+static const struct {
+ enum ibmvfc_ae_link_state state;
+ const char *desc;
+} link_desc [] = {
+ { IBMVFC_AE_LS_LINK_UP, " link up" },
+ { IBMVFC_AE_LS_LINK_BOUNCED, " link bounced" },
+ { IBMVFC_AE_LS_LINK_DOWN, " link down" },
+ { IBMVFC_AE_LS_LINK_DEAD, " link dead" },
+};
+
+/**
+ * ibmvfc_get_link_state - Get text description for link state
+ * @state: link state
+ *
+ **/
+static const char *ibmvfc_get_link_state(enum ibmvfc_ae_link_state state)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(link_desc); i++)
+ if (link_desc[i].state == state)
+ return link_desc[i].desc;
+
+ return "";
}
/**
@@ -2542,11 +2641,12 @@ static const char *ibmvfc_get_ae_desc(u64 ae)
static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
struct ibmvfc_host *vhost)
{
- const char *desc = ibmvfc_get_ae_desc(crq->event);
+ const struct ibmvfc_async_desc *desc = ibmvfc_get_ae_desc(crq->event);
struct ibmvfc_target *tgt;
- ibmvfc_log(vhost, 3, "%s event received. scsi_id: %llx, wwpn: %llx,"
- " node_name: %llx\n", desc, crq->scsi_id, crq->wwpn, crq->node_name);
+ ibmvfc_log(vhost, desc->log_level, "%s event received. scsi_id: %llx, wwpn: %llx,"
+ " node_name: %llx%s\n", desc->desc, crq->scsi_id, crq->wwpn, crq->node_name,
+ ibmvfc_get_link_state(crq->link_state));
switch (crq->event) {
case IBMVFC_AE_RESUME:
@@ -2575,8 +2675,10 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
case IBMVFC_AE_SCN_FABRIC:
case IBMVFC_AE_SCN_DOMAIN:
vhost->events_to_log |= IBMVFC_AE_RSCN;
- vhost->delay_init = 1;
- __ibmvfc_reset_host(vhost);
+ if (vhost->state < IBMVFC_HALTED) {
+ vhost->delay_init = 1;
+ __ibmvfc_reset_host(vhost);
+ }
break;
case IBMVFC_AE_SCN_NPORT:
case IBMVFC_AE_SCN_GROUP:
@@ -2788,7 +2890,6 @@ static int ibmvfc_target_alloc(struct scsi_target *starget)
static int ibmvfc_slave_configure(struct scsi_device *sdev)
{
struct Scsi_Host *shost = sdev->host;
- struct fc_rport *rport = starget_to_rport(sdev->sdev_target);
unsigned long flags = 0;
spin_lock_irqsave(shost->host_lock, flags);
@@ -2800,8 +2901,6 @@ static int ibmvfc_slave_configure(struct scsi_device *sdev)
scsi_activate_tcq(sdev, sdev->queue_depth);
} else
scsi_deactivate_tcq(sdev, sdev->queue_depth);
-
- rport->dev_loss_tmo = dev_loss_tmo;
spin_unlock_irqrestore(shost->host_lock, flags);
return 0;
}
@@ -4285,6 +4384,8 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
spin_unlock_irqrestore(vhost->host->host_lock, flags);
rc = ibmvfc_reset_crq(vhost);
spin_lock_irqsave(vhost->host->host_lock, flags);
+ if (rc == H_CLOSED)
+ vio_enable_interrupts(to_vio_dev(vhost->dev));
if (rc || (rc = ibmvfc_send_crq_init(vhost)) ||
(rc = vio_enable_interrupts(to_vio_dev(vhost->dev)))) {
ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
@@ -4414,7 +4515,7 @@ static int ibmvfc_work(void *data)
struct ibmvfc_host *vhost = data;
int rc;
- set_user_nice(current, -20);
+ set_user_nice(current, MIN_NICE);
while (1) {
rc = wait_event_interruptible(vhost->work_wait_q,
@@ -4744,6 +4845,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
if ((rc = scsi_add_host(shost, dev)))
goto release_event_pool;
+ fc_host_dev_loss_tmo(shost) = IBMVFC_DEV_LOSS_TMO;
+
if ((rc = ibmvfc_create_trace_file(&shost->shost_dev.kobj,
&ibmvfc_trace_attr))) {
dev_err(dev, "Failed to create trace file. rc=%d\n", rc);
@@ -4852,7 +4955,7 @@ static unsigned long ibmvfc_get_desired_dma(struct vio_dev *vdev)
return pool_dma + ((512 * 1024) * driver_template.cmd_per_lun);
}
-static struct vio_device_id ibmvfc_device_table[] __devinitdata = {
+static struct vio_device_id ibmvfc_device_table[] = {
{"fcp", "IBM,vfc-client"},
{ "", "" }
};
@@ -4867,11 +4970,8 @@ static struct vio_driver ibmvfc_driver = {
.probe = ibmvfc_probe,
.remove = ibmvfc_remove,
.get_desired_dma = ibmvfc_get_desired_dma,
- .driver = {
- .name = IBMVFC_NAME,
- .owner = THIS_MODULE,
- .pm = &ibmvfc_pm_ops,
- }
+ .name = IBMVFC_NAME,
+ .pm = &ibmvfc_pm_ops,
};
static struct fc_function_template ibmvfc_transport_functions = {