/* * QLogic Fibre Channel HBA Driver * Copyright (c) 2003-2008 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ #include "qla_def.h" #include #include #include /* BSG support for ELS/CT pass through */ inline srb_t * qla2x00_get_ctx_bsg_sp(scsi_qla_host_t *vha, fc_port_t *fcport, size_t size) { srb_t *sp; struct qla_hw_data *ha = vha->hw; struct srb_ctx *ctx; sp = mempool_alloc(ha->srb_mempool, GFP_KERNEL); if (!sp) goto done; ctx = kzalloc(size, GFP_KERNEL); if (!ctx) { mempool_free(sp, ha->srb_mempool); sp = NULL; goto done; } memset(sp, 0, sizeof(*sp)); sp->fcport = fcport; sp->ctx = ctx; done: return sp; } int qla24xx_fcp_prio_cfg_valid(struct qla_fcp_prio_cfg *pri_cfg, uint8_t flag) { int i, ret, num_valid; uint8_t *bcode; struct qla_fcp_prio_entry *pri_entry; uint32_t *bcode_val_ptr, bcode_val; ret = 1; num_valid = 0; bcode = (uint8_t *)pri_cfg; bcode_val_ptr = (uint32_t *)pri_cfg; bcode_val = (uint32_t)(*bcode_val_ptr); if (bcode_val == 0xFFFFFFFF) { /* No FCP Priority config data in flash */ DEBUG2(printk(KERN_INFO "%s: No FCP priority config data.\n", __func__)); return 0; } if (bcode[0] != 'H' || bcode[1] != 'Q' || bcode[2] != 'O' || bcode[3] != 'S') { /* Invalid FCP priority data header*/ DEBUG2(printk(KERN_ERR "%s: Invalid FCP Priority data header. bcode=0x%x\n", __func__, bcode_val)); return 0; } if (flag != 1) return ret; pri_entry = &pri_cfg->entry[0]; for (i = 0; i < pri_cfg->num_entries; i++) { if (pri_entry->flags & FCP_PRIO_ENTRY_TAG_VALID) num_valid++; pri_entry++; } if (num_valid == 0) { /* No valid FCP priority data entries */ DEBUG2(printk(KERN_ERR "%s: No valid FCP Priority data entries.\n", __func__)); ret = 0; } else { /* FCP priority data is valid */ DEBUG2(printk(KERN_INFO "%s: Valid FCP priority data. num entries = %d\n", __func__, num_valid)); } return ret; } static int qla24xx_proc_fcp_prio_cfg_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; int ret = 0; uint32_t len; uint32_t oper; bsg_job->reply->reply_payload_rcv_len = 0; if (!IS_QLA24XX_TYPE(ha) || !IS_QLA25XX(ha)) { ret = -EINVAL; goto exit_fcp_prio_cfg; } 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)) { ret = -EBUSY; goto exit_fcp_prio_cfg; } /* Get the sub command */ oper = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; /* Only set config is allowed if config memory is not allocated */ if (!ha->fcp_prio_cfg && (oper != QLFC_FCP_PRIO_SET_CONFIG)) { ret = -EINVAL; goto exit_fcp_prio_cfg; } switch (oper) { case QLFC_FCP_PRIO_DISABLE: if (ha->flags.fcp_prio_enabled) { ha->flags.fcp_prio_enabled = 0; ha->fcp_prio_cfg->attributes &= ~FCP_PRIO_ATTR_ENABLE; qla24xx_update_all_fcp_prio(vha); bsg_job->reply->result = DID_OK; } else { ret = -EINVAL; bsg_job->reply->result = (DID_ERROR << 16); goto exit_fcp_prio_cfg; } break; case QLFC_FCP_PRIO_ENABLE: if (!ha->flags.fcp_prio_enabled) { if (ha->fcp_prio_cfg) { ha->flags.fcp_prio_enabled = 1; ha->fcp_prio_cfg->attributes |= FCP_PRIO_ATTR_ENABLE; qla24xx_update_all_fcp_prio(vha); bsg_job->reply->result = DID_OK; } else { ret = -EINVAL; bsg_job->reply->result = (DID_ERROR << 16); goto exit_fcp_prio_cfg; } } break; case QLFC_FCP_PRIO_GET_CONFIG: len = bsg_job->reply_payload.payload_len; if (!len || len > FCP_PRIO_CFG_SIZE) { ret = -EINVAL; bsg_job->reply->result = (DID_ERROR << 16); goto exit_fcp_prio_cfg; } bsg_job->reply->result = DID_OK; bsg_job->reply->reply_payload_rcv_len = sg_copy_from_buffer( bsg_job->reply_payload.sg_list, bsg_job->reply_payload.sg_cnt, ha->fcp_prio_cfg, len); break; case QLFC_FCP_PRIO_SET_CONFIG: len = bsg_job->request_payload.payload_len; if (!len || len > FCP_PRIO_CFG_SIZE) { bsg_job->reply->result = (DID_ERROR << 16); ret = -EINVAL; goto exit_fcp_prio_cfg; } if (!ha->fcp_prio_cfg) { ha->fcp_prio_cfg = vmalloc(FCP_PRIO_CFG_SIZE); if (!ha->fcp_prio_cfg) { qla_printk(KERN_WARNING, ha, "Unable to allocate memory " "for fcp prio config data (%x).\n", FCP_PRIO_CFG_SIZE); bsg_job->reply->result = (DID_ERROR << 16); ret = -ENOMEM; goto exit_fcp_prio_cfg; } } memset(ha->fcp_prio_cfg, 0, FCP_PRIO_CFG_SIZE); sg_copy_to_buffer(bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, ha->fcp_prio_cfg, FCP_PRIO_CFG_SIZE); /* validate fcp priority data */ if (!qla24xx_fcp_prio_cfg_valid( (struct qla_fcp_prio_cfg *) ha->fcp_prio_cfg, 1)) { bsg_job->reply->result = (DID_ERROR << 16); ret = -EINVAL; /* If buffer was invalidatic int * fcp_prio_cfg is of no use */ vfree(ha->fcp_prio_cfg); ha->fcp_prio_cfg = NULL; goto exit_fcp_prio_cfg; } ha->flags.fcp_prio_enabled = 0; if (ha->fcp_prio_cfg->attributes & FCP_PRIO_ATTR_ENABLE) ha->flags.fcp_prio_enabled = 1; qla24xx_update_all_fcp_prio(vha); bsg_job->reply->result = DID_OK; break; default: ret = -EINVAL; break; } exit_fcp_prio_cfg: bsg_job->job_done(bsg_job); return ret; } static int qla2x00_process_els(struct fc_bsg_job *bsg_job) { struct fc_rport *rport; fc_port_t *fcport; struct Scsi_Host *host; scsi_qla_host_t *vha; struct qla_hw_data *ha; srb_t *sp; const char *type; int req_sg_cnt, rsp_sg_cnt; int rval = (DRIVER_ERROR << 16); uint16_t nextlid = 0; struct srb_ctx *els; /* Multiple SG's are not supported for ELS requests */ if (bsg_job->request_payload.sg_cnt > 1 || bsg_job->reply_payload.sg_cnt > 1) { DEBUG2(printk(KERN_INFO "multiple SG's are not supported for ELS requests" " [request_sg_cnt: %x reply_sg_cnt: %x]\n", bsg_job->request_payload.sg_cnt, bsg_job->reply_payload.sg_cnt)); rval = -EPERM; goto done; } /* ELS request for rport */ if (bsg_job->request->msgcode == FC_BSG_RPT_ELS) { rport = bsg_job->rport; fcport = *(fc_port_t **) rport->dd_data; host = rport_to_shost(rport); vha = shost_priv(host); ha = vha->hw; type = "FC_BSG_RPT_ELS"; /* make sure the rport is logged in, * if not perform fabric login */ if (qla2x00_fabric_login(vha, fcport, &nextlid)) { DEBUG2(qla_printk(KERN_WARNING, ha, "failed to login port %06X for ELS passthru\n", fcport->d_id.b24)); rval = -EIO; goto done; } } else { host = bsg_job->shost; vha = shost_priv(host); ha = vha->hw; type = "FC_BSG_HST_ELS_NOLOGIN"; /* Allocate a dummy fcport structure, since functions * preparing the IOCB and mailbox command retrieves port * specific information from fcport structure. For Host based * ELS commands there will be no fcport structure allocated */ fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); if (!fcport) { rval = -ENOMEM; goto done; } /* Initialize all required fields of fcport */ fcport->vha = vha; fcport->vp_idx = vha->vp_idx; fcport->d_id.b.al_pa = bsg_job->request->rqst_data.h_els.port_id[0]; fcport->d_id.b.area = bsg_job->request->rqst_data.h_els.port_id[1]; fcport->d_id.b.domain = bsg_job->request->rqst_data.h_els.port_id[2]; fcport->loop_id = (fcport->d_id.b.al_pa == 0xFD) ? NPH_FABRIC_CONTROLLER : NPH_F_PORT; } if (!vha->flags.online) { DEBUG2(qla_printk(KERN_WARNING, ha, "host not online\n")); rval = -EIO; goto done; } 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 = -ENOMEM; goto done_free_fcport; } 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 = -ENOMEM; goto done_free_fcport; } if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) || (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) { DEBUG2(printk(KERN_INFO "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 = -EAGAIN; goto done_unmap_sg; } /* Alloc SRB structure */ sp = qla2x00_get_ctx_bsg_sp(vha, fcport, sizeof(struct srb_ctx)); if (!sp) { rval = -ENOMEM; goto done_unmap_sg; } els = sp->ctx; els->type = (bsg_job->request->msgcode == FC_BSG_RPT_ELS ? SRB_ELS_CMD_RPT : SRB_ELS_CMD_HST); els->name = (bsg_job->request->msgcode == FC_BSG_RPT_ELS ? "bsg_els_rpt" : "bsg_els_hst"); els->u.bsg_job = bsg_job; DEBUG2(qla_printk(KERN_INFO, ha, "scsi(%ld:%x): bsg rqst type: %s els type: %x - loop-id=%x " "portid=%02x%02x%02x.\n", vha->host_no, sp->handle, type, bsg_job->request->rqst_data.h_els.command_code, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa)); rval = qla2x00_start_sp(sp); if (rval != QLA_SUCCESS) { kfree(sp->ctx); mempool_free(sp, ha->srb_mempool); rval = -EIO; goto done_unmap_sg; } return rval; done_unmap_sg: dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); goto done_free_fcport; done_free_fcport: if (bsg_job->request->msgcode == FC_BSG_HST_ELS_NOLOGIN) kfree(fcport); done: return rval; } static int qla2x00_process_ct(struct fc_bsg_job *bsg_job) { srb_t *sp; struct Scsi_Host *host = bsg_job->shost; scsi_qla_host_t *vha = shost_priv(host); struct qla_hw_data *ha = vha->hw; int rval = (DRIVER_ERROR << 16); int req_sg_cnt, rsp_sg_cnt; uint16_t loop_id; struct fc_port *fcport; char *type = "FC_BSG_HST_CT"; struct srb_ctx *ct; /* pass through is supported only for ISP 4Gb or higher */ if (!IS_FWI2_CAPABLE(ha)) { DEBUG2(qla_printk(KERN_INFO, ha, "scsi(%ld):Firmware is not capable to support FC " "CT pass thru\n", vha->host_no)); rval = -EPERM; goto done; } 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 = -ENOMEM; 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 = -ENOMEM; goto done; } if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) || (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) { DEBUG2(qla_printk(KERN_WARNING, ha, "[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 = -EAGAIN; goto done_unmap_sg; } if (!vha->flags.online) { DEBUG2(qla_printk(KERN_WARNING, ha, "host not online\n")); rval = -EIO; goto done_unmap_sg; } loop_id = (bsg_job->request->rqst_data.h_ct.preamble_word1 & 0xFF000000) >> 24; switch (loop_id) { case 0xFC: loop_id = cpu_to_le16(NPH_SNS); break; case 0xFA: loop_id = vha->mgmt_svr_loop_id; break; default: DEBUG2(qla_printk(KERN_INFO, ha, "Unknown loop id: %x\n", loop_id)); rval = -EINVAL; goto done_unmap_sg; } /* Allocate a dummy fcport structure, since functions preparing the * IOCB and mailbox command retrieves port specific information * from fcport structure. For Host based ELS commands there will be * no fcport structure allocated */ fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); if (!fcport) { rval = -ENOMEM; goto done_unmap_sg; } /* Initialize all required fields of fcport */ fcport->vha = vha; fcport->vp_idx = vha->vp_idx; fcport->d_id.b.al_pa = bsg_job->request->rqst_data.h_ct.port_id[0]; fcport->d_id.b.area = bsg_job->request->rqst_data.h_ct.port_id[1]; fcport->d_id.b.domain = bsg_job->request->rqst_data.h_ct.port_id[2]; fcport->loop_id = loop_id; /* Alloc SRB structure */ sp = qla2x00_get_ctx_bsg_sp(vha, fcport, sizeof(struct srb_ctx)); if (!sp) { rval = -ENOMEM; goto done_free_fcport; } ct = sp->ctx; ct->type = SRB_CT_CMD; ct->name = "bsg_ct"; ct->u.bsg_job = bsg_job; DEBUG2(qla_printk(KERN_INFO, ha, "scsi(%ld:%x): bsg rqst type: %s els type: %x - loop-id=%x " "portid=%02x%02x%02x.\n", vha->host_no, sp->handle, type, (bsg_job->request->rqst_data.h_ct.preamble_word2 >> 16), fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa)); rval = qla2x00_start_sp(sp); if (rval != QLA_SUCCESS) { kfree(sp->ctx); mempool_free(sp, ha->srb_mempool); rval = -EIO; goto done_free_fcport; } return rval; done_free_fcport: kfree(fcport); done_unmap_sg: dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); done: return rval; } /* Set the port configuration to enable the * internal loopback on ISP81XX */ static inline int qla81xx_set_internal_loopback(scsi_qla_host_t *vha, uint16_t *config, uint16_t *new_config) { int ret = 0; int rval = 0; struct qla_hw_data *ha = vha->hw; if (!IS_QLA81XX(ha)) goto done_set_internal; new_config[0] = config[0] | (ENABLE_INTERNAL_LOOPBACK << 1); memcpy(&new_config[1], &config[1], sizeof(uint16_t) * 3) ; ha->notify_dcbx_comp = 1; ret = qla81xx_set_port_config(vha, new_config); if (ret != QLA_SUCCESS) { DEBUG2(printk(KERN_ERR "%s(%lu): Set port config failed\n", __func__, vha->host_no)); ha->notify_dcbx_comp = 0; rval = -EINVAL; goto done_set_internal; } /* Wait for DCBX complete event */ if (!wait_for_completion_timeout(&ha->dcbx_comp, (20 * HZ))) { DEBUG2(qla_printk(KERN_WARNING, ha, "State change notificaition not received.\n")); } else DEBUG2(qla_printk(KERN_INFO, ha, "State change RECEIVED\n")); ha->notify_dcbx_comp = 0; done_set_internal: return rval; } /* Set the port configuration to disable the * internal loopback on ISP81XX */ static inline int qla81xx_reset_internal_loopback(scsi_qla_host_t *vha, uint16_t *config, int wait) { int ret = 0; int rval = 0; uint16_t new_config[4]; struct qla_hw_data *ha = vha->hw; if (!IS_QLA81XX(ha)) goto done_reset_internal; memset(new_config, 0 , sizeof(new_config)); if ((config[0] & INTERNAL_LOOPBACK_MASK) >> 1 == ENABLE_INTERNAL_LOOPBACK) { new_config[0] = config[0] & ~INTERNAL_LOOPBACK_MASK; memcpy(&new_config[1], &config[1], sizeof(uint16_t) * 3) ; ha->notify_dcbx_comp = wait; ret = qla81xx_set_port_config(vha, new_config); if (ret != QLA_SUCCESS) { DEBUG2(printk(KERN_ERR "%s(%lu): Set port config failed\n", __func__, vha->host_no)); ha->notify_dcbx_comp = 0; rval = -EINVAL; goto done_reset_internal; } /* Wait for DCBX complete event */ if (wait && !wait_for_completion_timeout(&ha->dcbx_comp, (20 * HZ))) { DEBUG2(qla_printk(KERN_WARNING, ha, "State change notificaition not received.\n")); ha->notify_dcbx_comp = 0; rval = -EINVAL; goto done_reset_internal; } else DEBUG2(qla_printk(KERN_INFO, ha, "State change RECEIVED\n")); ha->notify_dcbx_comp = 0; } done_reset_internal: return rval; } static int qla2x00_process_loopback(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; int rval; uint8_t command_sent; char *type; struct msg_echo_lb elreq; uint16_t response[MAILBOX_REGISTER_COUNT]; uint16_t config[4], new_config[4]; uint8_t *fw_sts_ptr; uint8_t *req_data = NULL; dma_addr_t req_data_dma; uint32_t req_data_len; uint8_t *rsp_data = NULL; dma_addr_t rsp_data_dma; uint32_t rsp_data_len; 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)) return -EBUSY; if (!vha->flags.online) { DEBUG2(qla_printk(KERN_WARNING, ha, "host not online\n")); return -EIO; } elreq.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 (!elreq.req_sg_cnt) return -ENOMEM; elreq.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 (!elreq.rsp_sg_cnt) { rval = -ENOMEM; goto done_unmap_req_sg; } if ((elreq.req_sg_cnt != bsg_job->request_payload.sg_cnt) || (elreq.rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) { DEBUG2(printk(KERN_INFO "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, elreq.req_sg_cnt, bsg_job->reply_payload.sg_cnt, elreq.rsp_sg_cnt)); rval = -EAGAIN; goto done_unmap_sg; } req_data_len = rsp_data_len = bsg_job->request_payload.payload_len; req_data = dma_alloc_coherent(&ha->pdev->dev, req_data_len, &req_data_dma, GFP_KERNEL); if (!req_data) { DEBUG2(printk(KERN_ERR "%s: dma alloc for req_data " "failed for host=%lu\n", __func__, vha->host_no)); rval = -ENOMEM; goto done_unmap_sg; } rsp_data = dma_alloc_coherent(&ha->pdev->dev, rsp_data_len, &rsp_data_dma, GFP_KERNEL); if (!rsp_data) { DEBUG2(printk(KERN_ERR "%s: dma alloc for rsp_data " "failed for host=%lu\n", __func__, vha->host_no)); rval = -ENOMEM; goto done_free_dma_req; } /* Copy the request buffer in req_data now */ sg_copy_to_buffer(bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, req_data, req_data_len); elreq.send_dma = req_data_dma; elreq.rcv_dma = rsp_data_dma; elreq.transfer_size = req_data_len; elreq.options = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; if ((ha->current_topology == ISP_CFG_F || (IS_QLA81XX(ha) && le32_to_cpu(*(uint32_t *)req_data) == ELS_OPCODE_BYTE && req_data_len == MAX_ELS_FRAME_PAYLOAD)) && elreq.options == EXTERNAL_LOOPBACK) { type = "FC_BSG_HST_VENDOR_ECHO_DIAG"; DEBUG2(qla_printk(KERN_INFO, ha, "scsi(%ld) bsg rqst type: %s\n", vha->host_no, type)); command_sent = INT_DEF_LB_ECHO_CMD; rval = qla2x00_echo_test(vha, &elreq, response); } else { if (IS_QLA81XX(ha)) { memset(config, 0, sizeof(config)); memset(new_config, 0, sizeof(new_config)); if (qla81xx_get_port_config(vha, config)) { DEBUG2(printk(KERN_ERR "%s(%lu): Get port config failed\n", __func__, vha->host_no)); bsg_job->reply->reply_payload_rcv_len = 0; bsg_job->reply->result = (DID_ERROR << 16); rval = -EPERM; goto done_free_dma_req; } if (elreq.options != EXTERNAL_LOOPBACK) { DEBUG2(qla_printk(KERN_INFO, ha, "Internal: current port config = %x\n", config[0])); if (qla81xx_set_internal_loopback(vha, config, new_config)) { bsg_job->reply->reply_payload_rcv_len = 0; bsg_job->reply->result = (DID_ERROR << 16); rval = -EPERM; goto done_free_dma_req; } } else { /* For external loopback to work * ensure internal loopback is disabled */ if (qla81xx_reset_internal_loopback(vha, config, 1)) { bsg_job->reply->reply_payload_rcv_len = 0; bsg_job->reply->result = (DID_ERROR << 16); rval = -EPERM; goto done_free_dma_req; } } type = "FC_BSG_HST_VENDOR_LOOPBACK"; DEBUG2(qla_printk(KERN_INFO, ha, "scsi(%ld) bsg rqst type: %s\n", vha->host_no, type)); command_sent = INT_DEF_LB_LOOPBACK_CMD; rval = qla2x00_loopback_test(vha, &elreq, response); if (new_config[1]) { /* Revert back to original port config * Also clear internal loopback */ qla81xx_reset_internal_loopback(vha, new_config, 0); } if (response[0] == MBS_COMMAND_ERROR && response[1] == MBS_LB_RESET) { DEBUG2(printk(KERN_ERR "%s(%ld): ABORTing " "ISP\n", __func__, vha->host_no)); set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); qla2xxx_wake_dpc(vha); qla2x00_wait_for_chip_reset(vha); /* Also reset the MPI */ if (qla81xx_restart_mpi_firmware(vha) != QLA_SUCCESS) { qla_printk(KERN_INFO, ha, "MPI reset failed for host%ld.\n", vha->host_no); } bsg_job->reply->reply_payload_rcv_len = 0; bsg_job->reply->result = (DID_ERROR << 16); rval = -EIO; goto done_free_dma_req; } } else { type = "FC_BSG_HST_VENDOR_LOOPBACK"; DEBUG2(qla_printk(KERN_INFO, ha, "scsi(%ld) bsg rqst type: %s\n", vha->host_no, type)); command_sent = INT_DEF_LB_LOOPBACK_CMD; rval = qla2x00_loopback_test(vha, &elreq, response); } } if (rval) { DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " "request %s failed\n", vha->host_no, type)); fw_sts_ptr = ((uint8_t *)bsg_job->req->sense) + sizeof(struct fc_bsg_reply); memcpy(fw_sts_ptr, response, sizeof(response)); fw_sts_ptr += sizeof(response); *fw_sts_ptr = command_sent; rval = 0; bsg_job->reply->reply_payload_rcv_len = 0; bsg_job->reply->result = (DID_ERROR << 16); } else { DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " "request %s completed\n", vha->host_no, type)); bsg_job->reply_len = sizeof(struct fc_bsg_reply) + sizeof(response) + sizeof(uint8_t); bsg_job->reply->reply_payload_rcv_len = bsg_job->reply_payload.payload_len; fw_sts_ptr = ((uint8_t *)bsg_job->req->sense) + sizeof(struct fc_bsg_reply); memcpy(fw_sts_ptr, response, sizeof(response)); fw_sts_ptr += sizeof(response); *fw_sts_ptr = command_sent; bsg_job->reply->result = DID_OK; sg_copy_from_buffer(bsg_job->reply_payload.sg_list, bsg_job->reply_payload.sg_cnt, rsp_data, rsp_data_len); } bsg_job->job_done(bsg_job); dma_free_coherent(&ha->pdev->dev, rsp_data_len, rsp_data, rsp_data_dma); done_free_dma_req: dma_free_coherent(&ha->pdev->dev, req_data_len, req_data, req_data_dma); 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); return rval; } static int qla84xx_reset(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; int rval = 0; uint32_t flag; 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)) return -EBUSY; if (!IS_QLA84XX(ha)) { DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld): Not 84xx, " "exiting.\n", vha->host_no)); return -EINVAL; } flag = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; rval = qla84xx_reset_chip(vha, flag == A84_ISSUE_RESET_DIAG_FW); if (rval) { DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " "request 84xx reset failed\n", vha->host_no)); rval = bsg_job->reply->reply_payload_rcv_len = 0; bsg_job->reply->result = (DID_ERROR << 16); } else { DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " "request 84xx reset completed\n", vha->host_no)); bsg_job->reply->result = DID_OK; } bsg_job->job_done(bsg_job); return rval; } static int qla84xx_updatefw(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; struct verify_chip_entry_84xx *mn = NULL; dma_addr_t mn_dma, fw_dma; void *fw_buf = NULL; int rval = 0; uint32_t sg_cnt; uint32_t data_len; uint16_t options; uint32_t flag; uint32_t fw_ver; 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)) return -EBUSY; if (!IS_QLA84XX(ha)) { DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld): Not 84xx, " "exiting.\n", vha->host_no)); return -EINVAL; } sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); if (!sg_cnt) return -ENOMEM; if (sg_cnt != bsg_job->request_payload.sg_cnt) { DEBUG2(printk(KERN_INFO "dma mapping resulted in different sg counts " "request_sg_cnt: %x dma_request_sg_cnt: %x ", bsg_job->request_payload.sg_cnt, sg_cnt)); rval = -EAGAIN; goto done_unmap_sg; } data_len = bsg_job->request_payload.payload_len; fw_buf = dma_alloc_coherent(&ha->pdev->dev, data_len, &fw_dma, GFP_KERNEL); if (!fw_buf) { DEBUG2(printk(KERN_ERR "%s: dma alloc for fw_buf " "failed for host=%lu\n", __func__, vha->host_no)); rval = -ENOMEM; goto done_unmap_sg; } sg_copy_to_buffer(bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, fw_buf, data_len); mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma); if (!mn) { DEBUG2(printk(KERN_ERR "%s: dma alloc for fw buffer " "failed for host=%lu\n", __func__, vha->host_no)); rval = -ENOMEM; goto done_free_fw_buf; } flag = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; fw_ver = le32_to_cpu(*((uint32_t *)((uint32_t *)fw_buf + 2))); memset(mn, 0, sizeof(struct access_chip_84xx)); mn->entry_type = VERIFY_CHIP_IOCB_TYPE; mn->entry_count = 1; options = VCO_FORCE_UPDATE | VCO_END_OF_DATA; if (flag == A84_ISSUE_UPDATE_DIAGFW_CMD) options |= VCO_DIAG_FW; mn->options = cpu_to_le16(options); mn->fw_ver = cpu_to_le32(fw_ver); mn->fw_size = cpu_to_le32(data_len); mn->fw_seq_size = cpu_to_le32(data_len); mn->dseg_address[0] = cpu_to_le32(LSD(fw_dma)); mn->dseg_address[1] = cpu_to_le32(MSD(fw_dma)); mn->dseg_length = cpu_to_le32(data_len); mn->data_seg_cnt = cpu_to_le16(1); rval = qla2x00_issue_iocb_timeout(vha, mn, mn_dma, 0, 120); if (rval) { DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " "request 84xx updatefw failed\n", vha->host_no)); rval = bsg_job->reply->reply_payload_rcv_len = 0; bsg_job->reply->result = (DID_ERROR << 16); } else { DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " "request 84xx updatefw completed\n", vha->host_no)); bsg_job->reply_len = sizeof(struct fc_bsg_reply); bsg_job->reply->result = DID_OK; } bsg_job->job_done(bsg_job); dma_pool_free(ha->s_dma_pool, mn, mn_dma); done_free_fw_buf: dma_free_coherent(&ha->pdev->dev, data_len, fw_buf, fw_dma); done_unmap_sg: dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); return rval; } static int qla84xx_mgmt_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; struct access_chip_84xx *mn = NULL; dma_addr_t mn_dma, mgmt_dma; void *mgmt_b = NULL; int rval = 0; struct qla_bsg_a84_mgmt *ql84_mgmt; uint32_t sg_cnt; uint32_t data_len = 0; uint32_t dma_direction = DMA_NONE; 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)) return -EBUSY; if (!IS_QLA84XX(ha)) { DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld): Not 84xx, " "exiting.\n", vha->host_no)); return -EINVAL; } ql84_mgmt = (struct qla_bsg_a84_mgmt *)((char *)bsg_job->request + sizeof(struct fc_bsg_request)); if (!ql84_mgmt) { DEBUG2(printk("%s(%ld): mgmt header not provided, exiting.\n", __func__, vha->host_no)); return -EINVAL; } mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma); if (!mn) { DEBUG2(printk(KERN_ERR "%s: dma alloc for fw buffer " "failed for host=%lu\n", __func__, vha->host_no)); return -ENOMEM; } memset(mn, 0, sizeof(struct access_chip_84xx)); mn->entry_type = ACCESS_CHIP_IOCB_TYPE; mn->entry_count = 1; switch (ql84_mgmt->mgmt.cmd) { case QLA84_MGMT_READ_MEM: case QLA84_MGMT_GET_INFO: sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); if (!sg_cnt) { rval = -ENOMEM; goto exit_mgmt; } dma_direction = DMA_FROM_DEVICE; if (sg_cnt != bsg_job->reply_payload.sg_cnt) { DEBUG2(printk(KERN_INFO "dma mapping resulted in different sg counts " "reply_sg_cnt: %x dma_reply_sg_cnt: %x\n", bsg_job->reply_payload.sg_cnt, sg_cnt)); rval = -EAGAIN; goto done_unmap_sg; } data_len = bsg_job->reply_payload.payload_len; mgmt_b = dma_alloc_coherent(&ha->pdev->dev, data_len, &mgmt_dma, GFP_KERNEL); if (!mgmt_b) { DEBUG2(printk(KERN_ERR "%s: dma alloc for mgmt_b " "failed for host=%lu\n", __func__, vha->host_no)); rval = -ENOMEM; goto done_unmap_sg; } if (ql84_mgmt->mgmt.cmd == QLA84_MGMT_READ_MEM) { mn->options = cpu_to_le16(ACO_DUMP_MEMORY); mn->parameter1 = cpu_to_le32( ql84_mgmt->mgmt.mgmtp.u.mem.start_addr); } else if (ql84_mgmt->mgmt.cmd == QLA84_MGMT_GET_INFO) { mn->options = cpu_to_le16(ACO_REQUEST_INFO); mn->parameter1 = cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.info.type); mn->parameter2 = cpu_to_le32( ql84_mgmt->mgmt.mgmtp.u.info.context); } break; case QLA84_MGMT_WRITE_MEM: sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); if (!sg_cnt) { rval = -ENOMEM; goto exit_mgmt; } dma_direction = DMA_TO_DEVICE; if (sg_cnt != bsg_job->request_payload.sg_cnt) { DEBUG2(printk(KERN_INFO "dma mapping resulted in different sg counts " "request_sg_cnt: %x dma_request_sg_cnt: %x ", bsg_job->request_payload.sg_cnt, sg_cnt)); rval = -EAGAIN; goto done_unmap_sg; } data_len = bsg_job->request_payload.payload_len; mgmt_b = dma_alloc_coherent(&ha->pdev->dev, data_len, &mgmt_dma, GFP_KERNEL); if (!mgmt_b) { DEBUG2(printk(KERN_ERR "%s: dma alloc for mgmt_b " "failed for host=%lu\n", __func__, vha->host_no)); rval = -ENOMEM; goto done_unmap_sg; } sg_copy_to_buffer(bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, mgmt_b, data_len); mn->options = cpu_to_le16(ACO_LOAD_MEMORY); mn->parameter1 = cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.mem.start_addr); break; case QLA84_MGMT_CHNG_CONFIG: mn->options = cpu_to_le16(ACO_CHANGE_CONFIG_PARAM); mn->parameter1 = cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.config.id); mn->parameter2 = cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.config.param0); mn->parameter3 = cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.config.param1); break; default: rval = -EIO; goto exit_mgmt; } if (ql84_mgmt->mgmt.cmd != QLA84_MGMT_CHNG_CONFIG) { mn->total_byte_cnt = cpu_to_le32(ql84_mgmt->mgmt.len); mn->dseg_count = cpu_to_le16(1); mn->dseg_address[0] = cpu_to_le32(LSD(mgmt_dma)); mn->dseg_address[1] = cpu_to_le32(MSD(mgmt_dma)); mn->dseg_length = cpu_to_le32(ql84_mgmt->mgmt.len); } rval = qla2x00_issue_iocb(vha, mn, mn_dma, 0); if (rval) { DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " "request 84xx mgmt failed\n", vha->host_no)); rval = bsg_job->reply->reply_payload_rcv_len = 0; bsg_job->reply->result = (DID_ERROR << 16); } else { DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " "request 84xx mgmt completed\n", vha->host_no)); bsg_job->reply_len = sizeof(struct fc_bsg_reply); bsg_job->reply->result = DID_OK; if ((ql84_mgmt->mgmt.cmd == QLA84_MGMT_READ_MEM) || (ql84_mgmt->mgmt.cmd == QLA84_MGMT_GET_INFO)) { bsg_job->reply->reply_payload_rcv_len = bsg_job->reply_payload.payload_len; sg_copy_from_buffer(bsg_job->reply_payload.sg_list, bsg_job->reply_payload.sg_cnt, mgmt_b, data_len); } } bsg_job->job_done(bsg_job); done_unmap_sg: if (mgmt_b) dma_free_coherent(&ha->pdev->dev, data_len, mgmt_b, mgmt_dma); if (dma_direction == DMA_TO_DEVICE) dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); else if (dma_direction == DMA_FROM_DEVICE) dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); exit_mgmt: dma_pool_free(ha->s_dma_pool, mn, mn_dma); return rval; } static int qla24xx_iidma(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; int rval = 0; struct qla_port_param *port_param = NULL; fc_port_t *fcport = NULL; uint16_t mb[MAILBOX_REGISTER_COUNT]; uint8_t *rsp_ptr = NULL; bsg_job->reply->reply_payload_rcv_len = 0; 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)) return -EBUSY; if (!IS_IIDMA_CAPABLE(vha->hw)) { DEBUG2(qla_printk(KERN_WARNING, ha, "%s(%lu): iiDMA not " "supported\n", __func__, vha->host_no)); return -EINVAL; } port_param = (struct qla_port_param *)((char *)bsg_job->request + sizeof(struct fc_bsg_request)); if (!port_param) { DEBUG2(printk("%s(%ld): port_param header not provided, " "exiting.\n", __func__, vha->host_no)); return -EINVAL; } if (port_param->fc_scsi_addr.dest_type != EXT_DEF_TYPE_WWPN) { DEBUG2(printk(KERN_ERR "%s(%ld): Invalid destination type\n", __func__, vha->host_no)); return -EINVAL; } list_for_each_entry(fcport, &vha->vp_fcports, list) { if (fcport->port_type != FCT_TARGET) continue; if (memcmp(port_param->fc_scsi_addr.dest_addr.wwpn, fcport->port_name, sizeof(fcport->port_name))) continue; break; } if (!fcport) { DEBUG2(printk(KERN_ERR "%s(%ld): Failed to find port\n", __func__, vha->host_no)); return -EINVAL; } if (fcport->loop_id == FC_NO_LOOP_ID) { DEBUG2(printk(KERN_ERR "%s(%ld): Invalid port loop id, " "loop_id = 0x%x\n", __func__, vha->host_no, fcport->loop_id)); return -EINVAL; } if (fcport->flags & FCF_LOGIN_NEEDED) { DEBUG2(printk(KERN_ERR "%s(%ld): Remote port not logged in, " "flags = 0x%x\n", __func__, vha->host_no, fcport->flags)); return -EINVAL; } if (port_param->mode) rval = qla2x00_set_idma_speed(vha, fcport->loop_id, port_param->speed, mb); else rval = qla2x00_get_idma_speed(vha, fcport->loop_id, &port_param->speed, mb); if (rval) { DEBUG16(printk(KERN_ERR "scsi(%ld): iIDMA cmd failed for " "%02x%02x%02x%02x%02x%02x%02x%02x -- " "%04x %x %04x %04x.\n", vha->host_no, fcport->port_name[0], fcport->port_name[1], fcport->port_name[2], fcport->port_name[3], fcport->port_name[4], fcport->port_name[5], fcport->port_name[6], fcport->port_name[7], rval, fcport->fp_speed, mb[0], mb[1])); rval = 0; bsg_job->reply->result = (DID_ERROR << 16); } else { if (!port_param->mode) { bsg_job->reply_len = sizeof(struct fc_bsg_reply) + sizeof(struct qla_port_param); rsp_ptr = ((uint8_t *)bsg_job->reply) + sizeof(struct fc_bsg_reply); memcpy(rsp_ptr, port_param, sizeof(struct qla_port_param)); } bsg_job->reply->result = DID_OK; } bsg_job->job_done(bsg_job); return rval; } static int qla2x00_process_vendor_specific(struct fc_bsg_job *bsg_job) { switch (bsg_job->request->rqst_data.h_vendor.vendor_cmd[0]) { case QL_VND_LOOPBACK: return qla2x00_process_loopback(bsg_job); case QL_VND_A84_RESET: return qla84xx_reset(bsg_job); case QL_VND_A84_UPDATE_FW: return qla84xx_updatefw(bsg_job); case QL_VND_A84_MGMT_CMD: return qla84xx_mgmt_cmd(bsg_job); case QL_VND_IIDMA: return qla24xx_iidma(bsg_job); case QL_VND_FCP_PRIO_CFG_CMD: return qla24xx_proc_fcp_prio_cfg_cmd(bsg_job); default: bsg_job->reply->result = (DID_ERROR << 16); bsg_job->job_done(bsg_job); return -ENOSYS; } } int qla24xx_bsg_request(struct fc_bsg_job *bsg_job) { int ret = -EINVAL; switch (bsg_job->request->msgcode) { case FC_BSG_RPT_ELS: case FC_BSG_HST_ELS_NOLOGIN: ret = qla2x00_process_els(bsg_job); break; case FC_BSG_HST_CT: ret = qla2x00_process_ct(bsg_job); break; case FC_BSG_HST_VENDOR: ret = qla2x00_process_vendor_specific(bsg_job); break; case FC_BSG_HST_ADD_RPORT: case FC_BSG_HST_DEL_RPORT: case FC_BSG_RPT_CT: default: DEBUG2(printk("qla2xxx: unsupported BSG request\n")); break; } return ret; } int qla24xx_bsg_timeout(struct fc_bsg_job *bsg_job) { scsi_qla_host_t *vha = shost_priv(bsg_job->shost); struct qla_hw_data *ha = vha->hw; srb_t *sp; int cnt, que; unsigned long flags; struct req_que *req; struct srb_ctx *sp_bsg; /* find the bsg job from the active list of commands */ spin_lock_irqsave(&ha->hardware_lock, flags); for (que = 0; que < ha->max_req_queues; que++) { req = ha->req_q_map[que]; if (!req) continue; for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { sp = req->outstanding_cmds[cnt]; if (sp) { sp_bsg = sp->ctx; if (((sp_bsg->type == SRB_CT_CMD) || (sp_bsg->type == SRB_ELS_CMD_HST)) && (sp_bsg->u.bsg_job == bsg_job)) { if (ha->isp_ops->abort_command(sp)) { DEBUG2(qla_printk(KERN_INFO, ha, "scsi(%ld): mbx " "abort_command failed\n", vha->host_no)); bsg_job->req->errors = bsg_job->reply->result = -EIO; } else { DEBUG2(qla_printk(KERN_INFO, ha, "scsi(%ld): mbx " "abort_command success\n", vha->host_no)); bsg_job->req->errors = bsg_job->reply->result = 0; } goto done; } } } } spin_unlock_irqrestore(&ha->hardware_lock, flags); DEBUG2(qla_printk(KERN_INFO, ha, "scsi(%ld) SRB not found to abort\n", vha->host_no)); bsg_job->req->errors = bsg_job->reply->result = -ENXIO; return 0; done: spin_unlock_irqrestore(&ha->hardware_lock, flags); if (bsg_job->request->msgcode == FC_BSG_HST_CT) kfree(sp->fcport); kfree(sp->ctx); mempool_free(sp, ha->srb_mempool); return 0; }