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.c1443
1 files changed, 1068 insertions, 375 deletions
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index ea4abee7a2a..8dd47689d58 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -28,7 +28,9 @@
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>
+#include <linux/slab.h>
#include <linux/of.h>
+#include <linux/pm.h>
#include <linux/stringify.h>
#include <asm/firmware.h>
#include <asm/irq.h>
@@ -39,6 +41,7 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport_fc.h>
+#include <scsi/scsi_bsg_fc.h>
#include "ibmvfc.h"
static unsigned int init_timeout = IBMVFC_INIT_TIMEOUT;
@@ -47,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);
@@ -81,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) "]");
@@ -110,7 +107,7 @@ static const struct {
{ IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_DEAD, DID_ERROR, 0, 1, "transport dead" },
{ IBMVFC_FABRIC_MAPPED, IBMVFC_CONFIG_ERROR, DID_ERROR, 1, 1, "configuration error" },
{ IBMVFC_FABRIC_MAPPED, IBMVFC_NAME_SERVER_FAIL, DID_ERROR, 1, 1, "name server failure" },
- { IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_HALTED, DID_REQUEUE, 0, 0, "link halted" },
+ { IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_HALTED, DID_REQUEUE, 1, 0, "link halted" },
{ IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_GENERAL, DID_OK, 1, 0, "general transport error" },
{ IBMVFC_VIOS_FAILURE, IBMVFC_CRQ_FAILURE, DID_REQUEUE, 1, 1, "CRQ failure" },
@@ -143,6 +140,7 @@ static void ibmvfc_npiv_login(struct ibmvfc_host *);
static void ibmvfc_tgt_send_prli(struct ibmvfc_target *);
static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *);
static void ibmvfc_tgt_query_target(struct ibmvfc_target *);
+static void ibmvfc_npiv_logout(struct ibmvfc_host *);
static const char *unknown_error = "unknown error";
@@ -275,7 +273,7 @@ static int ibmvfc_get_err_result(struct ibmvfc_cmd *vfc_cmd)
int fc_rsp_len = rsp->fcp_rsp_len;
if ((rsp->flags & FCP_RSP_LEN_VALID) &&
- ((!fc_rsp_len && fc_rsp_len != 4 && fc_rsp_len != 8) ||
+ ((fc_rsp_len && fc_rsp_len != 4 && fc_rsp_len != 8) ||
rsp->data.info.rsp_code))
return DID_ERROR << 16;
@@ -429,8 +427,13 @@ 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)
+ tgt->add_rport = 0;
tgt->action = action;
break;
}
@@ -475,6 +478,10 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT)
vhost->action = action;
break;
+ case IBMVFC_HOST_ACTION_LOGO_WAIT:
+ if (vhost->action == IBMVFC_HOST_ACTION_LOGO)
+ vhost->action = action;
+ break;
case IBMVFC_HOST_ACTION_INIT_WAIT:
if (vhost->action == IBMVFC_HOST_ACTION_INIT)
vhost->action = action;
@@ -483,7 +490,7 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
switch (vhost->action) {
case IBMVFC_HOST_ACTION_INIT_WAIT:
case IBMVFC_HOST_ACTION_NONE:
- case IBMVFC_HOST_ACTION_TGT_ADD:
+ case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
vhost->action = action;
break;
default:
@@ -496,10 +503,21 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
break;
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_TGT_ADD:
case IBMVFC_HOST_ACTION_NONE:
+ case IBMVFC_HOST_ACTION_RESET:
+ case IBMVFC_HOST_ACTION_REENABLE:
default:
vhost->action = action;
break;
@@ -551,12 +569,11 @@ static void ibmvfc_link_down(struct ibmvfc_host *vhost,
/**
* ibmvfc_init_host - Start host initialization
* @vhost: ibmvfc host struct
- * @relogin: is this a re-login?
*
* Return value:
* nothing
**/
-static void ibmvfc_init_host(struct ibmvfc_host *vhost, int relogin)
+static void ibmvfc_init_host(struct ibmvfc_host *vhost)
{
struct ibmvfc_target *tgt;
@@ -570,13 +587,11 @@ static void ibmvfc_init_host(struct ibmvfc_host *vhost, int relogin)
}
if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
- if (!relogin) {
- memset(vhost->async_crq.msgs, 0, PAGE_SIZE);
- vhost->async_crq.cur = 0;
- }
+ memset(vhost->async_crq.msgs, 0, PAGE_SIZE);
+ vhost->async_crq.cur = 0;
list_for_each_entry(tgt, &vhost->targets, queue)
- tgt->need_login = 1;
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
scsi_block_requests(vhost->host);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
vhost->job_step = ibmvfc_npiv_login;
@@ -634,7 +649,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;
@@ -642,10 +657,13 @@ 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));
vhost->state = IBMVFC_NO_CRQ;
+ vhost->logged_in = 0;
dma_unmap_single(vhost->dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL);
free_page((unsigned long)crq->msgs);
}
@@ -659,11 +677,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));
@@ -682,16 +702,21 @@ 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);
/* Clean out the queue */
@@ -707,6 +732,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;
}
@@ -807,27 +833,35 @@ static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code)
}
/**
+ * ibmvfc_hard_reset_host - Reset the connection to the server by breaking the CRQ
+ * @vhost: struct ibmvfc host to reset
+ **/
+static void ibmvfc_hard_reset_host(struct ibmvfc_host *vhost)
+{
+ ibmvfc_purge_requests(vhost, DID_ERROR);
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_RESET);
+}
+
+/**
* __ibmvfc_reset_host - Reset the connection to the server (no locking)
* @vhost: struct ibmvfc host to reset
**/
static void __ibmvfc_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);
+ if (vhost->logged_in && vhost->action != IBMVFC_HOST_ACTION_LOGO_WAIT &&
+ !ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
+ scsi_block_requests(vhost->host);
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_LOGO);
+ vhost->job_step = ibmvfc_npiv_logout;
+ wake_up(&vhost->work_wait_q);
} else
- ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+ ibmvfc_hard_reset_host(vhost);
}
/**
* ibmvfc_reset_host - Reset the connection to the server
- * @vhost: struct ibmvfc host to reset
+ * @vhost: ibmvfc host struct
**/
static void ibmvfc_reset_host(struct ibmvfc_host *vhost)
{
@@ -842,9 +876,13 @@ static void ibmvfc_reset_host(struct ibmvfc_host *vhost)
* ibmvfc_retry_host_init - Retry host initialization if allowed
* @vhost: ibmvfc host struct
*
+ * Returns: 1 if init will be retried / 0 if not
+ *
**/
-static void ibmvfc_retry_host_init(struct ibmvfc_host *vhost)
+static int ibmvfc_retry_host_init(struct ibmvfc_host *vhost)
{
+ int retry = 0;
+
if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) {
vhost->delay_init = 1;
if (++vhost->init_retries > IBMVFC_MAX_HOST_INIT_RETRIES) {
@@ -853,11 +891,14 @@ static void ibmvfc_retry_host_init(struct ibmvfc_host *vhost)
ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
} else if (vhost->init_retries == IBMVFC_MAX_HOST_INIT_RETRIES)
__ibmvfc_reset_host(vhost);
- else
+ else {
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
+ retry = 1;
+ }
}
wake_up(&vhost->work_wait_q);
+ return retry;
}
/**
@@ -1125,7 +1166,7 @@ static void ibmvfc_gather_partition_info(struct ibmvfc_host *vhost)
static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
{
struct ibmvfc_npiv_login *login_info = &vhost->login_info;
- struct device_node *of_node = vhost->dev->archdata.of_node;
+ struct device_node *of_node = vhost->dev->of_node;
const char *location;
memset(login_info, 0, sizeof(*login_info));
@@ -1137,8 +1178,9 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
login_info->partition_num = vhost->partition_number;
login_info->vfc_frame_version = 1;
login_info->fcp_version = 3;
+ login_info->flags = IBMVFC_FLUSH_ON_HALT;
if (vhost->client_migrated)
- login_info->flags = IBMVFC_CLIENT_MIGRATED;
+ login_info->flags |= IBMVFC_CLIENT_MIGRATED;
login_info->max_cmds = max_requests + IBMVFC_NUM_INTERNAL_REQ;
login_info->capabilities = IBMVFC_CAN_MIGRATE;
@@ -1452,6 +1494,27 @@ static void ibmvfc_log_error(struct ibmvfc_event *evt)
}
/**
+ * ibmvfc_relogin - Log back into the specified device
+ * @sdev: scsi device struct
+ *
+ **/
+static void ibmvfc_relogin(struct scsi_device *sdev)
+{
+ struct ibmvfc_host *vhost = shost_priv(sdev->host);
+ struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+ struct ibmvfc_target *tgt;
+
+ list_for_each_entry(tgt, &vhost->targets, queue) {
+ if (rport == tgt->rport) {
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ break;
+ }
+ }
+
+ ibmvfc_reinit_host(vhost);
+}
+
+/**
* ibmvfc_scsi_done - Handle responses from commands
* @evt: ibmvfc event to be handled
*
@@ -1483,7 +1546,7 @@ static void ibmvfc_scsi_done(struct ibmvfc_event *evt)
if ((rsp->flags & FCP_SNS_LEN_VALID) && rsp->fcp_sense_len && rsp_len <= 8)
memcpy(cmnd->sense_buffer, rsp->data.sense + rsp_len, sense_len);
if ((vfc_cmd->status & IBMVFC_VIOS_FAILURE) && (vfc_cmd->error == IBMVFC_PLOGI_REQUIRED))
- ibmvfc_reinit_host(evt->vhost);
+ ibmvfc_relogin(cmnd->device);
if (!cmnd->result && (!scsi_get_resid(cmnd) || (rsp->flags & FCP_RESID_OVER)))
cmnd->result = (DID_ERROR << 16);
@@ -1543,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);
@@ -1609,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
@@ -1624,6 +1689,276 @@ static void ibmvfc_sync_completion(struct ibmvfc_event *evt)
}
/**
+ * ibmvfc_bsg_timeout_done - Completion handler for cancelling BSG commands
+ * @evt: struct ibmvfc_event
+ *
+ **/
+static void ibmvfc_bsg_timeout_done(struct ibmvfc_event *evt)
+{
+ struct ibmvfc_host *vhost = evt->vhost;
+
+ ibmvfc_free_event(evt);
+ vhost->aborting_passthru = 0;
+ dev_info(vhost->dev, "Passthru command cancelled\n");
+}
+
+/**
+ * ibmvfc_bsg_timeout - Handle a BSG timeout
+ * @job: struct fc_bsg_job that timed out
+ *
+ * Returns:
+ * 0 on success / other on failure
+ **/
+static int ibmvfc_bsg_timeout(struct fc_bsg_job *job)
+{
+ struct ibmvfc_host *vhost = shost_priv(job->shost);
+ unsigned long port_id = (unsigned long)job->dd_data;
+ struct ibmvfc_event *evt;
+ struct ibmvfc_tmf *tmf;
+ unsigned long flags;
+ int rc;
+
+ ENTER;
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ if (vhost->aborting_passthru || vhost->state != IBMVFC_ACTIVE) {
+ __ibmvfc_reset_host(vhost);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ return 0;
+ }
+
+ vhost->aborting_passthru = 1;
+ evt = ibmvfc_get_event(vhost);
+ ibmvfc_init_event(evt, ibmvfc_bsg_timeout_done, IBMVFC_MAD_FORMAT);
+
+ tmf = &evt->iu.tmf;
+ memset(tmf, 0, sizeof(*tmf));
+ tmf->common.version = 1;
+ tmf->common.opcode = IBMVFC_TMF_MAD;
+ tmf->common.length = sizeof(*tmf);
+ tmf->scsi_id = port_id;
+ tmf->cancel_key = IBMVFC_PASSTHRU_CANCEL_KEY;
+ tmf->my_cancel_key = IBMVFC_INTERNAL_CANCEL_KEY;
+ rc = ibmvfc_send_event(evt, vhost, default_timeout);
+
+ if (rc != 0) {
+ vhost->aborting_passthru = 0;
+ dev_err(vhost->dev, "Failed to send cancel event. rc=%d\n", rc);
+ rc = -EIO;
+ } else
+ dev_info(vhost->dev, "Cancelling passthru command to port id 0x%lx\n",
+ port_id);
+
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+ LEAVE;
+ return rc;
+}
+
+/**
+ * ibmvfc_bsg_plogi - PLOGI into a target to handle a BSG command
+ * @vhost: struct ibmvfc_host to send command
+ * @port_id: port ID to send command
+ *
+ * Returns:
+ * 0 on success / other on failure
+ **/
+static int ibmvfc_bsg_plogi(struct ibmvfc_host *vhost, unsigned int port_id)
+{
+ struct ibmvfc_port_login *plogi;
+ struct ibmvfc_target *tgt;
+ struct ibmvfc_event *evt;
+ union ibmvfc_iu rsp_iu;
+ unsigned long flags;
+ int rc = 0, issue_login = 1;
+
+ ENTER;
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ list_for_each_entry(tgt, &vhost->targets, queue) {
+ if (tgt->scsi_id == port_id) {
+ issue_login = 0;
+ break;
+ }
+ }
+
+ if (!issue_login)
+ goto unlock_out;
+ if (unlikely((rc = ibmvfc_host_chkready(vhost))))
+ goto unlock_out;
+
+ evt = ibmvfc_get_event(vhost);
+ ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT);
+ plogi = &evt->iu.plogi;
+ memset(plogi, 0, sizeof(*plogi));
+ plogi->common.version = 1;
+ plogi->common.opcode = IBMVFC_PORT_LOGIN;
+ plogi->common.length = sizeof(*plogi);
+ plogi->scsi_id = port_id;
+ evt->sync_iu = &rsp_iu;
+ init_completion(&evt->comp);
+
+ rc = ibmvfc_send_event(evt, vhost, default_timeout);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+ if (rc)
+ return -EIO;
+
+ wait_for_completion(&evt->comp);
+
+ if (rsp_iu.plogi.common.status)
+ rc = -EIO;
+
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ ibmvfc_free_event(evt);
+unlock_out:
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ LEAVE;
+ return rc;
+}
+
+/**
+ * ibmvfc_bsg_request - Handle a BSG request
+ * @job: struct fc_bsg_job to be executed
+ *
+ * Returns:
+ * 0 on success / other on failure
+ **/
+static int ibmvfc_bsg_request(struct fc_bsg_job *job)
+{
+ struct ibmvfc_host *vhost = shost_priv(job->shost);
+ struct fc_rport *rport = job->rport;
+ struct ibmvfc_passthru_mad *mad;
+ struct ibmvfc_event *evt;
+ union ibmvfc_iu rsp_iu;
+ unsigned long flags, port_id = -1;
+ unsigned int code = job->request->msgcode;
+ int rc = 0, req_seg, rsp_seg, issue_login = 0;
+ u32 fc_flags, rsp_len;
+
+ ENTER;
+ job->reply->reply_payload_rcv_len = 0;
+ if (rport)
+ port_id = rport->port_id;
+
+ switch (code) {
+ case FC_BSG_HST_ELS_NOLOGIN:
+ port_id = (job->request->rqst_data.h_els.port_id[0] << 16) |
+ (job->request->rqst_data.h_els.port_id[1] << 8) |
+ job->request->rqst_data.h_els.port_id[2];
+ case FC_BSG_RPT_ELS:
+ fc_flags = IBMVFC_FC_ELS;
+ break;
+ case FC_BSG_HST_CT:
+ issue_login = 1;
+ port_id = (job->request->rqst_data.h_ct.port_id[0] << 16) |
+ (job->request->rqst_data.h_ct.port_id[1] << 8) |
+ job->request->rqst_data.h_ct.port_id[2];
+ case FC_BSG_RPT_CT:
+ fc_flags = IBMVFC_FC_CT_IU;
+ break;
+ default:
+ return -ENOTSUPP;
+ };
+
+ if (port_id == -1)
+ return -EINVAL;
+ if (!mutex_trylock(&vhost->passthru_mutex))
+ return -EBUSY;
+
+ job->dd_data = (void *)port_id;
+ req_seg = dma_map_sg(vhost->dev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+
+ if (!req_seg) {
+ mutex_unlock(&vhost->passthru_mutex);
+ return -ENOMEM;
+ }
+
+ rsp_seg = dma_map_sg(vhost->dev, job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+
+ if (!rsp_seg) {
+ dma_unmap_sg(vhost->dev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ mutex_unlock(&vhost->passthru_mutex);
+ return -ENOMEM;
+ }
+
+ if (req_seg > 1 || rsp_seg > 1) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (issue_login)
+ rc = ibmvfc_bsg_plogi(vhost, port_id);
+
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+
+ if (unlikely(rc || (rport && (rc = fc_remote_port_chkready(rport)))) ||
+ unlikely((rc = ibmvfc_host_chkready(vhost)))) {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ goto out;
+ }
+
+ evt = ibmvfc_get_event(vhost);
+ ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT);
+ mad = &evt->iu.passthru;
+
+ memset(mad, 0, sizeof(*mad));
+ mad->common.version = 1;
+ mad->common.opcode = IBMVFC_PASSTHRU;
+ mad->common.length = sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu);
+
+ mad->cmd_ioba.va = (u64)evt->crq.ioba +
+ offsetof(struct ibmvfc_passthru_mad, iu);
+ mad->cmd_ioba.len = sizeof(mad->iu);
+
+ mad->iu.cmd_len = job->request_payload.payload_len;
+ mad->iu.rsp_len = job->reply_payload.payload_len;
+ mad->iu.flags = fc_flags;
+ mad->iu.cancel_key = IBMVFC_PASSTHRU_CANCEL_KEY;
+
+ mad->iu.cmd.va = sg_dma_address(job->request_payload.sg_list);
+ mad->iu.cmd.len = sg_dma_len(job->request_payload.sg_list);
+ mad->iu.rsp.va = sg_dma_address(job->reply_payload.sg_list);
+ mad->iu.rsp.len = sg_dma_len(job->reply_payload.sg_list);
+ mad->iu.scsi_id = port_id;
+ mad->iu.tag = (u64)evt;
+ rsp_len = mad->iu.rsp.len;
+
+ evt->sync_iu = &rsp_iu;
+ init_completion(&evt->comp);
+ rc = ibmvfc_send_event(evt, vhost, 0);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+ if (rc) {
+ rc = -EIO;
+ goto out;
+ }
+
+ wait_for_completion(&evt->comp);
+
+ if (rsp_iu.passthru.common.status)
+ rc = -EIO;
+ else
+ job->reply->reply_payload_rcv_len = rsp_len;
+
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ ibmvfc_free_event(evt);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ job->reply->result = rc;
+ job->job_done(job);
+ rc = 0;
+out:
+ dma_unmap_sg(vhost->dev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ dma_unmap_sg(vhost->dev, job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ mutex_unlock(&vhost->passthru_mutex);
+ LEAVE;
+ return rc;
+}
+
+/**
* ibmvfc_reset_device - Reset the device with the specified reset type
* @sdev: scsi device to reset
* @type: reset type
@@ -1677,7 +2012,10 @@ static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc)
sdev_printk(KERN_INFO, sdev, "Resetting %s\n", desc);
wait_for_completion(&evt->comp);
- if (rsp_iu.cmd.status) {
+ 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;
@@ -1697,92 +2035,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;
-
- 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;
- }
- }
+ struct fc_rport *cmd_rport;
- 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 (evt->cmnd) {
+ cmd_rport = starget_to_rport(scsi_target(evt->cmnd->device));
+ if (cmd_rport == rport)
+ return 1;
}
+ 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);
- }
+/**
+ * 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;
+}
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
+/**
+ * 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;
+}
- if (rsp_rc != 0) {
- sdev_printk(KERN_ERR, sdev, "Failed to send abort. rc=%d\n", rsp_rc);
- return -EIO;
- }
+/**
+ * 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;
- sdev_printk(KERN_INFO, sdev, "Aborting outstanding commands\n");
- wait_for_completion(&evt->comp);
+ 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 = &comp;
+ wait++;
+ }
+ }
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
- if (rsp_iu.cmd.status) {
- 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;
}
/**
@@ -1825,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);
@@ -1836,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;
@@ -1849,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");
@@ -1862,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");
@@ -1870,88 +2240,158 @@ 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)
+ unsigned long cancel_key = (unsigned long)key;
+
+ if (evt->crq.format == IBMVFC_CMD_FORMAT &&
+ evt->iu.cmd.cancel_key == cancel_key)
return 1;
return 0;
}
/**
- * ibmvfc_match_lun - Match function for specified LUN
+ * ibmvfc_match_evt - Match function for specified event
* @evt: ibmvfc event struct
- * @device: device to match (sdev)
+ * @match: event to match
*
* Returns:
- * 1 if event matches sdev / 0 if event does not match sdev
+ * 1 if event matches key / 0 if event does not match key
**/
-static int ibmvfc_match_lun(struct ibmvfc_event *evt, void *device)
+static int ibmvfc_match_evt(struct ibmvfc_event *evt, void *match)
{
- if (evt->cmnd && evt->cmnd->device == device)
+ if (evt == match)
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 = init_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 = &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;
+ 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, 0);
+ if (!rc) {
+ rc = ibmvfc_wait_for_ops(vhost, sdev->hostdata, ibmvfc_match_key);
+ if (rc == SUCCESS)
+ rc = 0;
+ }
+
+ if (rc) {
+ sdev_printk(KERN_INFO, sdev, "Cancel failed, resetting host\n");
+ ibmvfc_reset_host(vhost);
+ 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);
- list_for_each_entry(evt, &vhost->sent, queue) {
- if (match(evt, device)) {
- evt->eh_comp = NULL;
- wait++;
- }
- }
+ ibmvfc_hard_reset_host(vhost);
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;
+ rsp_rc = 0;
}
+
+ goto out;
}
- } while (wait);
+ }
- LEAVE;
- return SUCCESS;
+ 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;
}
/**
@@ -1959,23 +2399,30 @@ static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device,
* @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;
+ 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;
}
@@ -1985,49 +2432,56 @@ 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;
+ 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 - Device iterated cancel all function
+ * ibmvfc_dev_cancel_all_noreset - Device iterated cancel all function
* @sdev: scsi device struct
* @data: return code
*
**/
-static void ibmvfc_dev_cancel_all(struct scsi_device *sdev, void *data)
+static void ibmvfc_dev_cancel_all_noreset(struct scsi_device *sdev, void *data)
{
unsigned long *rc = data;
- *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_TGT_RESET);
+ *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS);
}
/**
- * ibmvfc_dev_abort_all - Device iterated abort task set function
+ * ibmvfc_dev_cancel_all_reset - Device iterated cancel all function
* @sdev: scsi device struct
* @data: return code
*
**/
-static void ibmvfc_dev_abort_all(struct scsi_device *sdev, void *data)
+static void ibmvfc_dev_cancel_all_reset(struct scsi_device *sdev, void *data)
{
unsigned long *rc = data;
- *rc |= ibmvfc_abort_task_set(sdev);
+ *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_TGT_RESET);
}
/**
@@ -2035,25 +2489,33 @@ static void ibmvfc_dev_abort_all(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;
+ block_rc = fc_block_scsi_eh(cmd);
ibmvfc_wait_while_resetting(vhost);
- starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all);
- 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;
}
@@ -2065,11 +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);
+ 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;
}
@@ -2082,60 +2549,87 @@ 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);
- 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_SUPPRESS_ABTS);
+ }
- 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);
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;
+}
+
+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" },
+};
- return unknown_ae;
+/**
+ * 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 "";
}
/**
@@ -2147,14 +2641,33 @@ 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_LINK_UP:
case IBMVFC_AE_RESUME:
+ switch (crq->link_state) {
+ case IBMVFC_AE_LS_LINK_DOWN:
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+ break;
+ case IBMVFC_AE_LS_LINK_DEAD:
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ break;
+ case IBMVFC_AE_LS_LINK_UP:
+ case IBMVFC_AE_LS_LINK_BOUNCED:
+ default:
+ vhost->events_to_log |= IBMVFC_AE_LINKUP;
+ vhost->delay_init = 1;
+ __ibmvfc_reset_host(vhost);
+ break;
+ };
+
+ break;
+ case IBMVFC_AE_LINK_UP:
vhost->events_to_log |= IBMVFC_AE_LINKUP;
vhost->delay_init = 1;
__ibmvfc_reset_host(vhost);
@@ -2162,16 +2675,35 @@ 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:
vhost->events_to_log |= IBMVFC_AE_RSCN;
+ ibmvfc_reinit_host(vhost);
+ break;
case IBMVFC_AE_ELS_LOGO:
case IBMVFC_AE_ELS_PRLO:
case IBMVFC_AE_ELS_PLOGI:
- ibmvfc_reinit_host(vhost);
+ list_for_each_entry(tgt, &vhost->targets, queue) {
+ if (!crq->scsi_id && !crq->wwpn && !crq->node_name)
+ break;
+ if (crq->scsi_id && tgt->scsi_id != crq->scsi_id)
+ continue;
+ if (crq->wwpn && tgt->ids.port_name != crq->wwpn)
+ continue;
+ if (crq->node_name && tgt->ids.node_name != crq->node_name)
+ continue;
+ if (tgt->need_login && crq->event == IBMVFC_AE_ELS_LOGO)
+ tgt->logo_rcvd = 1;
+ if (!tgt->need_login || crq->event == IBMVFC_AE_ELS_PLOGI) {
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ ibmvfc_reinit_host(vhost);
+ }
+ }
break;
case IBMVFC_AE_LINK_DOWN:
case IBMVFC_AE_ADAPTER_FAILED:
@@ -2208,13 +2740,13 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost)
/* Send back a response */
rc = ibmvfc_send_crq_init_complete(vhost);
if (rc == 0)
- ibmvfc_init_host(vhost, 0);
+ ibmvfc_init_host(vhost);
else
dev_err(vhost->dev, "Unable to send init rsp. rc=%ld\n", rc);
break;
case IBMVFC_CRQ_INIT_COMPLETE:
dev_info(vhost->dev, "Partner initialization complete\n");
- ibmvfc_init_host(vhost, 0);
+ ibmvfc_init_host(vhost);
break;
default:
dev_err(vhost->dev, "Unknown crq message type: %d\n", crq->format);
@@ -2222,28 +2754,20 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost)
return;
case IBMVFC_CRQ_XPORT_EVENT:
vhost->state = IBMVFC_NO_CRQ;
+ vhost->logged_in = 0;
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
if (crq->format == IBMVFC_PARTITION_MIGRATED) {
/* We need to re-setup the interpartition connection */
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:
@@ -2299,7 +2823,7 @@ static int ibmvfc_scan_finished(struct Scsi_Host *shost, unsigned long time)
done = 1;
}
- if (vhost->state != IBMVFC_NO_CRQ && vhost->action == IBMVFC_HOST_ACTION_NONE)
+ if (vhost->scan_complete)
done = 1;
spin_unlock_irqrestore(shost->host_lock, flags);
return done;
@@ -2366,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);
@@ -2378,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;
}
@@ -2388,12 +2909,17 @@ static int ibmvfc_slave_configure(struct scsi_device *sdev)
* ibmvfc_change_queue_depth - Change the device's queue depth
* @sdev: scsi device struct
* @qdepth: depth to set
+ * @reason: calling context
*
* Return value:
* actual depth set
**/
-static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth)
+static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth,
+ int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (qdepth > IBMVFC_MAX_CMDS_PER_LUN)
qdepth = IBMVFC_MAX_CMDS_PER_LUN;
@@ -2434,14 +2960,6 @@ static ssize_t ibmvfc_show_host_partition_name(struct device *dev,
vhost->login_buf->resp.partition_name);
}
-static struct device_attribute ibmvfc_host_partition_name = {
- .attr = {
- .name = "partition_name",
- .mode = S_IRUGO,
- },
- .show = ibmvfc_show_host_partition_name,
-};
-
static ssize_t ibmvfc_show_host_device_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -2452,14 +2970,6 @@ static ssize_t ibmvfc_show_host_device_name(struct device *dev,
vhost->login_buf->resp.device_name);
}
-static struct device_attribute ibmvfc_host_device_name = {
- .attr = {
- .name = "device_name",
- .mode = S_IRUGO,
- },
- .show = ibmvfc_show_host_device_name,
-};
-
static ssize_t ibmvfc_show_host_loc_code(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -2470,14 +2980,6 @@ static ssize_t ibmvfc_show_host_loc_code(struct device *dev,
vhost->login_buf->resp.port_loc_code);
}
-static struct device_attribute ibmvfc_host_loc_code = {
- .attr = {
- .name = "port_loc_code",
- .mode = S_IRUGO,
- },
- .show = ibmvfc_show_host_loc_code,
-};
-
static ssize_t ibmvfc_show_host_drc_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -2488,14 +2990,6 @@ static ssize_t ibmvfc_show_host_drc_name(struct device *dev,
vhost->login_buf->resp.drc_name);
}
-static struct device_attribute ibmvfc_host_drc_name = {
- .attr = {
- .name = "drc_name",
- .mode = S_IRUGO,
- },
- .show = ibmvfc_show_host_drc_name,
-};
-
static ssize_t ibmvfc_show_host_npiv_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -2504,13 +2998,13 @@ static ssize_t ibmvfc_show_host_npiv_version(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%d\n", vhost->login_buf->resp.version);
}
-static struct device_attribute ibmvfc_host_npiv_version = {
- .attr = {
- .name = "npiv_version",
- .mode = S_IRUGO,
- },
- .show = ibmvfc_show_host_npiv_version,
-};
+static ssize_t ibmvfc_show_host_capabilities(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct ibmvfc_host *vhost = shost_priv(shost);
+ return snprintf(buf, PAGE_SIZE, "%llx\n", vhost->login_buf->resp.capabilities);
+}
/**
* ibmvfc_show_log_level - Show the adapter's error logging level
@@ -2556,18 +3050,19 @@ static ssize_t ibmvfc_store_log_level(struct device *dev,
return strlen(buf);
}
-static struct device_attribute ibmvfc_log_level_attr = {
- .attr = {
- .name = "log_level",
- .mode = S_IRUGO | S_IWUSR,
- },
- .show = ibmvfc_show_log_level,
- .store = ibmvfc_store_log_level
-};
+static DEVICE_ATTR(partition_name, S_IRUGO, ibmvfc_show_host_partition_name, NULL);
+static DEVICE_ATTR(device_name, S_IRUGO, ibmvfc_show_host_device_name, NULL);
+static DEVICE_ATTR(port_loc_code, S_IRUGO, ibmvfc_show_host_loc_code, NULL);
+static DEVICE_ATTR(drc_name, S_IRUGO, ibmvfc_show_host_drc_name, NULL);
+static DEVICE_ATTR(npiv_version, S_IRUGO, ibmvfc_show_host_npiv_version, NULL);
+static DEVICE_ATTR(capabilities, S_IRUGO, ibmvfc_show_host_capabilities, NULL);
+static DEVICE_ATTR(log_level, S_IRUGO | S_IWUSR,
+ ibmvfc_show_log_level, ibmvfc_store_log_level);
#ifdef CONFIG_SCSI_IBMVFC_TRACE
/**
* ibmvfc_read_trace - Dump the adapter trace
+ * @filp: open sysfs file
* @kobj: kobject struct
* @bin_attr: bin_attribute struct
* @buf: buffer
@@ -2577,7 +3072,7 @@ static struct device_attribute ibmvfc_log_level_attr = {
* Return value:
* number of bytes printed to buffer
**/
-static ssize_t ibmvfc_read_trace(struct kobject *kobj,
+static ssize_t ibmvfc_read_trace(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
@@ -2612,12 +3107,13 @@ static struct bin_attribute ibmvfc_trace_attr = {
#endif
static struct device_attribute *ibmvfc_attrs[] = {
- &ibmvfc_host_partition_name,
- &ibmvfc_host_device_name,
- &ibmvfc_host_loc_code,
- &ibmvfc_host_drc_name,
- &ibmvfc_host_npiv_version,
- &ibmvfc_log_level_attr,
+ &dev_attr_partition_name,
+ &dev_attr_device_name,
+ &dev_attr_port_loc_code,
+ &dev_attr_drc_name,
+ &dev_attr_npiv_version,
+ &dev_attr_capabilities,
+ &dev_attr_log_level,
NULL
};
@@ -2661,6 +3157,7 @@ static struct ibmvfc_async_crq *ibmvfc_next_async_crq(struct ibmvfc_host *vhost)
if (crq->valid & 0x80) {
if (++async_crq->cur == async_crq->size)
async_crq->cur = 0;
+ rmb();
} else
crq = NULL;
@@ -2683,6 +3180,7 @@ static struct ibmvfc_crq *ibmvfc_next_crq(struct ibmvfc_host *vhost)
if (crq->valid & 0x80) {
if (++queue->cur == queue->size)
queue->cur = 0;
+ rmb();
} else
crq = NULL;
@@ -2727,27 +3225,31 @@ static void ibmvfc_tasklet(void *data)
spin_lock_irqsave(vhost->host->host_lock, flags);
while (!done) {
- /* Pull all the valid messages off the CRQ */
- while ((crq = ibmvfc_next_crq(vhost)) != NULL) {
- ibmvfc_handle_crq(crq, vhost);
- crq->valid = 0;
- }
-
/* Pull all the valid messages off the async CRQ */
while ((async = ibmvfc_next_async_crq(vhost)) != NULL) {
ibmvfc_handle_async(async, vhost);
async->valid = 0;
+ wmb();
}
- vio_enable_interrupts(vdev);
- if ((crq = ibmvfc_next_crq(vhost)) != NULL) {
- vio_disable_interrupts(vdev);
+ /* Pull all the valid messages off the CRQ */
+ while ((crq = ibmvfc_next_crq(vhost)) != NULL) {
ibmvfc_handle_crq(crq, vhost);
crq->valid = 0;
- } else if ((async = ibmvfc_next_async_crq(vhost)) != NULL) {
+ wmb();
+ }
+
+ vio_enable_interrupts(vdev);
+ if ((async = ibmvfc_next_async_crq(vhost)) != NULL) {
vio_disable_interrupts(vdev);
ibmvfc_handle_async(async, vhost);
async->valid = 0;
+ wmb();
+ } else if ((crq = ibmvfc_next_crq(vhost)) != NULL) {
+ vio_disable_interrupts(vdev);
+ ibmvfc_handle_crq(crq, vhost);
+ crq->valid = 0;
+ wmb();
} else
done = 1;
}
@@ -2774,15 +3276,19 @@ static void ibmvfc_init_tgt(struct ibmvfc_target *tgt,
* @tgt: ibmvfc target struct
* @job_step: initialization job step
*
+ * Returns: 1 if step will be retried / 0 if not
+ *
**/
-static void ibmvfc_retry_tgt_init(struct ibmvfc_target *tgt,
+static int ibmvfc_retry_tgt_init(struct ibmvfc_target *tgt,
void (*job_step) (struct ibmvfc_target *))
{
if (++tgt->init_retries > IBMVFC_MAX_TGT_INIT_RETRIES) {
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
wake_up(&tgt->vhost->work_wait_q);
+ return 0;
} else
ibmvfc_init_tgt(tgt, job_step);
+ return 1;
}
/* Defined in FC-LS */
@@ -2831,7 +3337,7 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
struct ibmvfc_process_login *rsp = &evt->xfer_iu->prli;
struct ibmvfc_prli_svc_parms *parms = &rsp->parms;
u32 status = rsp->common.status;
- int index;
+ int index, level = IBMVFC_DEFAULT_LOG_LEVEL;
vhost->discovery_threads--;
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
@@ -2850,7 +3356,7 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
tgt->ids.roles |= FC_PORT_ROLE_FCP_TARGET;
if (parms->service_parms & IBMVFC_PRLI_INITIATOR_FUNC)
tgt->ids.roles |= FC_PORT_ROLE_FCP_INITIATOR;
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_ADD_RPORT);
+ tgt->add_rport = 1;
} else
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
} else if (prli_rsp[index].retry)
@@ -2867,13 +3373,18 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
break;
case IBMVFC_MAD_FAILED:
default:
- tgt_err(tgt, "Process Login failed: %s (%x:%x) rc=0x%02X\n",
- ibmvfc_get_cmd_error(rsp->status, rsp->error),
- rsp->status, rsp->error, status);
- if (ibmvfc_retry_cmd(rsp->status, rsp->error))
- ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
+ if ((rsp->status & IBMVFC_VIOS_FAILURE) && rsp->error == IBMVFC_PLOGI_REQUIRED)
+ level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
+ else if (tgt->logo_rcvd)
+ level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
+ else if (ibmvfc_retry_cmd(rsp->status, rsp->error))
+ level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
else
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+
+ tgt_log(tgt, level, "Process Login failed: %s (%x:%x) rc=0x%02X\n",
+ ibmvfc_get_cmd_error(rsp->status, rsp->error),
+ rsp->status, rsp->error, status);
break;
};
@@ -2932,6 +3443,7 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
struct ibmvfc_host *vhost = evt->vhost;
struct ibmvfc_port_login *rsp = &evt->xfer_iu->plogi;
u32 status = rsp->common.status;
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
vhost->discovery_threads--;
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
@@ -2960,15 +3472,15 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
break;
case IBMVFC_MAD_FAILED:
default:
- tgt_err(tgt, "Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
- ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error,
- ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type,
- ibmvfc_get_ls_explain(rsp->fc_explain), rsp->fc_explain, status);
-
if (ibmvfc_retry_cmd(rsp->status, rsp->error))
- ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
+ level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
else
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+
+ tgt_log(tgt, level, "Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
+ ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error,
+ ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type,
+ ibmvfc_get_ls_explain(rsp->fc_explain), rsp->fc_explain, status);
break;
};
@@ -2992,6 +3504,7 @@ static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *tgt)
return;
kref_get(&tgt->kref);
+ tgt->logo_rcvd = 0;
evt = ibmvfc_get_event(vhost);
vhost->discovery_threads++;
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
@@ -3129,13 +3642,13 @@ static void ibmvfc_tgt_adisc_done(struct ibmvfc_event *evt)
case IBMVFC_MAD_SUCCESS:
tgt_dbg(tgt, "ADISC succeeded\n");
if (ibmvfc_adisc_needs_plogi(mad, tgt))
- tgt->need_login = 1;
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
break;
case IBMVFC_MAD_DRIVER_FAILED:
break;
case IBMVFC_MAD_FAILED:
default:
- tgt->need_login = 1;
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
fc_reason = (mad->fc_iu.response[1] & 0x00ff0000) >> 16;
fc_explain = (mad->fc_iu.response[1] & 0x0000ff00) >> 8;
tgt_info(tgt, "ADISC failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
@@ -3322,6 +3835,7 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
struct ibmvfc_host *vhost = evt->vhost;
struct ibmvfc_query_tgt *rsp = &evt->xfer_iu->query_tgt;
u32 status = rsp->common.status;
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
vhost->discovery_threads--;
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
@@ -3341,19 +3855,19 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
break;
case IBMVFC_MAD_FAILED:
default:
- tgt_err(tgt, "Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
- ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error,
- ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type,
- ibmvfc_get_gs_explain(rsp->fc_explain), rsp->fc_explain, status);
-
if ((rsp->status & IBMVFC_FABRIC_MAPPED) == IBMVFC_FABRIC_MAPPED &&
rsp->error == IBMVFC_UNABLE_TO_PERFORM_REQ &&
rsp->fc_explain == IBMVFC_PORT_NAME_NOT_REG)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
else if (ibmvfc_retry_cmd(rsp->status, rsp->error))
- ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target);
+ level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target);
else
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+
+ tgt_log(tgt, level, "Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
+ ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error,
+ ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type,
+ ibmvfc_get_gs_explain(rsp->fc_explain), rsp->fc_explain, status);
break;
};
@@ -3420,7 +3934,7 @@ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost, u64 scsi_id)
}
spin_unlock_irqrestore(vhost->host->host_lock, flags);
- tgt = mempool_alloc(vhost->tgt_pool, GFP_KERNEL);
+ tgt = mempool_alloc(vhost->tgt_pool, GFP_NOIO);
if (!tgt) {
dev_err(vhost->dev, "Target allocation failure for scsi id %08llx\n",
scsi_id);
@@ -3472,6 +3986,7 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
struct ibmvfc_host *vhost = evt->vhost;
struct ibmvfc_discover_targets *rsp = &evt->xfer_iu->discover_targets;
u32 mad_status = rsp->common.status;
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
switch (mad_status) {
case IBMVFC_MAD_SUCCESS:
@@ -3480,9 +3995,9 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_ALLOC_TGTS);
break;
case IBMVFC_MAD_FAILED:
- dev_err(vhost->dev, "Discover Targets failed: %s (%x:%x)\n",
- ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error);
- ibmvfc_retry_host_init(vhost);
+ level += ibmvfc_retry_host_init(vhost);
+ ibmvfc_log(vhost, level, "Discover Targets failed: %s (%x:%x)\n",
+ ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error);
break;
case IBMVFC_MAD_DRIVER_FAILED:
break;
@@ -3534,18 +4049,19 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
u32 mad_status = evt->xfer_iu->npiv_login.common.status;
struct ibmvfc_npiv_login_resp *rsp = &vhost->login_buf->resp;
unsigned int npiv_max_sectors;
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
switch (mad_status) {
case IBMVFC_MAD_SUCCESS:
ibmvfc_free_event(evt);
break;
case IBMVFC_MAD_FAILED:
- dev_err(vhost->dev, "NPIV Login failed: %s (%x:%x)\n",
- ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error);
if (ibmvfc_retry_cmd(rsp->status, rsp->error))
- ibmvfc_retry_host_init(vhost);
+ level += ibmvfc_retry_host_init(vhost);
else
ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ ibmvfc_log(vhost, level, "NPIV Login failed: %s (%x:%x)\n",
+ ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error);
ibmvfc_free_event(evt);
return;
case IBMVFC_MAD_CRQ_ERROR:
@@ -3578,6 +4094,7 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
return;
}
+ vhost->logged_in = 1;
npiv_max_sectors = min((uint)(rsp->max_dma_len >> 9), IBMVFC_MAX_SECTORS);
dev_info(vhost->dev, "Host partition: %s, device: %s %s %s max sectors %u\n",
rsp->partition_name, rsp->device_name, rsp->port_loc_code,
@@ -3636,6 +4153,65 @@ static void ibmvfc_npiv_login(struct ibmvfc_host *vhost)
};
/**
+ * ibmvfc_npiv_logout_done - Completion handler for NPIV Logout
+ * @vhost: ibmvfc host struct
+ *
+ **/
+static void ibmvfc_npiv_logout_done(struct ibmvfc_event *evt)
+{
+ struct ibmvfc_host *vhost = evt->vhost;
+ u32 mad_status = evt->xfer_iu->npiv_logout.common.status;
+
+ ibmvfc_free_event(evt);
+
+ switch (mad_status) {
+ case IBMVFC_MAD_SUCCESS:
+ if (list_empty(&vhost->sent) &&
+ vhost->action == IBMVFC_HOST_ACTION_LOGO_WAIT) {
+ ibmvfc_init_host(vhost);
+ return;
+ }
+ break;
+ case IBMVFC_MAD_FAILED:
+ case IBMVFC_MAD_NOT_SUPPORTED:
+ case IBMVFC_MAD_CRQ_ERROR:
+ case IBMVFC_MAD_DRIVER_FAILED:
+ default:
+ ibmvfc_dbg(vhost, "NPIV Logout failed. 0x%X\n", mad_status);
+ break;
+ }
+
+ ibmvfc_hard_reset_host(vhost);
+}
+
+/**
+ * ibmvfc_npiv_logout - Issue an NPIV Logout
+ * @vhost: ibmvfc host struct
+ *
+ **/
+static void ibmvfc_npiv_logout(struct ibmvfc_host *vhost)
+{
+ struct ibmvfc_npiv_logout_mad *mad;
+ struct ibmvfc_event *evt;
+
+ evt = ibmvfc_get_event(vhost);
+ ibmvfc_init_event(evt, ibmvfc_npiv_logout_done, IBMVFC_MAD_FORMAT);
+
+ mad = &evt->iu.npiv_logout;
+ memset(mad, 0, sizeof(*mad));
+ mad->common.version = 1;
+ mad->common.opcode = IBMVFC_NPIV_LOGOUT;
+ mad->common.length = sizeof(struct ibmvfc_npiv_logout_mad);
+
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_LOGO_WAIT);
+
+ if (!ibmvfc_send_event(evt, vhost, default_timeout))
+ ibmvfc_dbg(vhost, "Sent NPIV logout\n");
+ else
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+}
+
+/**
* ibmvfc_dev_init_to_do - Is there target initialization work to do?
* @vhost: ibmvfc host struct
*
@@ -3671,6 +4247,7 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
switch (vhost->action) {
case IBMVFC_HOST_ACTION_NONE:
case IBMVFC_HOST_ACTION_INIT_WAIT:
+ case IBMVFC_HOST_ACTION_LOGO_WAIT:
return 0;
case IBMVFC_HOST_ACTION_TGT_INIT:
case IBMVFC_HOST_ACTION_QUERY_TGTS:
@@ -3683,12 +4260,14 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
return 0;
return 1;
+ case IBMVFC_HOST_ACTION_LOGO:
case IBMVFC_HOST_ACTION_INIT:
case IBMVFC_HOST_ACTION_ALLOC_TGTS:
- case IBMVFC_HOST_ACTION_TGT_ADD:
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;
};
@@ -3740,25 +4319,30 @@ static void ibmvfc_log_ae(struct ibmvfc_host *vhost, int events)
static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt)
{
struct ibmvfc_host *vhost = tgt->vhost;
- struct fc_rport *rport = tgt->rport;
+ struct fc_rport *rport;
unsigned long flags;
- if (rport) {
- tgt_dbg(tgt, "Setting rport roles\n");
- fc_remote_port_rolechg(rport, tgt->ids.roles);
- spin_lock_irqsave(vhost->host->host_lock, flags);
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+ tgt_dbg(tgt, "Adding rport\n");
+ rport = fc_remote_port_add(vhost->host, 0, &tgt->ids);
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+
+ 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;
}
- tgt_dbg(tgt, "Adding rport\n");
- rport = fc_remote_port_add(vhost->host, 0, &tgt->ids);
- spin_lock_irqsave(vhost->host->host_lock, flags);
- tgt->rport = rport;
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
if (rport) {
tgt_dbg(tgt, "rport add succeeded\n");
+ tgt->rport = rport;
rport->maxframe_size = tgt->service_parms.common.bb_rcv_sz & 0x0fff;
rport->supported_classes = 0;
tgt->target_id = rport->scsi_target_id;
@@ -3768,6 +4352,8 @@ static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt)
rport->supported_classes |= FC_COS_CLASS2;
if (tgt->service_parms.class3_parms[0] & 0x80000000)
rport->supported_classes |= FC_COS_CLASS3;
+ if (rport->rqst_q)
+ blk_queue_max_segments(rport->rqst_q, 1);
} else
tgt_dbg(tgt, "rport add failed\n");
spin_unlock_irqrestore(vhost->host->host_lock, flags);
@@ -3783,14 +4369,42 @@ 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);
vhost->events_to_log = 0;
switch (vhost->action) {
case IBMVFC_HOST_ACTION_NONE:
+ 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 == 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);
+ 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;
case IBMVFC_HOST_ACTION_INIT:
BUG_ON(vhost->state != IBMVFC_INITIALIZING);
if (vhost->delay_init) {
@@ -3825,6 +4439,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);
@@ -3836,11 +4451,21 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
if (vhost->state == IBMVFC_INITIALIZING) {
if (vhost->action == IBMVFC_HOST_ACTION_TGT_DEL_FAILED) {
- ibmvfc_set_host_state(vhost, IBMVFC_ACTIVE);
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_ADD);
- vhost->init_retries = 0;
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- scsi_unblock_requests(vhost->host);
+ if (vhost->reinit) {
+ vhost->reinit = 0;
+ scsi_block_requests(vhost->host);
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ } else {
+ ibmvfc_set_host_state(vhost, IBMVFC_ACTIVE);
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
+ wake_up(&vhost->init_wait_q);
+ schedule_work(&vhost->rport_add_work_q);
+ vhost->init_retries = 0;
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ scsi_unblock_requests(vhost->host);
+ }
+
return;
} else {
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
@@ -3871,24 +4496,6 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
if (!ibmvfc_dev_init_to_do(vhost))
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL_FAILED);
break;
- case IBMVFC_HOST_ACTION_TGT_ADD:
- list_for_each_entry(tgt, &vhost->targets, queue) {
- if (tgt->action == IBMVFC_TGT_ACTION_ADD_RPORT) {
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- ibmvfc_tgt_add_rport(tgt);
- return;
- }
- }
-
- if (vhost->reinit && !ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
- vhost->reinit = 0;
- scsi_block_requests(vhost->host);
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
- } else {
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
- wake_up(&vhost->init_wait_q);
- }
- break;
default:
break;
};
@@ -3908,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,
@@ -4086,7 +4693,7 @@ static int ibmvfc_alloc_mem(struct ibmvfc_host *vhost)
if (!vhost->trace)
goto free_disc_buffer;
- vhost->tgt_pool = mempool_create_kzalloc_pool(IBMVFC_TGT_MEMPOOL_SZ,
+ vhost->tgt_pool = mempool_create_kmalloc_pool(IBMVFC_TGT_MEMPOOL_SZ,
sizeof(struct ibmvfc_target));
if (!vhost->tgt_pool) {
@@ -4118,6 +4725,56 @@ nomem:
}
/**
+ * ibmvfc_rport_add_thread - Worker thread for rport adds
+ * @work: work struct
+ *
+ **/
+static void ibmvfc_rport_add_thread(struct work_struct *work)
+{
+ struct ibmvfc_host *vhost = container_of(work, struct ibmvfc_host,
+ rport_add_work_q);
+ struct ibmvfc_target *tgt;
+ struct fc_rport *rport;
+ unsigned long flags;
+ int did_work;
+
+ ENTER;
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ do {
+ did_work = 0;
+ if (vhost->state != IBMVFC_ACTIVE)
+ break;
+
+ list_for_each_entry(tgt, &vhost->targets, queue) {
+ if (tgt->add_rport) {
+ did_work = 1;
+ tgt->add_rport = 0;
+ kref_get(&tgt->kref);
+ rport = tgt->rport;
+ if (!rport) {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ ibmvfc_tgt_add_rport(tgt);
+ } else if (get_device(&rport->dev)) {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ tgt_dbg(tgt, "Setting rport roles\n");
+ fc_remote_port_rolechg(rport, tgt->ids.roles);
+ put_device(&rport->dev);
+ }
+
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ break;
+ }
+ }
+ } while(did_work);
+
+ if (vhost->state == IBMVFC_ACTIVE)
+ vhost->scan_complete = 1;
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ LEAVE;
+}
+
+/**
* ibmvfc_probe - Adapter hot plug add entry point
* @vdev: vio device struct
* @id: vio device id struct
@@ -4160,6 +4817,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
strcpy(vhost->partition_name, "UNKNOWN");
init_waitqueue_head(&vhost->work_wait_q);
init_waitqueue_head(&vhost->init_wait_q);
+ INIT_WORK(&vhost->rport_add_work_q, ibmvfc_rport_add_thread);
+ mutex_init(&vhost->passthru_mutex);
if ((rc = ibmvfc_alloc_mem(vhost)))
goto free_scsi_host;
@@ -4186,12 +4845,16 @@ 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);
goto remove_shost;
}
+ if (shost_to_fc_host(shost)->rqst_q)
+ blk_queue_max_segments(shost_to_fc_host(shost)->rqst_q, 1);
dev_set_drvdata(dev, vhost);
spin_lock(&ibmvfc_driver_lock);
list_add_tail(&vhost->queue, &ibmvfc_head);
@@ -4232,7 +4895,11 @@ static int ibmvfc_remove(struct vio_dev *vdev)
ENTER;
ibmvfc_remove_trace_file(&vhost->host->shost_dev.kobj, &ibmvfc_trace_attr);
+
+ spin_lock_irqsave(vhost->host->host_lock, flags);
ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
ibmvfc_wait_while_resetting(vhost);
ibmvfc_release_crq_queue(vhost);
kthread_stop(vhost->work_thread);
@@ -4254,6 +4921,27 @@ static int ibmvfc_remove(struct vio_dev *vdev)
}
/**
+ * ibmvfc_resume - Resume from suspend
+ * @dev: device struct
+ *
+ * We may have lost an interrupt across suspend/resume, so kick the
+ * interrupt handler
+ *
+ */
+static int ibmvfc_resume(struct device *dev)
+{
+ unsigned long flags;
+ struct ibmvfc_host *vhost = dev_get_drvdata(dev);
+ struct vio_dev *vdev = to_vio_dev(dev);
+
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ vio_disable_interrupts(vdev);
+ tasklet_schedule(&vhost->tasklet);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ return 0;
+}
+
+/**
* ibmvfc_get_desired_dma - Calculate DMA resources needed by the driver
* @vdev: vio device struct
*
@@ -4267,21 +4955,23 @@ 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"},
{ "", "" }
};
MODULE_DEVICE_TABLE(vio, ibmvfc_device_table);
+static struct dev_pm_ops ibmvfc_pm_ops = {
+ .resume = ibmvfc_resume
+};
+
static struct vio_driver ibmvfc_driver = {
.id_table = ibmvfc_device_table,
.probe = ibmvfc_probe,
.remove = ibmvfc_remove,
.get_desired_dma = ibmvfc_get_desired_dma,
- .driver = {
- .name = IBMVFC_NAME,
- .owner = THIS_MODULE,
- }
+ .name = IBMVFC_NAME,
+ .pm = &ibmvfc_pm_ops,
};
static struct fc_function_template ibmvfc_transport_functions = {
@@ -4316,6 +5006,9 @@ static struct fc_function_template ibmvfc_transport_functions = {
.get_starget_port_id = ibmvfc_get_starget_port_id,
.show_starget_port_id = 1,
+
+ .bsg_request = ibmvfc_bsg_request,
+ .bsg_timeout = ibmvfc_bsg_timeout,
};
/**