[SCSI] ibmvfc: Add FC Passthru support

Adds support for FC passthru via BSG.

Signed-off-by: Brian King <brking@linux.vnet.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
This commit is contained in:
Brian King 2009-10-19 15:07:54 -05:00 committed by James Bottomley
parent 4a5c4a5ed2
commit d31429e151
2 changed files with 286 additions and 1 deletions

View File

@ -39,6 +39,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;
@ -1674,6 +1675,276 @@ static void ibmvfc_sync_completion(struct ibmvfc_event *evt)
complete(&evt->comp);
}
/**
* 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
@ -3918,6 +4189,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_hw_segments(rport->rqst_q, 1);
} else
tgt_dbg(tgt, "rport add failed\n");
spin_unlock_irqrestore(vhost->host->host_lock, flags);
@ -4357,6 +4630,7 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
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;
@ -4389,6 +4663,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
goto remove_shost;
}
if (shost_to_fc_host(shost)->rqst_q)
blk_queue_max_hw_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);
@ -4517,6 +4793,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,
};
/**

View File

@ -58,9 +58,10 @@
* 1 for ERP
* 1 for initialization
* 1 for NPIV Logout
* 2 for BSG passthru
* 2 for each discovery thread
*/
#define IBMVFC_NUM_INTERNAL_REQ (1 + 1 + 1 + (disc_threads * 2))
#define IBMVFC_NUM_INTERNAL_REQ (1 + 1 + 1 + 2 + (disc_threads * 2))
#define IBMVFC_MAD_SUCCESS 0x00
#define IBMVFC_MAD_NOT_SUPPORTED 0xF1
@ -466,7 +467,10 @@ struct ibmvfc_passthru_iu {
u16 error;
u32 flags;
#define IBMVFC_FC_ELS 0x01
#define IBMVFC_FC_CT_IU 0x02
u32 cancel_key;
#define IBMVFC_PASSTHRU_CANCEL_KEY 0x80000000
#define IBMVFC_INTERNAL_CANCEL_KEY 0x80000001
u32 reserved;
struct srp_direct_buf cmd;
struct srp_direct_buf rsp;
@ -693,6 +697,7 @@ struct ibmvfc_host {
int disc_buf_sz;
int log_level;
struct ibmvfc_discover_targets_buf *disc_buf;
struct mutex passthru_mutex;
int task_set;
int init_retries;
int discovery_threads;
@ -702,6 +707,7 @@ struct ibmvfc_host {
int delay_init;
int scan_complete;
int logged_in;
int aborting_passthru;
int events_to_log;
#define IBMVFC_AE_LINKUP 0x0001
#define IBMVFC_AE_LINKDOWN 0x0002