diff options
author | Saurav Kashyap <saurav.kashyap@qlogic.com> | 2012-08-22 22:21:01 +0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2012-09-24 12:10:47 +0400 |
commit | a9b6f722f62d0a302b980a4fdcdf9c9933955772 (patch) | |
tree | d353225c380d8183faddbf535a6116ada1a41279 /drivers/scsi/qla2xxx/qla_bsg.c | |
parent | 5f16b331d83757ad5154af07b449c722fef45d5e (diff) | |
download | linux-a9b6f722f62d0a302b980a4fdcdf9c9933955772.tar.xz |
[SCSI] qla2xxx: Implementation of bidirectional.
[jejb: merge fix for introduced warning]
Signed-off-by: Saurav Kashyap <saurav.kashyap@qlogic.com>
Signed-off-by: Chad Dupuis <chad.dupuis@qlogic.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_bsg.c')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_bsg.c | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c index 462ac707b8d6..dac3427ebf24 100644 --- a/drivers/scsi/qla2xxx/qla_bsg.c +++ b/drivers/scsi/qla2xxx/qla_bsg.c @@ -1650,6 +1650,186 @@ done: } static int +qla24xx_process_bidir_cmd(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + uint16_t thread_id; + uint32_t rval = EXT_STATUS_OK; + uint16_t req_sg_cnt = 0; + uint16_t rsp_sg_cnt = 0; + uint16_t nextlid = 0; + uint32_t tot_dsds; + srb_t *sp = NULL; + uint32_t req_data_len = 0; + uint32_t rsp_data_len = 0; + + /* Check the type of the adapter */ + if (!IS_BIDI_CAPABLE(ha)) { + ql_log(ql_log_warn, vha, 0x70a0, + "This adapter is not supported\n"); + rval = EXT_STATUS_NOT_SUPPORTED; + goto done; + } + + if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || + test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || + test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) { + rval = EXT_STATUS_BUSY; + goto done; + } + + /* Check if host is online */ + if (!vha->flags.online) { + ql_log(ql_log_warn, vha, 0x70a1, + "Host is not online\n"); + rval = EXT_STATUS_DEVICE_OFFLINE; + goto done; + } + + /* Check if cable is plugged in or not */ + if (vha->device_flags & DFLG_NO_CABLE) { + ql_log(ql_log_warn, vha, 0x70a2, + "Cable is unplugged...\n"); + rval = EXT_STATUS_INVALID_CFG; + goto done; + } + + /* Check if the switch is connected or not */ + if (ha->current_topology != ISP_CFG_F) { + ql_log(ql_log_warn, vha, 0x70a3, + "Host is not connected to the switch\n"); + rval = EXT_STATUS_INVALID_CFG; + goto done; + } + + /* Check if operating mode is P2P */ + if (ha->operating_mode != P2P) { + ql_log(ql_log_warn, vha, 0x70a4, + "Host is operating mode is not P2p\n"); + rval = EXT_STATUS_INVALID_CFG; + goto done; + } + + thread_id = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; + + mutex_lock(&ha->selflogin_lock); + if (vha->self_login_loop_id == 0) { + /* Initialize all required fields of fcport */ + vha->bidir_fcport.vha = vha; + vha->bidir_fcport.d_id.b.al_pa = vha->d_id.b.al_pa; + vha->bidir_fcport.d_id.b.area = vha->d_id.b.area; + vha->bidir_fcport.d_id.b.domain = vha->d_id.b.domain; + vha->bidir_fcport.loop_id = vha->loop_id; + + if (qla2x00_fabric_login(vha, &(vha->bidir_fcport), &nextlid)) { + ql_log(ql_log_warn, vha, 0x70a7, + "Failed to login port %06X for bidirectional IOCB\n", + vha->bidir_fcport.d_id.b24); + mutex_unlock(&ha->selflogin_lock); + rval = EXT_STATUS_MAILBOX; + goto done; + } + vha->self_login_loop_id = nextlid - 1; + + } + /* Assign the self login loop id to fcport */ + mutex_unlock(&ha->selflogin_lock); + + vha->bidir_fcport.loop_id = vha->self_login_loop_id; + + req_sg_cnt = dma_map_sg(&ha->pdev->dev, + bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, + DMA_TO_DEVICE); + + if (!req_sg_cnt) { + rval = EXT_STATUS_NO_MEMORY; + goto done; + } + + rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, + bsg_job->reply_payload.sg_list, bsg_job->reply_payload.sg_cnt, + DMA_FROM_DEVICE); + + if (!rsp_sg_cnt) { + rval = EXT_STATUS_NO_MEMORY; + goto done_unmap_req_sg; + } + + if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) || + (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) { + ql_dbg(ql_dbg_user, vha, 0x70a9, + "Dma mapping resulted in different sg counts " + "[request_sg_cnt: %x dma_request_sg_cnt: %x reply_sg_cnt: " + "%x dma_reply_sg_cnt: %x]\n", + bsg_job->request_payload.sg_cnt, req_sg_cnt, + bsg_job->reply_payload.sg_cnt, rsp_sg_cnt); + rval = EXT_STATUS_NO_MEMORY; + goto done_unmap_sg; + } + + if (req_data_len != rsp_data_len) { + rval = EXT_STATUS_BUSY; + ql_log(ql_log_warn, vha, 0x70aa, + "req_data_len != rsp_data_len\n"); + goto done_unmap_sg; + } + + req_data_len = bsg_job->request_payload.payload_len; + rsp_data_len = bsg_job->reply_payload.payload_len; + + + /* Alloc SRB structure */ + sp = qla2x00_get_sp(vha, &(vha->bidir_fcport), GFP_KERNEL); + if (!sp) { + ql_dbg(ql_dbg_user, vha, 0x70ac, + "Alloc SRB structure failed\n"); + rval = EXT_STATUS_NO_MEMORY; + goto done_unmap_sg; + } + + /*Populate srb->ctx with bidir ctx*/ + sp->u.bsg_job = bsg_job; + sp->free = qla2x00_bsg_sp_free; + sp->type = SRB_BIDI_CMD; + sp->done = qla2x00_bsg_job_done; + + /* Add the read and write sg count */ + tot_dsds = rsp_sg_cnt + req_sg_cnt; + + rval = qla2x00_start_bidir(sp, vha, tot_dsds); + if (rval != EXT_STATUS_OK) + goto done_free_srb; + /* the bsg request will be completed in the interrupt handler */ + return rval; + +done_free_srb: + mempool_free(sp, ha->srb_mempool); +done_unmap_sg: + dma_unmap_sg(&ha->pdev->dev, + bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); +done_unmap_req_sg: + dma_unmap_sg(&ha->pdev->dev, + bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); +done: + + /* Return an error vendor specific response + * and complete the bsg request + */ + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = rval; + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + bsg_job->reply->reply_payload_rcv_len = 0; + bsg_job->reply->result = (DID_OK) << 16; + bsg_job->job_done(bsg_job); + /* Always retrun success, vendor rsp carries correct status */ + return 0; +} + +static int qla2x00_process_vendor_specific(struct fc_bsg_job *bsg_job) { switch (bsg_job->request->rqst_data.h_vendor.vendor_cmd[0]) { @@ -1692,6 +1872,9 @@ qla2x00_process_vendor_specific(struct fc_bsg_job *bsg_job) case QL_VND_READ_I2C: return qla2x00_read_i2c(bsg_job); + case QL_VND_DIAG_IO_CMD: + return qla24xx_process_bidir_cmd(bsg_job); + default: bsg_job->reply->result = (DID_ERROR << 16); bsg_job->job_done(bsg_job); |